What is @EntityGraph in Spring Data JPA, and what problem does it solve?
answer
- N+1 = 1 parents query + N children queries
- attributePaths -> LEFT JOIN in one query
- per-query eager, mapping stays LAZY
- query hint jakarta.persistence.fetchgraph
- opt-in instead of global EAGER
basics
~20 s@EntityGraph is an annotation you put on a repository method to tell JPA which associations to load eagerly in one query. It fixes the N+1 select problem where each parent triggers an extra query for its children.
solid answer
~40 s@EntityGraph is a Spring Data JPA annotation placed on a repository query method. Its attributePaths list the associations that should be fetched eagerly for that specific query, translated by Hibernate into a JOIN so the parent and its associations come back in one round trip. Its main purpose is defeating the N+1 select problem: with LAZY associations, loading N parents and then touching each parent's collection fires 1 query for the parents plus N more for the children. By declaring @EntityGraph(attributePaths = "orders"), you get the parents and their orders in a single query. It is per-query and non-invasive: you keep associations LAZY by default in the mapping and opt into eager fetching only on the methods that need it, instead of forcing FetchType.EAGER globally.
code
java · 7 linespublic interface AuthorRepository extends JpaRepository<Author, Long> {
// Without this, iterating authors then a.getBooks() = N+1 queries.
// With it, authors + their books load in a single SQL join.
@EntityGraph(attributePaths = "books")
List<Author> findAll();
}go deeper
Know the definition: annotation on a repo method, attributePaths, and that it fixes N+1 by joining in one query.
Explain how it maps to a query hint and a LEFT JOIN, and why it beats global EAGER.
Contrast per-query graphs against mapping-level EAGER and the maintainability win of opt-in fetch plans.
Frame it as one tool among JOIN FETCH, @BatchSize, and projections for controlling fetch strategy across a codebase.
### The N+1 problem In JPA, associations like `@OneToMany` are `LAZY` by default (and `@ManyToOne` is `EAGER` by default, though best practice is to make everything `LAZY`). Lazy means the associated data is not loaded until you actually access it. Consider loading 10 `Author` entities and then, in a loop, reading each author's `books` collection. Hibernate runs **1** query to fetch the authors, then **1 more query per author** (10) to fetch each books collection = **N+1 = 11 queries**. This is the classic N+1 select problem: it silently balloons round trips and destroys performance under load. ### What @EntityGraph does `@EntityGraph` is a Spring Data JPA annotation you place on a repository method. Its `attributePaths` element names the associations that should be fetched **eagerly for that one query**. Under the hood Spring builds a JPA `EntityGraph` object and passes it as a query hint (`jakarta.persistence.fetchgraph`) to the `EntityManager`; Hibernate then plans the SQL with a `LEFT JOIN` (or `LEFT OUTER JOIN`) to the named associations so everything arrives in **one** SQL statement. The N+1 collapses to 1. ```java public interface AuthorRepository extends JpaRepository<Author, Long> { @EntityGraph(attributePaths = "books") List<Author> findAll(); } ``` ### Why not just make it EAGER in the mapping? Setting `@OneToMany(fetch = FetchType.EAGER)` forces the join on **every** query for that entity, even queries that never touch the collection — wasteful, and it can cause cartesian-product explosions and paging problems. `@EntityGraph` keeps the default `LAZY` and lets each **read path** declare exactly what it needs. It is a query-scoped fetch plan, not a global mapping change. ### Key terms - **Association**: a mapped relationship between entities (`@OneToMany`, `@ManyToOne`, `@ManyToMany`, `@OneToOne`). - **LAZY vs EAGER**: whether the association is loaded on demand (LAZY) or immediately (EAGER). - **N+1**: one query for the roots plus one extra per root for a lazy association. - **attributePaths**: the property names (dot-nested for depth) to fetch eagerly. ### When to use Use `@EntityGraph` on the specific read methods where you know you will use the association, so you avoid both N+1 and over-fetching on the paths that don't need it.
- Does @EntityGraph change the entity mapping's fetch type permanently?No. It only affects the query it annotates. The association stays LAZY (or whatever its mapping says) for every other query.
- How would you detect an N+1 problem in the first place?Enable SQL logging (Hibernate `show_sql` / statistics) or a tool like Hypersistence datasource-proxy / p6spy and watch for a burst of near-identical SELECTs per row. Integration tests can assert query counts.