Careeroza
Pageable and Sort
Never Load the Whole Table
A store may have 2 lakh products. Loading them all to show 20 wastes memory and time. Spring Data adds pagination to any repository method that takes a Pageable parameter; Hibernate turns it into LIMIT/OFFSET.
public interface ProductRepository extends JpaRepository<Product, Long> {
Page<Product> findByCategoryName(String category, Pageable pageable);
}
Pageable pageable = PageRequest.of(2, 20, Sort.by("price").descending().and(Sort.by("id")));
Page<Product> page = productRepository.findByCategoryName("Books", pageable);-- page index 2, size 20 -> skip 40 rows (MySQL)
select p1_0.id,p1_0.category_id,p1_0.description,p1_0.name,p1_0.price,p1_0.stock
from products p1_0 join categories c1_0 on c1_0.id=p1_0.category_id
where c1_0.name=?
order by p1_0.price desc,p1_0.id
limit ?,?
-- count query for totalElements
select count(p1_0.id) from products p1_0 join categories c1_0 on c1_0.id=p1_0.category_id where c1_0.name=?Page Math
Example
Page numbers in Spring Data start at 0. Here is how offset, total pages and the last page are calculated for 53 products, 10 per page.
public class Main {
public static void main(String[] args) {
int total = 53, size = 10;
int totalPages = (total + size - 1) / size;
System.out.println("totalElements=" + total + ", size=" + size + ", totalPages=" + totalPages);
for (int page = 0; page < totalPages; page++) {
int offset = page * size;
int rows = Math.min(size, total - offset);
boolean first = page == 0, last = page == totalPages - 1;
System.out.printf("page %d -> limit %d,%d rows %2d first=%-5s last=%-5s%n",
page, offset, size, rows, first, last);
}
}
}Output
totalElements=53, size=10, totalPages=6 page 0 -> limit 0,10 rows 10 first=true last=false page 1 -> limit 10,10 rows 10 first=false last=false page 2 -> limit 20,10 rows 10 first=false last=false page 3 -> limit 30,10 rows 10 first=false last=false page 4 -> limit 40,10 rows 10 first=false last=false page 5 -> limit 50,10 rows 3 first=false last=true
Sorting
Sort.by("price"),Sort.by(Sort.Direction.DESC, "createdAt"),Sort.by("category.name")(sort by a joined field).- Sort without paging:
List<Product> findByStockGreaterThan(int s, Sort sort). - Typed:
Sort.sort(Product.class).by(Product::getPrice).descending(). - Always add a unique tie-breaker (e.g.
id). With onlyprice, products with equal prices can appear on two pages or on none.
Note: Sort properties coming from the URL are validated against the entity, so ?sort=password on a non-existing field fails with PropertyReferenceException. Still, allow only a whitelist of sortable fields in public APIs.