Spring Boot Pagination and Sorting with Spring Data JPA — Complete Guide with Examples

Spring Boot Pagination and Sorting with Spring Data JPA (Best Practices)

When building REST APIs, it’s critical to handle large database tables efficiently. Instead of returning thousands of records at once, we should return only a single page of data — with sorting and metadata like total pages, count, etc.

In this guide, you will learn everything about pagination and sorting using Spring Data JPA — from basics to real-world advanced techniques 🚀.

1. Why Pagination Matters?

  • Reduces memory consumption
  • Faster API responses
  • Better UX in tables and infinite scroll
  • Scales easily with large datasets

2. Project Setup — Spring Boot + JPA

2.1 Maven dependencies

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
  </dependency>
</dependencies>

We use H2 DB for simplicity, but you can replace with MySQL/Oracle.


3. Example Entity — Customer

package com.example.pagination.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name = "customers")
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder
public class Customer {
  
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;
  private String email;
  private Integer age;
}

4. Repository — Pageable and Sort

package com.example.pagination.repository;

import com.example.pagination.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
No extra code required — JpaRepository already provides pagination + sorting support!

5. Service — Pagination Logic

package com.example.pagination.service;

import com.example.pagination.entity.Customer;
import com.example.pagination.repository.CustomerRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CustomerService {

  private final CustomerRepository repository;

  public Page<Customer> getCustomers(int page, int size, String sortBy, String dir) {
    Sort sort = dir.equalsIgnoreCase("desc")
        ? Sort.by(sortBy).descending()
        : Sort.by(sortBy).ascending();

    Pageable pageable = PageRequest.of(page, size, sort);
    return repository.findAll(pageable);
  }
}

6. REST API — Dynamic Pagination + Sorting

MethodPathDescription
GET/api/customersPaginated list
package com.example.pagination.controller;

import com.example.pagination.entity.Customer;
import com.example.pagination.service.CustomerService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/customers")
@RequiredArgsConstructor
public class CustomerController {

  private final CustomerService service;

  @GetMapping
  public ResponseEntity<Page<Customer>> getCustomers(
      @RequestParam(defaultValue = "0") int page,
      @RequestParam(defaultValue = "5") int size,
      @RequestParam(defaultValue = "id") String sortBy,
      @RequestParam(defaultValue = "asc") String direction
  ) {
      return ResponseEntity.ok(service.getCustomers(page, size, sortBy, direction));
  }
}

7. Example API Calls

7.1 Page #0, size 5

GET http://localhost:8080/api/customers?page=0&size=5

7.2 Sort by age descending

GET http://localhost:8080/api/customers?sortBy=age&direction=desc

Response contains metadata:

{
  "content": [...],
  "totalPages": 3,
  "totalElements": 12,
  "size": 5,
  "number": 0,
  "numberOfElements": 5,
  "last": false
}

8. Sorting on Multiple Fields

Sort sort = Sort.by("age").descending()
                .and(Sort.by("name").ascending());
Pageable pageable = PageRequest.of(page, size, sort);

9. Common Pitfalls (and Fixes)

IssueCauseSolution
Sorting on non-indexed columns is slowDB scans entire tableIndex frequently sorted fields
Bad user input breaks sortingInvalid field in sortByValidate or use a whitelist
Large OFFSET values → slowDB loads many rows before pageUse keyset pagination for huge pages

10. Best Practices for Production

  • Always provide default pagination values
  • Expose only sorted columns allowed by API
  • Prefer indexes on sorting fields
  • Paginate at DB level, NEVER in Java code

11. Summary

You now know how to build fast and scalable paginated APIs using Spring Boot and JPA:

  • Fetch only data needed for UI
  • Dynamic sorting
  • Metadata for pagination UI
  • Multiple field sorting
  • Security + performance considerations

Next steps: filtering, search pagination, keyset pagination with cursor based scrolling.