In modern applications, databases often contain millions of records. Fetching all of them at once is both inefficient and impractical. This is where pagination comes in — the ability to fetch only a subset of results (like page 1 with 10 records, page 2 with the next 10, and so on).
Hibernate, as a powerful ORM tool, provides simple yet effective APIs for pagination using setFirstResult(int)
and setMaxResults(int)
. These methods ensure efficient database interaction, reduced memory consumption, and faster user experiences.
Think of pagination like reading a book: you don’t read all the pages at once, but flip to the current page you need.
What is Pagination in Hibernate?
Pagination is the process of dividing large datasets into smaller, manageable chunks. In Hibernate, pagination allows applications to retrieve only a portion of results from the database instead of loading the entire dataset.
setFirstResult(int startPosition)
: Defines the starting row (zero-based index).setMaxResults(int maxResults)
: Defines the maximum number of rows to fetch.
Why Use Pagination?
- Performance optimization: Fetch only the required records.
- Memory efficiency: Prevents the application from loading large datasets into memory.
- Improved UX: Enables smooth navigation for end-users, similar to e-commerce websites displaying products page by page.
Setting Up Hibernate Pagination
Example Entity
import jakarta.persistence.*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
// Getters and Setters
}
Pagination with HQL Query
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.query.Query;
import java.util.List;
public class HibernatePaginationExample {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
Query<Product> query = session.createQuery("FROM Product", Product.class);
query.setFirstResult(0); // Starting row index (page 1)
query.setMaxResults(5); // Fetch 5 results
List<Product> products = query.list();
products.forEach(p -> System.out.println(p.getName() + " - " + p.getPrice()));
tx.commit();
session.close();
}
}
Pagination with Criteria API
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import java.util.List;
public class CriteriaPaginationExample {
public static void main(String[] args) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Product.class);
criteria.setFirstResult(5); // Skip first 5 records
criteria.setMaxResults(5); // Fetch next 5 records
List<Product> products = criteria.list();
products.forEach(p -> System.out.println(p.getName()));
tx.commit();
session.close();
}
}
Pagination with Native SQL Query
Query<Product> query = session.createNativeQuery("SELECT * FROM products", Product.class);
query.setFirstResult(10); // Start at row 10
query.setMaxResults(10); // Fetch 10 rows
List<Product> products = query.list();
Real-World Example: Pagination with Spring Boot + Hibernate
In a Spring Boot application, pagination is commonly used with Pageable
and Page
objects.
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);
}
Usage:
Page<Product> page = productRepository.findAll(PageRequest.of(0, 10));
page.getContent().forEach(System.out::println);
Performance Considerations
- Indexes matter: Ensure your database columns used in ordering or filtering are indexed.
- Avoid deep pagination: Jumping to page 10000 can still be expensive — use keyset pagination instead.
- Batch fetching: Combine pagination with batching for related entities.
Analogy: Pagination is like ordering food in portions. You don’t order everything on the menu at once; you get just what you can consume.
Common Pitfalls and Anti-Patterns
- N+1 Select Problem: When paginated entities fetch related collections lazily, it may cause multiple queries.
- Eager fetching misuse: Loading all associations eagerly on each page increases memory usage.
- Large OFFSET values: As
setFirstResult
skips rows, performance degrades for very high offsets.
Best Practices
- Use Spring Data JPA Pageable for enterprise applications.
- Prefer keyset pagination for high-offset use cases.
- Monitor queries with Hibernate’s SQL logging.
- Use second-level cache for frequently accessed paginated results.
📌 Hibernate Version Notes
Hibernate 5.x
- Pagination with
setFirstResult
andsetMaxResults
is widely supported. - Legacy
Criteria
API is available but deprecated.
Hibernate 6.x
- Jakarta Persistence (
jakarta.persistence
) namespace replacesjavax.persistence
. - Enhanced SQL and pagination support with improved query plans.
- Criteria API fully migrated to
jakarta.persistence.criteria
.
Conclusion and Key Takeaways
Pagination in Hibernate using setFirstResult
and setMaxResults
is a powerful feature to handle large datasets efficiently. By applying pagination correctly, developers can achieve faster query execution, reduced memory load, and better end-user experiences.
Key Takeaways:
- Use pagination to improve application scalability.
- Combine with caching and indexing for optimal performance.
- Avoid anti-patterns like eager fetching with large datasets.
- Use Spring Data JPA for seamless integration with modern apps.
FAQ
Q1. What’s the difference between Hibernate and JPA?
Hibernate is an ORM implementation; JPA is a specification. Hibernate implements JPA.
Q2. How does Hibernate caching improve performance?
By storing frequently used data in memory, Hibernate reduces repeated DB hits.
Q3. What are the drawbacks of eager fetching?
It loads unnecessary associations, increasing memory usage and query time.
Q4. How do I solve the N+1 select problem in Hibernate?
Use JOIN FETCH
, batch fetching, or entity graphs.
Q5. Can I use Hibernate without Spring?
Yes, Hibernate works standalone with plain Java SE.
Q6. What’s the best strategy for inheritance mapping?
It depends: SINGLE_TABLE
is simplest, JOINED
is normalized, and TABLE_PER_CLASS
is rare.
Q7. How does Hibernate handle composite keys?
Through @IdClass
or @EmbeddedId
annotations.
Q8. How is Hibernate 6 different from Hibernate 5?
Hibernate 6 adopts Jakarta namespaces and has a new query API.
Q9. Is Hibernate suitable for microservices?
Yes, but consider lighter alternatives if your services require high independence.
Q10. When should I not use Hibernate?
For ultra-high performance cases with complex, hand-optimized SQL needs.