# Pagination with Spring Data JPA

By Atta Ur Rehman Shah (https://attacomsian.com/about). Published Oct 12, 2019, updated Sep 25, 2022. Topics: Spring Boot, Spring Data JPA.
Canonical URL: https://attacomsian.com/blog/spring-data-jpa-pagination

> A comprehensive guide on learning how to use paginate query results using the Pageable interface in Spring Data JPA.

In the [last article](https://attacomsian.com/blog/spring-data-jpa-sorting), I wrote about how to sort the query results in Spring Data JPA using static and dynamic sorting techniques. 

In the modern web, the response time of your website is a critical factor for higher search engine ranking. The visitors expect pages to load quickly and only show the relevant information. For example, if you own an e-commerce website with tens of thousands of products, this means only displaying a small number of products at once, and not all of them.

To help you deal with such situations, **Spring Data JPA provides the concepts of pagination**. It makes it easy to deal with a large amount of data in the most efficient way. 

In this article, you will learn how to paginate the query results in Spring Data JPA. I shall show you how pagination works with [derived](https://attacomsian.com/blog/derived-query-methods-spring-data-jpa) and [custom](https://attacomsian.com/blog/spring-data-jpa-query-annotation) queries. 

## Create an Application

Let us create a simple Spring Boot application with the [Spring Data JPA and H2](https://attacomsian.com/blog/spring-data-jpa-h2-database) in-memory database. It only contains a single entity called `Person`:

<p class="file-desc"><span>Person.java</span></p>

```java
package com.attacomsian.jpa.domains;

import javax.persistence.*;

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;
    private String lastName;
    private int age;

    public Person() {
    }

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    // getters and setters, equals(), toString(), ... (omitted for brevity)
}
```

Here is what our `PersonRespository` looks like:

<p class="file-desc"><span>PersonRepository.java</span></p>

```java
package com.attacomsian.jpa.repositories;

import com.attacomsian.jpa.domains.Person;
import org.springframework.data.repository.PagingAndSortingRepository;

public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {

    // TODO: add paging methods
}
```

## Spring Data's `Pageable` Interface

Just like the special `Sort` parameter, we used for the [dynamic sorting](https://attacomsian.com/blog/spring-data-jpa-sorting#sorting-query-results-using-dynamic-sorting), Spring Data JPA supports another special parameter called `Pageable` for paginating the query results. These special parameters are automatically recognized by the Spring Data infrastructure to apply pagination and sorting to database queries dynamically.

The `Pageable` interface contains information about the requested page such as the size and the number of the page. It provides the following methods, among others, to add paging to statically define queries:

```java
public interface Pageable {

    // returns the current page number (zero-based)
    int getPageNumber();

     // returns the size of the page
    int getPageSize();

     // returns the sorting parameters
    Sort getSort();

   // ... other methods
}
```

Whenever you want to apply pagination to query results, all you need to do is just add `Pageable` to the query method definition as a parameter and set the return by `Page<T>`:

```java
Page<Person> findByLastName(String lastName, Pageable pageable);
```

While calling the above method, you need to create an object of `Pageable` and pass it to the invoked repository method. The simplest way to create an instance of `Pageable` is to use the `PageRequest` class, which provides the implementation of `Pageable` methods:

```java
Pageable pageable = PageRequest.of(0, 10);
```

This will create a page request for the first page (page index is zero-based) with 10 as the size of the page to be returned.

You can even apply [dynamic sorting](https://attacomsian.com/blog/spring-data-jpa-sorting#sorting-query-results-using-dynamic-sorting) by using the `Pageable` instance as shown below:

```java
// pageable instance with dynamic sorting
Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending());

// multiple sort parameters
Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending()
        .and(Sort.by("lastName").ascending()));
```

## Spring Data's `Slice` and `Page`

The example, we have defined above, returns a `Page` object. A `Page` contains information about the total number of elements and pages available in the database. This is because the Spring Data JPA triggers a count query to calculate the number of elements. 

Depending on the database you are using in your application, it might become expensive as the number of items increases. To avoid this costly count query, you should instead return a `Slice`.  Unlike a `Page`, a `Slice` only knows about whether the next slice is available or not. This information is sufficient to walk through a large result set.

Both `Slice` and `Page` are part of Spring Data JPA, where `Page` is just a sub-interface of `Slice` with additional methods. You should use `Slice` if you don't need the total number of items and pages. An example of such a scenario is when you only need the *Next Page* and *Previous Page* buttons. Here is an example query that returns a `Slice`:

```java
Slice<Person> findByAgeGreaterThan(int age, Pageable pageable);
```


## Paginating Query Results with `Pageable`

Now we know the `Pageable` interface and the differences between a `Page` and a `Slice`, let us create database queries that accept a `Pageable` object as a parameter to paginate their results.

The `PagingAndSortingRepository` interface, which we are extending above, provides us the `findAll(Pageable pageable)` method to paginate all `Person` entities currently available in the database. All you need to do is just create an instance of `Pageable` and pass it to this method to get a `Page`.

However, if you want to get a `Slice` or a `List`, you need to define your own queries in the repository interface.

### Derived Queries

Applying pagination to [derived queries](https://attacomsian.com/blog/spring-data-jpa-sorting#sort-query-results-using-static-sorting) is a straightforward task. You just need to pass the `Pageable` interface as a parameter to any derived query and set the desired return type as shown below:

```java
Page<Person> findByFirstName(String firstName, Pageable pageable);

Slice<Person> findByAgeBetween(int start, int end, Pageable pageable);

List<Person> findByLastNameIsNotNull(Pageable pageable);
```

### Custom Queries with `@Query` Annotation

To apply pagination to JPQL queries defined using the [@Query annotation](https://attacomsian.com/blog/spring-data-jpa-query-annotation), Spring Data JPA allows you to pass the `Pageable` interface as a parameter:

```java
@Query("SELECT p FROM Person p WHERE p.lastName = ?1")
Page<Person> findByLastNameJPQL(String lastName, Pageable pageable);

@Query("SELECT p FROM Person p WHERE p.age < :age")
Page<Person> findByAgeLessThanJPQL(@Param("age") int page, Pageable pageable);
```

To use pagination with native SQL queries declared using the `@Query` annotation, you need to **define the count query by yourself**, as shown in the following example:

```java
@Query(value = "SELECT * FROM Person p WHERE p.firstName = :firstName",
        countQuery = "SELECT count(*) Person p WHERE p.firstName = :firstName",
        nativeQuery = true)
Page<Person> findByFirstNameNativeSQL(@Param("firstName") String firstName, Pageable pageable);
```

### Named Queries

You can also paginate the query results of [named queries](https://attacomsian.com/blog/spring-data-jpa-named-queries) that use JPQL to define the query statements.

Let us first declare a JPQL named query by using the [@NamedQuery annotation](https://attacomsian.com/blog/spring-data-jpa-named-queries#namedquery-annotation):

```java
@NamedQuery(name = "Person.findByFirstNameNamed",
        query = "SELECT p FROM Person p WHERE p.firstName = ?1")
public class Person {
    // ...
}
```

Now you can reference the named query above in the `PersonRepository` interface and pass `Pageable` as a parameter:

```java
Page<Person> findByFirstNameNamed(String firstName, Pageable pageable);
```

Similar to native SQL custom queries, **native named queries also require a separate count query** to use the `Pageable` interface for paging the query results. 

Let us define a native SQL named query:

```java
@NamedNativeQuery(name = "Person.findByLastNameNativeNamed",
        query = "SELECT * FROM Person p WHERE p.firstName = :firstName")
public class Person {
    // ...
}
```

To reference the above native SQL named query in your repository interface, you must add a `@Query` annotation on the repository method and set the `nativeQuery` attribute to true along with the count query as shown below:

```java
@Query(countQuery = "SELECT count(*) Person p WHERE p.lastName = :lastName",
        nativeQuery = true)
Page<Person> findByLastNameNativeNamed(@Param("lastName") String lastName, Pageable pageable);
```

> **Note:** Dynamic sorting is not supported by named queries. So while calling the above repository methods, your `Pageable` instance should not contain a `Sort` object.

## Using the `Pageable` Interface

The following examples show how to create an instance of `Pageable` and then call different query methods that paginate their results:

```java
// get all persons by the last name
Pageable pageable = PageRequest.of(0, 3);
Page<Person> personPage = personRepository.findByLastName("Doe", pageable);

// get all persons sorted by their age in the descending order
Pageable pageable2 = PageRequest.of(0, 5, Sort.by("age").descending());
Page<Person> personPage2 = personRepository.findAll(pageable2);
```

If you want to **skip the pagination** for query methods that require an instance of `Pageable`, just use the `Pageable.unpaged()` method:

```java
Slice<Person> personSlice = personRepository.findByAgeBetween(20, 60, Pageable.unpaged());
```

> **Source Code:** Download the complete source code from [GitHub](https://github.com/attacomsian/code-examples/tree/master/spring-data-jpa/jpa-pagination) available under MIT license.

## Conclusion

In this article, you have learned how to apply pagination to query results for different queries in Spring Data JPA.

We also discussed the `Pageable` interface and the `Slice` and `Page` differences in detail. Read the [Sorting Query Results in Spring Data JPA](https://attacomsian.com/blog/spring-data-jpa-sorting) if you want to learn more about the sorting functionality in Spring Data JPA.

## Further Reading

To learn about Spring Data JPA, check out the following articles:

* [Getting Started with Spring Data JPA](https://attacomsian.com/blog/getting-started-spring-data-jpa)
* [Spring Data JPA with H2 DataBase and Spring Boot](https://attacomsian.com/blog/spring-data-jpa-h2-database)
* [Accessing Data with Spring Data JPA and MySQL](https://attacomsian.com/blog/accessing-data-spring-data-jpa-mysql)
* [Derived Query Methods in Spring Data JPA](https://attacomsian.com/blog/derived-query-methods-spring-data-jpa)
* [Spring Data JPA Custom Queries using @Query Annotation](https://attacomsian.com/blog/spring-data-jpa-query-annotation)
* [How to Use Spring Data JPA Named Queries](https://attacomsian.com/blog/spring-data-jpa-named-queries)
* [Sorting Query Results in Spring Data JPA](https://attacomsian.com/blog/spring-data-jpa-sorting)
