Pagination and Sorting in Spring Boot

  • Last updated Apr 26, 2024

Pagination is the process of dividing data into separate pages. 

Sorting is the process of ordering data in ascending or descending order.

In Spring Boot, pagination and sorting are important features for handling large datasets efficiently and presenting them to users in a manageable way. Spring Boot provides built-in support for pagination and sorting using Spring Data JPA, which makes it easy to paginate query results and apply sorting based on specific properties.

To use pagination and sorting in Spring Boot with Spring Data JPA, we can perform a paged query by using the built-in Pageable parameter in query methods:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.user.entity.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

   public Page<User> findAll(Pageable pageable);
		
}

Here's a code that represents a Spring Boot RestController that handles HTTP requests. It uses Spring Data's pagination and sorting support to retrieve a paginated list of users from a service and returns the result as a Page of UserResponse objects:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.SortDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.user.model.UserResponse;
import com.example.user.service.UserService;

@RequestMapping(path = "/users")
@RestController
public class UserController {

  @Autowired
  private UserService userService;


  @GetMapping(path = "/all")
  public ResponseEntity<Page<UserResponse>> getUsers(
      @PageableDefault(page = 0, size = 30) @SortDefault.SortDefaults({
          @SortDefault(sort = "modifiedDate", direction = Sort.Direction.DESC)}) Pageable pageable) {

    return ResponseEntity.ok(userService.getUsers(pageable));
  }
}

Here, the @PageableDefault annotation sets default values for pagination if the client doesn't provide any parameters. In this case, the default page number is 0, and the default page size is 30. The @SortDefault annotation sets default sorting for the query if the client doesn't provide any sorting parameters. Here, the default sorting is done based on the "modifiedDate" property in descending order (Sort.Direction.DESC). The Pageable object as a parameter, will be automatically populated with the pagination and sorting information from the request (using the defaults if not provided).

The API endpoint should include page, size, and sort as query params. For example : http://localhost:8080/users/all?page=0&size=30&sort=modifiedDate,desc

Pagination and sorting can also be included as a method parameters as follows:

@GetMapping(path = "/all")
public ResponseEntity<Page<UserResponse>> getUsers(
      @RequestParam(name = "page", defaultValue = "0") int page,
      @RequestParam(name = "size", defaultValue = "30") int size,
      @RequestParam(name = "sort", defaultValue = "firstName,asc") String sort) {

    Pageable pageable = PageRequest.of(page, size, Sort.by(sort));

    Page<UserResponse> usersPage = userService.getUsers(pageable);

    return ResponseEntity.ok(usersPage);
}


Here's a Java class that represents a Service interface called UserService. It defines a method for retrieving users in a paginated manner. The service abstracts the data access layer and allows the controller to interact with the users without worrying about the underlying data retrieval logic:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import com.example.user.model.UserResponse;

public interface UserService {

  Page<UserResponse> getUsers(Pageable pageable);
}

Here's an implementation of the UserService interface. It defines the getUsers method to retrieve a paginated list of UserResponse objects, which are transformed from User entities fetched from the UserRepository:

import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import com.example.user.model.UserResponse;
import com.example.user.service.UserService;

@Service
public class UserServiceImpl implements UserService {
  
  @Autowired
  private UserRepository userRepository;
  
  @Override
  public Page<UserResponse> getUsers(Pageable pageable) {
    Page<User> usersPage = userRepository.findAll(pageable);

    List<UserResponse> users = new ArrayList<>();
    Page<UserResponse> usersResponsePage = new PageImpl<>(users, pageable, 0);

    if (usersPage != null && !usersPage.isEmpty()) {

      usersPage.getContent().forEach(user -> {

        UserResponse userResponse = UserResponse.builder().userId(user.getId())
            .firstName(user.getFirstName() != null ? user.getFirstName() : "")
            .lastName(user.getLastName() != null ? user.getLastName() : "")
            .email(user.getEmail() != null ? user.getEmail() : "")
            .createdDate(user.getCreatedDate() != null ? user.getCreatedDate() : null)
            .modifiedDate(user.getModifiedDate() != null ? user.getModifiedDate() : null)
            .build();

        users.add(userResponse);
      });
      usersResponsePage =
          new PageImpl<>(users, pageable, usersPage.getTotalElements());
    }
    return usersResponsePage;
  }
  
}