Hibernate’s N+1 problem occurs when an operation runs a query for its root results, then issues additional, similar SELECT statements as it loads related records. Find it by inspecting SQL for the entire operation—not just the initial query—and resolve it by choosing a fetch plan that matches the data the use case actually needs.
What an N+1 SELECT problem looks like
Suppose a query loads a list of orders, then application code accesses each order’s customer. Hibernate may run the root query and follow it with a separate SELECT for each customer. With N root results, that can mean one initial query plus as many as N secondary queries for that association. The exact count depends on the mapping, query, and what the code accesses.
Lazy association traversal is one common cause. N+1 can also occur with an EAGER association: if a JPQL query does not fetch that association, Hibernate may issue a secondary select for each result to satisfy the eager requirement before returning the results. Hibernate documents this behavior in its ORM 7.0 user guide and ORM 7.1 introduction. It is a data-access design problem to diagnose, not by itself evidence that Hibernate is malfunctioning.
How to identify N+1 in your application
- Reproduce the real operation. Run the slow endpoint, service method, or batch with representative data. A tiny fixture may not reveal the cost of repeated queries.
- Inspect SQL for the whole operation. Include the work after the root query returns, such as mapping entities into a response or traversing associations. The initial query alone cannot show secondary loads triggered later.
- Look for repeated query shapes. A common signal is one root SELECT followed by many structurally similar SELECTs keyed by individual IDs or foreign keys. Correlate those statements with the associations accessed on the code path.
- Record the context. Note the root query, repeated SQL, association path, result or page size, and the number of to-many paths involved. These details help distinguish an N+1 from an intentional set of queries and inform the choice of remedy.
There is no universal query-count threshold or single diagnostic tool established for every application. Validate the SQL generated by your deployed Hibernate version, database, mappings, and actual data shape.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems#1 Best Overall
Choose a fetch plan for the use case
Start with the data the operation needs, then decide how to load it. Hibernate’s current stable guide recommends LAZY associations with eager fetching selected per query; an EAGER mapping can cause secondary selects when a JPQL query omits the association. Avoid changing many mappings to EAGER just to silence one N+1: unrelated operations may then load data they do not need or trigger additional queries. See the current Hibernate ORM user guide.
Fetch join when the query needs the association
A JPQL or HQL fetch join can load an association with the root query. For example:
select o from Order o left join fetch o.customer
Use left join fetch when orders without a customer must remain in the result. An inner join fetch excludes roots without a matching association. A fetch join is often a good fit for a needed to-one association or a single to-many path, but fewer statements do not automatically mean less work: joined rows can multiply.
Hibernate ORM 7.2 warns that fetching multiple collections or other to-many paths in parallel can create a database Cartesian product and poor performance. It also advises against fetch joins with limited or paged queries and with scrolling or streaming. Check the ORM 7.2 query language guide and verify behavior for your version.
Rank #3
Use an entity graph for a dynamic fetch plan
Entity graphs let a query or operation specify the associations to load without encoding every use case into static mappings. Hibernate distinguishes fetch-graph and load-graph behavior, and its stable user guide describes graphs as a load plan. The relevant APIs and hint names depend on your Hibernate and Jakarta Persistence versions; do not copy an older example using legacy javax.persistence names into a newer stack without checking compatibility.
Consider batch or subselect fetching for lazy associations
Batch fetching loads several associated records in a secondary query using a group of keys. Subselect fetching can load associations for owners returned by an earlier query. Both can reduce repeated round trips while preserving lazy access, and may be useful when joining multiple collections would create an unwieldy result set.
Rank #4
These strategies mitigate some N+1 patterns, but they are not a universal replacement for a deliberate fetch plan. Hibernate’s ORM 7.1 introduction explicitly cautions that batch fetching may mitigate N+1 problems without solving them in general. Choose a batch size for the application’s data and workload; the documentation cited here does not establish one universally optimal value.
Use a DTO or projection for a narrow read
If a response needs only selected fields rather than managed entities and their associations, a DTO or projection query may return the required read model without loading a large object graph. Hibernate ORM 6.1 documentation describes DTO projection or a fetch join as often preferable to relying on @BatchSize when one query can retrieve the needed data. Compare selected columns and returned rows as well as statement count, and favor the shape that best fits the caller. See the ORM 6.1 user guide.
Compare the trade-offs before changing code
| Approach | Best fit | Watch for |
|---|---|---|
| Fetch join | Association needed immediately, especially a to-one or one to-many path | Row multiplication with parallel to-many paths; limitations with pagination, limits, scrolling, and streaming |
| Entity graph | A fetch plan that varies by use case | Version-specific API and fetch/load graph semantics |
| Batch fetching | Lazy associations accessed across a group of owners | Mitigates repeated loads but does not necessarily remove the underlying N+1 pattern; size must be validated |
| Subselect fetching | Associations of owners returned by an earlier query | May be preferable to a large join in some cases, but query shape and workload still matter |
| DTO or projection | A read operation needing only selected fields | Ensure the projection remains maintainable and returns the right columns and row shape |
Assess each option on statement count, rows and bytes returned, association cardinality, pagination needs, and the data the caller uses. The strategy that minimizes SQL statements may still be slower if it transfers a greatly expanded result.
Verify the fix against the deployed stack
After changing a fetch plan, rerun the same operation with representative data and inspect the full SQL sequence again. Confirm both that repeated per-root selects have been removed or bounded as intended and that the replacement query has not created excessive duplicate rows or fetched unrelated data. Hibernate guidance spans multiple ORM releases, so confirm generated SQL and API behavior on the version and database your application actually runs.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

