N+1 Query Problem in Spring Data JPA
Fast recall
Quick revision
Core answer
HHH90003004); `@EntityGraph` declares the same eager fetch per-method and is Spring Data's idiomatic alternative; `@BatchSize` on the association reduces N+1 to N/batchSize + 1 using WHERE id IN (?,?,...) and works with pagination. Diagnose with spring.jpa.show-sql=true, assert query counts in integration tests via Hibernate Statistics, and prefer DTO projections for read-heavy endpoints.Whether the candidate can identify a common ORM performance pitfall and knows multiple remediation strategies
Mentioning only `JOIN FETCH` without knowing `@EntityGraph` or `@BatchSize`, or switching everything to EAGER loading which causes over-fetching
Mention SQL logging to diagnose the problem, compare `JOIN FETCH` vs `@EntityGraph` trade-offs, and bring up the Hibernate Statistics API for production monitoring
Interview-ready guide
Interview answer
N+1 is the most common JPA performance bug. Loading a collection of entities and then accessing a lazy association on each — one query for the parents, one query per parent for the association.
The classic example:
List— 1 query, 100 ordersorders = orderRepo.findAll() orders.forEach(o -> o.getItems().size())— 100 additional queries- Total: 101 SQL queries to do what one JOIN could accomplish
Fix 1 — `JOIN FETCH` in JPQL:
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :status")- One SQL with a JOIN
- Limitation: cannot combine
JOIN FETCHwithPageableon a collection — in-memory pagination warning
Fix 2 — `@EntityGraph`:
@EntityGraph(attributePaths = {"items"})on the repository method- Spring translates to LEFT JOIN FETCH; same caveat with collection pagination
Fix 3 — `@BatchSize`:
@BatchSize(size = 25)on the collection field- Hibernate batches lazy loads: instead of 100 queries, 4
WHERE id IN (...)queries - Easiest to add, fully compatible with pagination
Fix 4 — DTO projection:
SELECT new com.x.OrderDto(...)returns plain objects- No entities, no proxies, no possibility of N+1
N+1 Query Problem in Spring Data JPA
Direct Answer
N+1 happens when JPA fires 1 query to load N parent entities, then fires N more queries to load each parent's lazy association — loading 100 orders and touching each order's customer triggers 101 SQL statements instead of 1 or 2. Three fixes: JOIN FETCH in JPQL eagerly joins the association in the same query but breaks pagination with collections (Hibernate paginates in memory, logs HHH90003004); @EntityGraph declares the same eager fetch per-method and is Spring Data's idiomatic alternative; @BatchSize on the association reduces N+1 to N/batchSize + 1 using WHERE id IN (?,?,...) and works with pagination. Diagnose with spring.jpa.show-sql=true, assert query counts in integration tests via Hibernate Statistics, and prefer DTO projections for read-heavy endpoints.
Common Mistakes & Pitfalls
Mentioning only JOIN FETCH without knowing @EntityGraph or @BatchSize, or switching everything to EAGER loading which causes over-fetching
Check your understanding
Answer these without looking back at the article.
- What is the Cartesian product problem with multiple JOIN FETCHes and how do you handle it?
- How does `@BatchSize` differ from `JOIN FETCH` in terms of the SQL generated?
- Can you still paginate queries that use `JOIN FETCH`?
- How would you write an integration test that fails when N+1 is introduced?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.