9.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 additionalcountquery to determine the total number of items (suitable for standard paginated lists).Slice<T>: Only checks if a next page exists without acountquery, providing better performance (suitable for infinite scrolls).
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.