Spring Data JPA - Pagination and Sorting

Pagination and Sorting

When you’re building web apps or APIs, working with large tables can get tricky fast. Pulling in every row from a table with thousands of entries isn’t practical and can slow things down. That’s where pagination and sorting make a difference. Spring Data JPA gives you tools to break up the data into smaller chunks and sort it the way you need, without a lot of extra code.

Native Queires do not support Dynamic Sorting

When working with a JPQL query, you can add a parameter of type Sort to your repository method. This enables you to define the sorting criteria at runtime. Spring Data JPA then generates the required ORDER BY clause based on the provided parameter value.

Unfortunately, Spring Data JPA doesn’t support this feature for native queries. Doing that would require Spring Data to analyze the provided statement and generate the ORDER BY clause in the database-specific dialect. This would be a very complex operation and is currently not supported by Spring Data JPA.

You can, of course, add your own ORDER BY clause to your query. But that limits you to one specific ordering per query. If you need to support multiple ones, using a composite repository is often the better approach. You can then implement your own query method using JPA’s Criteria API and specify the ORDER BY clause based on the provided input parameters.

https://thorben-janssen.com/composite-repositories-spring-data-jpa/

Basic Pagination

Spring Data JPA’s PagingAndSortingRepository provides methods to fetch data in a paginated format.

See Spring Data JPA - Repositories

Here’s a simple usage:

public interface UserRepository extends PagingAndSortingRepository<User, Long> {}

// Service or Controller layer
@Autowired
private UserRepository userRepository;

public Page<User> getAllUsers(Pageable pageable) {
    return userRepository.findAll(pageable);
}

When calling the getAllUsers method, you provide a Pageable object, which encapsulates pagination information, such as the page number and size.

Incorporating Sorting

The Pageable object can also include sorting directives:

Pageable pageable = PageRequest.of(0, 10, Sort.by("name"));
Page<User> users = getAllUsers(pageable);

In the example above, results are sorted by the name attribute in ascending order. For descending order, you can use Sort.by(“name”).descending().

Custom Queries with Pagination

You can combine custom queries with pagination:

public interface UserRepository extends PagingAndSortingRepository<User, Long> {
    Page<User> findByNameContaining(String name, Pageable pageable);
}

This repository method fetches users with names containing a specific string and supports pagination.

Web Integration

Spring makes it relatively easy to integrate pagination with web controllers. For instance:

@GetMapping("/users")
public ResponseEntity<Page<User>> fetchUsers(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(defaultValue = "name") String sortBy) {

    Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
    Page<User> users = userRepository.findAll(pageable);

    return ResponseEntity.ok(users);
}

The above endpoint allows clients to request user data with pagination and sorting parameters.

Things to Keep in Mind

  1. Set a Default Limit
    1. If you don’t set a default page size, it’s easy to end up pulling everything from the database at once. Even a simple query can turn expensive if the table gets big.
  2. Cap the Maximum Page Size
    1. Letting clients ask for thousands of records in a single request can hurt performance. It’s a good idea to set an upper bound so your app doesn’t slow down under pressure.
  3. Double-Check Sort Fields
    1. Sorting by user-provided values can get risky. Make sure you’re only allowing known fields, or you might run into unexpected results or open yourself up to bad queries.
  4. Stick to Projections When Possible
    1. If you only need a few fields, don’t fetch the whole object. Pairing projections with pagination can help cut down on the amount of data you’re pulling from the database and sending over the wire.

Reading material

  1. Sort - order my multiple columns: https://www.bezkoder.com/spring-data-sort-multiple-columns/
  2. https://medium.com/@AlexanderObregon/optimizing-queries-with-query-annotation-in-spring-data-jpa-fe213c8a60a

Links to this note