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.
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> {
}
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
| Method | Path | Description |
|---|---|---|
| GET | /api/customers | Paginated 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)
| Issue | Cause | Solution |
|---|---|---|
| Sorting on non-indexed columns is slow | DB scans entire table | Index frequently sorted fields |
| Bad user input breaks sorting | Invalid field in sortBy | Validate or use a whitelist |
| Large OFFSET values → slow | DB loads many rows before page | Use keyset pagination for huge pages |
10. Best Practices for Production
- Always provide
defaultpagination 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.
📄 Build Scalable APIs with Pagination & Sorting
Pagination and sorting are essential for handling large datasets efficiently in real-world Spring Boot applications. Strengthen your backend skills by exploring these related JPA, performance, and interview-focused topics.
📘 Spring Boot JPA Basics
Understand repositories, entities, and queries before applying pagination.
🧩 Spring Boot CRUD API Example
See pagination and sorting applied in a complete REST API example.
🔍 Sorting & Filtering with JPA Specifications
Build dynamic search APIs by combining filtering with pagination.
🗄️ Database Performance Tuning
Learn how pagination impacts indexes, query plans, and database load.
🚀 Spring Boot Performance Tuning
Avoid slow pagination strategies that cause latency and memory issues.
🚨 Global Exception Handling
Handle invalid page, size, and sort parameters gracefully.
🎓 Spring Boot Interview Questions (Freshers)
Common interview questions covering pagination and repositories.
💼 Spring Boot Interview Questions (2–5 Years)
Real-world interview scenarios involving pagination, sorting, and performance.