Pagination with JpaRepository and Pageable
Fast recall
Quick revision
Core answer
Whether you can paginate large datasets correctly and know Page runs an extra count query while Slice does not
Forgetting pages are zero-indexed, and using Page everywhere when Slice avoids a costly count query on huge tables
Explain Page vs Slice, capping max page size, passing a stable Sort, and that PageRequest maps to LIMIT/OFFSET
Interview-ready guide
Interview answer
PageRequest.of(0, 20, Sort.by("createdAt").descending().and(Sort.by("id").descending())) requests the first twenty rows in stable order. A Page is useful when the UI needs 'page 2 of 47'. A Slice is better for 'load more' because it only needs content and hasNext. With a complex join, provide an accurate countQuery when Spring cannot derive one safely. Do not expose unbounded page sizes from client input.
For example, findByStatus(OPEN, PageRequest.of(0, 20, Sort.by("createdAt").descending())) requests the first 20 open orders. Page also runs or derives a count query; Slice avoids the total count when the UI only needs to know whether another page exists.
Pagination with JpaRepository and Pageable
Direct Answer
A Pageable carries a zero-based page number, size, and optional Sort. A Page contains content plus total counts and therefore normally needs a count query; Spring Data may avoid that query when it can infer the total. A Slice reports only whether another slice exists and usually fetches one extra row instead of counting all matches. The provider renders database-specific limiting SQL, not always literal LIMIT/OFFSET.
Code Example
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderRepository orderRepository;
OrderController(OrderRepository r) { this.orderRepository = r; }
@GetMapping
public Page<Order> getOrders(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
int safeSize = Math.min(size, 100); // cap to protect the DB
Pageable pageable = PageRequest.of(page, safeSize, Sort.by("createdAt").descending());
return orderRepository.findAll(pageable); // Page includes total count
}
// Infinite scroll: Slice skips the count query
@GetMapping("/feed")
public Slice<Order> feed(@RequestParam(defaultValue = "0") int page) {
return orderRepository.findByStatus("OPEN", PageRequest.of(page, 20));
}
}
Common Mistakes & Pitfalls
- Page where Slice fits — on million-row tables the count query can take seconds; infinite-scroll UIs don't need totals, so use
Slice. - Off-by-one pages — pages are zero-indexed; if the frontend sends 1-based numbers, subtract 1.
- No max page size — a request for
size=1000000can overwhelm the DB; cap it (e.g., 100). - Forgetting sort — without a stable
Sort, page boundaries can shift between requests.
Check your understanding
Answer these without looking back at the article.
- What is the difference between Page and Slice in Spring Data?
- How do you create a PageRequest with sorting?
- How do you expose pagination parameters in a Spring Boot REST endpoint?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.