Explain the difference between EntityGraphType.FETCH and EntityGraphType.LOAD.
answer
- FETCH default, unlisted -> LAZY
- LOAD additive, unlisted -> mapping default
- fetchgraph vs loadgraph hints
- FETCH can suppress mapping EAGER
- provider may over-fetch; not a security boundary
basics
~10 sFETCH (the default) makes listed attributes EAGER and treats everything not listed as LAZY. LOAD makes listed attributes EAGER but leaves unlisted attributes with their mapped fetch type. FETCH gives a stricter, fully-specified plan.
solid answer
~50 sBoth types make the attributes named in the graph EAGER; the difference is what happens to attributes **not** in the graph. With `EntityGraphType.FETCH` — the default for Spring's `@EntityGraph` — the graph is treated as a **complete** specification: anything not listed is treated as `LAZY`, overriding its mapped fetch type. With `EntityGraphType.LOAD`, the graph is **additive**: listed attributes become EAGER, but unlisted attributes keep whatever fetch type their mapping declares (so a mapping-level `EAGER` @ManyToOne still loads). In JPA terms these are the `jakarta.persistence.fetchgraph` and `jakarta.persistence.loadgraph` hints. Practically, FETCH gives you a precise, minimal load and is what you usually want; LOAD is useful when you want to add associations on top of the entity's default eager set without suppressing the others. In practice Hibernate's honoring of the 'suppress to LAZY' semantics has historically been imperfect, so treat FETCH as intent rather than a hard guarantee for statically-EAGER paths.
code
java · 10 linespublic interface OrderRepository extends JpaRepository<Order, Long> {
// FETCH (default): items eager; every unlisted association treated LAZY
@EntityGraph(attributePaths = "items")
List<Order> findByStatus(Status status);
// LOAD: items eager PLUS any statically-EAGER association (e.g. customer)
@EntityGraph(attributePaths = "items", type = EntityGraph.EntityGraphType.LOAD)
List<Order> findByCustomerId(Long customerId);
}go deeper
Know the default is FETCH and that both make listed paths eager.
Explain the treatment of unlisted attributes: LAZY (FETCH) vs mapping default (LOAD).
Tie it to fetchgraph/loadgraph hints and the LazyInitializationException surprise from FETCH suppressing EAGER.
Note provider leeway (Hibernate may over-fetch), so don't treat FETCH as a data-hiding/security mechanism.
### The two graph types JPA defines two ways to interpret an entity graph, exposed by Spring as `@EntityGraph(type = ...)`: - **`EntityGraphType.FETCH`** (Spring's default) — a **fetch graph**. Attributes present in the graph are `EAGER`. Attributes **absent** from the graph are treated as `LAZY`, **regardless** of the fetch type declared in their mapping. It is a *complete* description of what to load. - **`EntityGraphType.LOAD`** — a **load graph**. Attributes present in the graph are `EAGER`. Attributes **absent** from the graph fall back to their **statically mapped** fetch type. It is an *additive* description layered on top of the entity's defaults. These correspond to the standard JPA query hints `jakarta.persistence.fetchgraph` and `jakarta.persistence.loadgraph`. ### Worked example Suppose `Order` maps `customer` as `@ManyToOne(fetch = EAGER)` and `items` as `@OneToMany(fetch = LAZY)`. ```java @EntityGraph(attributePaths = "items", type = EntityGraphType.FETCH) List<Order> findAll(); // items EAGER; customer treated as LAZY (not listed) @EntityGraph(attributePaths = "items", type = EntityGraphType.LOAD) List<Order> findAll(); // items EAGER; customer stays EAGER (its mapping) ``` With FETCH, only `items` is eagerly joined; `customer` is suppressed to lazy. With LOAD, both `items` (from the graph) and `customer` (from its EAGER mapping) are eager. ### Why it matters - **FETCH** minimizes the load — you get exactly what you asked for and nothing else, which is great for building tight, predictable queries and DTOs. - **LOAD** is convenient when the entity's default eager associations are genuinely always needed and you only want to *add* a few more for a particular query. ### Gotchas and caveats - **Default is FETCH**: if you forget `type`, Spring uses FETCH, which can *suppress* a mapping-level EAGER association you assumed would still load. If a downstream `LazyInitializationException` surprises you, this is a suspect. - **Provider behavior**: the JPA spec says a persistence provider *may* still fetch additional state beyond the graph; historically Hibernate did not always strictly honor 'suppress unlisted EAGER to LAZY' for `fetchgraph`. Don't rely on FETCH to *hide* data as a security boundary — it's a performance/fetch-planning hint. - **Basic (scalar) attributes**: entity graphs can also control eager/lazy loading of basic attributes marked `@Basic(fetch = LAZY)` (which itself needs bytecode enhancement). For most apps the interesting effect is on associations. - **LAZY-suppression still needs a session**: FETCH suppressing an association to lazy means touching it later still requires an open persistence context or you'll get `LazyInitializationException`. ### When to use which Reach for the **default FETCH** for precise, DTO-style reads. Choose **LOAD** only when you deliberately want the entity's baseline eager set plus a couple of extra associations, and you understand the mapping's static fetch types.
- Which JPA query hints do FETCH and LOAD correspond to?jakarta.persistence.fetchgraph and jakarta.persistence.loadgraph respectively.
- You used the default FETCH graph and later got a LazyInitializationException on customer even though it's mapped EAGER. Why?A fetch graph treats unlisted attributes as LAZY, so it suppressed the mapping-level EAGER on customer. Add customer to attributePaths or switch to type = LOAD.
saying these in an interview costs you the question
- Claiming FETCH and LOAD differ in how they treat the LISTED attributes (both make them eager)
- Assuming LOAD forces everything eager
- Treating a fetch graph as a hard guarantee that unlisted data won't be loaded