Skip to main content

10.5 Pagination and Sorting (Pageable)

When dealing with thousands of records, displaying them all on a single page is poor for performance and user experience. Spring Data JPA provides the Pageable interface to implement pagination and sorting easily.

1. Pageable and Page Interfaces​

Spring Data JPA provides two core interfaces for handling pagination:

  • Pageable: An interface containing pagination request information (page number, size, and sorting criteria).
  • Page<T>: An interface that contains the resulting data list along with metadata like total pages and total elements.

2. Repository Layer Implementation​

Simply add a Pageable parameter to your repository method.

public interface MemberRepository extends JpaRepository<Member, Long> {

// Search by name with pagination
Page<Member> findByNameContaining(String name, Pageable pageable);
}

3. Controller Layer Implementation​

When you include Pageable as a parameter in a controller method, Spring automatically binds query parameters (e.g., ?page=0&size=10&sort=id,desc) to it.

@RestController
@RequestMapping("/api/members")
public class MemberController {

private final MemberRepository memberRepository;

@GetMapping
public Page<MemberResponseDto> getMembers(
@PageableDefault(size = 10, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {

return memberRepository.findAll(pageable)
.map(MemberResponseDto::from); // Convert Entity to DTO
}
}

Key Query Parameters​

  • page: Page number (starting from 0).
  • size: Number of records per page.
  • sort: Sorting field and direction (sort=name,desc).

4. Understanding the Response Structure​

Returning a Page<T> type includes rich metadata in the JSON response:

{
"content": [...], // Actual data list
"pageable": { ... }, // Pagination info
"totalElements": 105, // Total number of records
"totalPages": 11, // Total number of pages
"last": false, // Whether it's the last page
"size": 10, // Number of items per page
"number": 0, // Current page number
"sort": { ... }, // Sorting info
"numberOfElements": 10, // Number of items on current page
"first": true, // Whether it's the first page
"empty": false // Whether the result is empty
}

5. Page vs. Slice​

  • Page<T>: Executes an additional count query to determine the total number of items (suitable for standard paginated lists).
  • Slice<T>: Only checks if a next page exists without a count query, providing better performance (suitable for infinite scrolls).
tip

In production environments with massive datasets, the count query can become a performance bottleneck. In such cases, consider using "Infinite Scroll" (Slice) or optimizing the count query with Querydsl.