Spring Boot Pagination and Sorting Example

Learn to request and display only chunks of data from the database using pagination and sorting inputs and query parameters in spring boot and spring data applications. It is mostly required when we are displaying domain data in tabular format in UI.

Pagination consists of two fields – page size and page number. Sorting is done on single or multiple fields in the table.

1. JPA Entity

In this post, we are taking examples of EmployeeEntity class. Each entity instance represents an employee record in the database.

@Entity
public class EmployeeEntity {

    @Id @GeneratedValue
    private Long id;
    private String firstName;
    private String lastName;
    private String email;

    //Setters, getters and toString()
}

2. Using PagingAndSortingRepository

Since Spring Data 3.0, PagingAndSortingRepository does not extends the CrudRepository. So if we want to add the CRUD capabilities to the repository class, we must explicitly extend from CrudRepository or ListCrudRepository interfaces.

The PagingAndSortingRepository provides methods to retrieve entities using the pagination and sorting abstraction. It implicitly provides two methods:

  • Page findAll(Pageable pageable) – returns a Page of entities meeting the paging restriction provided in the Pageable object.
  • Iterable findAll(Sort sort) – returns all entities sorted by the given options. No paging is applied here.
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.howtodoinjava.demo.entity.EmployeeEntity;

@Repository
public interface EmployeeRepository
        extends PagingAndSortingRepository<EmployeeEntity, Long> {

}

We can add more methods that accept Pageable and Sort as parameters for any custom requirements.

List<EmployeeEntity> findEmployeesByDepartmentId(long departmentId, Pageable pageable);

3. Accepting Page and Sort Parameters

Generally, paging and sorting parameters are optional and thus part of the request URL as query parameters. If any API supports paging and sorting, ALWAYS provide default values to these parameters – to be used when the client does not choose to specify any paging or sorting preferences.

The default paging and sorting values shall be clearly documented in API docs. In UI, these default values can be highlighted with separate colors.

Page number values start with 0. So in UI, if you are displaying page number from 1, then do not forget to subtracting ‘1’ while fetching records.

In the below Spring MVC controller, we are accepting paging and sorting parameters using the following query parameters:

  • pageNo: The current page number.
  • pageSize: Number of records on each page.
  • sortBy: Field names if any sorting has to be applied.

Also, by default '10' employees will be fetched from the database on page number '0', and employee records will be sorted based on 'id' field.

@RestController
@RequestMapping("/employees")
public class EmployeeController
{
    @Autowired
    EmployeeService service;

    @GetMapping
    public ResponseEntity<List<EmployeeEntity>> getAllEmployees(
                        @RequestParam(defaultValue = "0") Integer pageNo,
                        @RequestParam(defaultValue = "10") Integer pageSize,
                        @RequestParam(defaultValue = "id") String sortBy)
    {
        List<EmployeeEntity> list = service.getAllEmployees(pageNo, pageSize, sortBy);

        return new ResponseEntity<List<EmployeeEntity>>(list, new HttpHeaders(), HttpStatus.OK);
    }
}

To perform pagination and/or sorting, we must create org.springframework.data.domain.Pageable or org.springframework.data.domain.Sort instances are passed to the findAll() method.

@Service
public class EmployeeService
{
    @Autowired
    EmployeeRepository repository;

    public List<EmployeeEntity> getAllEmployees(Integer pageNo, Integer pageSize, String sortBy)
    {
        Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by(sortBy));

        Page<EmployeeEntity> pagedResult = repository.findAll(paging);

        if(pagedResult.hasContent()) {
            return pagedResult.getContent();
        } else {
            return new ArrayList<EmployeeEntity>();
        }
    }
}

4. Pagination and Sorting Examples

4.1. Paging Only

To apply only pagination in the result set, we shall create Pageable object without any Sort information.

Pageable paging = PageRequest.of(pageNo, pageSize);

Page<EmployeeEntity> pagedResult = repository.findAll(paging);

4.2. Sorting Only

If there is no need to page, and only sorting is required, we can create Sort object for that.

Sort sortOrder = Sort.by("email"); 

List<EmployeeEntity> list = repository.findAll(sortOrder);

If we wish to apply to sort on multiple columns or group by sort, then that is also possible by creating Sort using simple builder pattern steps.

Sort emailSort = Sort.by("email");
Sort firstNameSort = Sort.by("first_name"); 

Sort groupBySort = emailSort.and(firstNameSort);

List<EmployeeEntity> list = repository.findAll(groupBySort);

4.3. Paging and Sorting Together

To apply only pagination in the result set, we shall create Pageable object with desired Sort column name.

Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by("email"));

Page<EmployeeEntity> pagedResult = repository.findAll(paging);

By default, records are ordered in DESCENDING order. To choose ASCENDING order, use .ascending() method.

Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by("email").ascending()); 

Page<EmployeeEntity> pagedResult = repository.findAll(paging);

5. Difference between Page and Slice

5.1. Page

The findAll(Pageable pageable) method by default returns a Page object. A Page object provides much more useful information than just a list of employees on the current page. For example, a Page object has the number of total pages, the current page number and whether the current page is the first or the last page.

Finding total pages invokes an additional count() query causing an extra overhead cost. Be sure when we are using it.

5.2. Slice

Slice is very much similar to Page, except it does not provide the number of total pages in the database. It helps in improving performance when we do not need to display the total number of pages in UI.

Generally, Slice is used in case navigation consists of Next page and Previous page links.

To use Slice, we need to implement our own custom methods.

public interface EmployeeRepository extends PagingAndSortingRepository<EmployeeEntity, Long>
{
    public Slice<EmployeeEntity> findByFirstName(String firstName, Pageable pageable);
}

Remember that if we use PagingAndSortingRepository, the default return type is Page.

Pageable paging = PageRequest.of(pageNo, pageSize, Sort.by("email").descending()); 

Slice<EmployeeEntity> slicedResult = repository.findByFirstName("alex", paging); 

List<EmployeeEntity> employeeList = slicedResult.getContent();

6. Demo

In this demo, the default page number is 0, the page size is 10, and the default sort column is ‘id’.

Now invoke these URLs one by one and observe the outputs.

  • http://localhost:8080/employees?pageSize=5
  • http://localhost:8080/employees?pageSize=5&pageNo=1
  • http://localhost:8080/employees?pageSize=5&pageNo=2
  • http://localhost:8080/employees?pageSize=5&pageNo=1&sortBy=email
  • http://localhost:8080/employees?pageSize=5&pageNo=1&sortBy=firstName

Drop me your questions in the comments related to how to paginate and sort using Spring Data JPA.

Happy Learning !!

Sourcecode download

Comments

Subscribe
Notify of
guest
7 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode