Using Named JPA Queries with Pagination in Hibernate – A Practical Guide
Learn how to define and execute paginated named queries in Hibernate. This article walks through the exact configuration, execution, and common pitfalls, so you can fetch large datasets efficiently and safely.
09 Nov 2025, 05:55 UTC

Why Use Named Queries with Pagination?
When you need to reuse a static JPQL or SQL statement across your application, named queries are the canonical solution. They decouple query text from business logic, keep the codebase tidy, and let the persistence provider optimize the statement once. Pagination is essential when the result set can grow large – it prevents the JVM from loading millions of entities into memory and reduces database load.
Defining a Paginated Named Query
Named queries are declared either via annotations on an entity or in an XML descriptor. The query text itself can contain placeholder parameters (e.g., :status) but the pagination limits are applied at runtime, not at definition time.
@Entity
@NamedQuery(
name = "Order.findByStatus",
query = "SELECT o FROM Order o WHERE o.status = :status"
)
public class Order { /* … */ }
Notice that the query does not include a LIMIT or OFFSET clause – Hibernate will inject those when you set setFirstResult and setMaxResults on the Query object.
Executing the Query with Pagination
The following snippet demonstrates how to run the named query, bind a parameter, and apply pagination. It is a drop‑in replacement for any existing EntityManager usage.
// Run on a thread with a Transactional context
EntityManager em = ...; // injected or obtained via factory
int pageNumber = 2; // zero‑based
int pageSize = 20;
Query q = em.createNamedQuery("Order.findByStatus", Order.class);
q.setParameter("status", OrderStatus.PENDING); // bind named parameter
q.setFirstResult(pageNumber * pageSize); // OFFSET
q.setMaxResults(pageSize); // LIMIT
List<Order> orders = q.getResultList();
Key points:
- Pagination is applied after the query is created, so you can reuse the same named query for different pages.
- Hibernate translates
setFirstResultandsetMaxResultsinto the appropriateLIMIT/OFFSETclause for the underlying database. - Always use
setMaxResults; omitting it will return the full result set, potentially exhausting heap memory.
Verifying the Generated SQL
To confirm that pagination is working, enable SQL logging in your persistence configuration. For example, in application.yml:
spring:
jpa:
show-sql: true
properties:
hibernate.format_sql: true
When you run the query, the console should show something like:
SELECT o.* FROM orders o WHERE o.status = 'PENDING' LIMIT 20 OFFSET 20
Check that the LIMIT and OFFSET values match pageSize and pageNumber * pageSize respectively.
Common Mistakes and How to Avoid Them
- Mixing named and positional parameters – Hibernate throws
IllegalArgumentExceptionif you use?1placeholders together with:name. Stick to one style per query. - Using
setFirstResultwith negative values – the method expects a non‑negative integer. Negative values will result in a runtime exception. - Relying on
setMaxResultsfor total count – it only limits the returned rows. To get the total number of items, run a separate count query or useCriteriaQuerywithcount(). - Ignoring lazy relationships – if the query selects entities with lazy collections, pagination may still trigger multiple SQL statements when accessing those collections. Use
JOIN FETCHorEntityGraphif you need eager loading.
Limitations of Pagination in Hibernate
While setFirstResult and setMaxResults are straightforward, they have a few caveats:
- For very large offsets (e.g., page 10,000), the database may still scan many rows before applying the offset. Some databases support
ROW_NUMBER()or keyset pagination as a more efficient alternative. - When using
ORDER BYon non‑indexed columns, the database must sort the entire result set before applying the limit, which can be slow. - Hibernate 6 introduced a new
ScrollableResultsAPI that can be more memory‑efficient for large streams, but it is not a drop‑in replacement forsetMaxResults.
Practical Checklist Before Deployment
- Confirm that the named query is defined correctly in the entity or XML.
- Verify that all parameters are bound and that you are not mixing named and positional placeholders.
- Run the query in a development environment with SQL logging enabled to ensure the generated
LIMIT/OFFSETclause matches expectations. - Measure memory usage for a page size you plan to ship in production; ensure that
pageSizedoes not exceed the available heap. - Test edge cases: page number 0, page number beyond the last page, and page size of 1.
- If you encounter performance issues on very large offsets, consider implementing keyset pagination or using
CriteriaQuerywith a scrollable result set.
Conclusion
Named queries with pagination provide a clean, reusable way to fetch large datasets in Hibernate. By applying setFirstResult and setMaxResults after binding named parameters, you keep your code DRY, avoid memory pitfalls, and let Hibernate generate efficient SQL for your database. Keep an eye on offset size, ordering columns, and lazy relationships to ensure optimal performance in production.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.