Home/Learn/Spring Boot/Pagination & Sorting

Pagination & Sorting

Intermediate
Data Access

Pass a Pageable object to repository methods; Spring Data returns a Page<T> containing the slice of results plus total-count metadata.

Overview

Spring Data JPA pagination is built around Pageable (request) and Page<T> (response). A Pageable carries the page number (zero-based), page size, and Sort instructions; repositories automatically append LIMIT/OFFSET and a COUNT(*) query. Page<T> wraps the content slice plus metadata: totalElements, totalPages, number (current page), first/last flags. For large tables the COUNT(*) query can be expensive; use Slice<T> (no count) when you only need "has more pages" semantics for infinite scroll. Dynamic sorting from API parameters requires sanitisation — never pass raw field names from request parameters directly to Sort to prevent injection.

Repository pagination and controller wiring

Repository methods accept Pageable; Spring MVC resolves Pageable from request parameters automatically with @EnableSpringDataWebSupport.

Java — repository + controller with Pageable
// Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

    // Spring Data generates: SELECT * FROM orders WHERE status=? LIMIT ? OFFSET ?
    // + COUNT(*) query for totalElements
    Page<Order> findByStatus(String status, Pageable pageable);

    // Custom JPQL with pagination
    @Query("SELECT o FROM Order o WHERE o.createdAt > :since")
    Page<Order> findRecentOrders(@Param("since") LocalDateTime since,
                                  Pageable pageable);

    // Slice<T>: no count query — use for infinite scroll
    Slice<Order> findByCustomerId(Long customerId, Pageable pageable);
}

// Controller — Spring resolves Pageable from ?page=0&size=20&sort=createdAt,desc
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @GetMapping
    public Page<OrderDto> list(
            @RequestParam(defaultValue = "PENDING") String status,
            @PageableDefault(size = 20, sort = "createdAt",
                             direction = Sort.Direction.DESC) Pageable pageable) {
        return orderRepository.findByStatus(status, pageable)
                              .map(orderMapper::toDto);
    }
}

PageRequest and programmatic sorting

Construct PageRequest programmatically in service code when you need to control page size or sanitise sort fields.

Java — sanitised PageRequest with Sort
@Service
public class OrderService {

    private static final Set<String> SORTABLE_FIELDS = Set.of(
        "createdAt", "total", "status"
    );

    public Page<OrderDto> search(OrderSearchRequest req) {
        // Sanitise sort field — never trust raw user input
        String sortField = SORTABLE_FIELDS.contains(req.getSortBy())
            ? req.getSortBy() : "createdAt";

        Sort sort = Sort.by(
            "asc".equalsIgnoreCase(req.getDirection())
                ? Sort.Direction.ASC : Sort.Direction.DESC,
            sortField
        );

        // PageRequest is the concrete Pageable implementation
        Pageable pageable = PageRequest.of(
            Math.max(0, req.getPage()),       // clamp negative page to 0
            Math.min(100, req.getSize()),     // cap page size at 100
            sort
        );

        return orderRepository.findByStatus(req.getStatus(), pageable)
                              .map(orderMapper::toDto);
    }
}

// Multiple sort columns
Pageable multiSort = PageRequest.of(0, 20, Sort.by(
    Sort.Order.desc("status"),
    Sort.Order.asc("createdAt")
));

Page response shape and Slice for infinite scroll

Page<T> includes a COUNT query; Slice<T> skips it. For REST APIs, map Page<Entity> to a custom PageResponse DTO to control the response shape.

Java — custom PageResponse DTO + Slice infinite scroll
// Custom PageResponse DTO — avoids exposing Spring internals
@Value
public class PageResponse<T> {
    List<T>  content;
    int      page;
    int      size;
    long     totalElements;
    int      totalPages;
    boolean  first;
    boolean  last;

    public static <T> PageResponse<T> from(Page<T> page) {
        return new PageResponse<>(
            page.getContent(),
            page.getNumber(),
            page.getSize(),
            page.getTotalElements(),
            page.getTotalPages(),
            page.isFirst(),
            page.isLast()
        );
    }
}

// Controller returns custom DTO
@GetMapping
public PageResponse<OrderDto> list(@PageableDefault(size = 20) Pageable pageable) {
    Page<OrderDto> page = orderRepository.findAll(pageable).map(orderMapper::toDto);
    return PageResponse.from(page);
}

// Slice for infinite scroll (no count query → faster)
@GetMapping("/scroll")
public List<OrderDto> scroll(Pageable pageable) {
    Slice<Order> slice = orderRepository.findByCustomerId(currentUser(), pageable);
    // slice.hasNext() tells client whether to fetch next page
    return slice.getContent().stream().map(orderMapper::toDto).toList();
}

Key Points to Remember

  • 1Spring MVC resolves Pageable automatically from ?page=0&size=20&sort=field,direction request parameters.
  • 2Page<T> issues a COUNT(*) query for totalElements; Slice<T> skips it — use Slice for infinite scroll.
  • 3Always sanitise sort field names — never pass raw user input to Sort to prevent field-name injection.
  • 4Use @PageableDefault to set default page size and sort direction when no query parameters are provided.
  • 5Map Page<Entity> to a custom DTO before returning from the controller — do not leak JPA proxy internals.
  • 6For large offsets (page=10000), consider cursor-based pagination — OFFSET scans all preceding rows.

Interview Questions

Sign in to ask Aria
1

What is the difference between Page<T> and Slice<T> in Spring Data?

EasyAmazon
2

How does Spring MVC resolve a Pageable parameter from HTTP request parameters?

MediumNetflix
3

What SQL queries does Spring Data execute for a Page<T> repository method?

MediumGoogle
4

Why is OFFSET-based pagination slow for large offsets and what is the alternative?

HardUber
5

How would you prevent users from sorting by arbitrary database columns they should not see?

MediumShopify

Ask Aria about Pagination & Sorting

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…