What is Pagination?
Pagination is the process of dividing large data sets into smaller, manageable chunks, called pages. Instead of returning all rows from a database table (which can be thousands), you fetch a limited number of rows per request (like 10, 20, or 50).
Why Use Pagination?
- Performance: Reduces memory usage and improves response time.
- Scalability: Fetching 10 records is faster and lighter than fetching 10,000.
- User Experience: UI can show data page-by-page, making it readable.
Pagination Support in Spring Data JPA
Spring Data JPA provides built-in support for pagination using:
PageableinterfacePage<T>andSlice<T>interfaces
Use Case: Pagination on a Product Entity
We’ll create a Product entity and implement an API to fetch products with pagination.
Repository
Spring Data JPA provides pagination-ready methods out of the box.
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<Product> findAll(Pageable pageable);
}
Service Layer
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public Page<Product> getProducts(int page, int size, String sortBy) {
Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
return productRepository.findAll(pageable);
}
}
Controller Example
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public Page<Product> getAllProducts(
@RequestParam(defaultValue = "0") int page, // Page number
@RequestParam(defaultValue = "5") int size, // Page size
@RequestParam(defaultValue = "id") String sortBy // Sort field
) {
return productService.getProducts(page, size, sortBy);
}
}
Sample API Calls
URL: /api/products?page=0&size=10&sortBy=name
Returns:
{
"content": [
{
"id": 1,
"name": "Product 1",
"price": 10.0
},
...
],
"totalPages": 10,
"totalElements": 100,
"last": false,
"size": 10,
"number": 0,
"sort": {...},
"first": true,
"numberOfElements": 10,
"empty": false
}
Understanding the Page<T> Object
Spring’s Page<T> object gives you:
getContent()– list of current page contentgetTotalElements()– total number of recordsgetTotalPages()– total pagesgetNumber()– current page numberisFirst()/isLast()– is it the first/last pagegetSize()– size of each page
Pagination with Sorting
You can sort the result easily:
Pageable pageable = PageRequest.of(page, size, Sort.by("price").descending());
Or for multiple fields:
Sort sort = Sort.by("price").descending().and(Sort.by("name"));
Pageable pageable = PageRequest.of(page, size, sort);
Slice<T> in Spring Data JPA
Slice<T> is similar to Page<T>, but it doesn’t fetch the total number of elements or total pages.
When to use Slice<T> instead of Page<T>?
Use Slice<T> when:
- You don’t care about the total number of items in the result.
- You just want to fetch the next chunk/page of data.
- You want better performance, especially with very large datasets, because
Page<T>executes an additionalCOUNT(*)query to get totals, whileSlice<T>skips that.
| Feature | Page<T> | Slice<T> |
|---|---|---|
| Total items | Yes (getTotalElements()) | No |
| Total pages | Yes (getTotalPages()) | No |
| Has next/prev page? | Yes | Yes (hasNext(), hasPrevious()) |
| Additional COUNT query? | Yes | No (faster) |
How Does Slice<T> Work Internally?
To determine if more data is available, Slice<T> fetches one extra record than requested. So if you request size=5, it actually queries 6 records.
- If it finds 6 records → it sets
hasNext = trueand drops the extra record. - If less than or equal to 5 records → it sets
hasNext = false.
This allows efficient pagination without counting total rows.
