# Lazy and Eager Loading — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/f-lazy

> Choose fetch types, understand proxies and avoid LazyInitializationException.

## Loading what you need, when you need it

**Fetch type** controls when related data is loaded. **Lazy** loading returns a **proxy** (for single-valued associations) or an uninitialised collection, and loads the data only when it is first accessed. **Eager** loading loads it immediately with the owner. The JPA defaults are a trap: **`@ManyToOne` and `@OneToOne` are EAGER by default**, while `@OneToMany` and `@ManyToMany` are LAZY. Eager associations cannot be made lazy per query, cause extra joins or queries everywhere and cascade into large object graphs, so the widely recommended practice is to make **every association LAZY** and fetch what each use case needs explicitly. Accessing a lazy association **after the persistence context has closed** (for example in a controller after the service transaction ended) throws **`LazyInitializationException`**. Fix it by fetching the data inside the transaction (join fetch, entity graphs, DTO projections), not by switching to eager loading or relying on **Open Session in View**, which keeps the persistence context open through view rendering, hiding the problem while issuing unplanned queries from the web layer.

## Proxies loaded on demand

A lazy association starts as a placeholder and is loaded when first accessed inside the persistence context.

![A solid entity box linked to a hollow dashed box; an arrow from the hollow box to a database cylinder fills it in.](assets/figures/hibernate-jpa/section-5-map.svg) — Figure 5.1 — A lazy proxy initialised on first access.

## Lazy everywhere, fetched explicitly

Override the eager default and load associations deliberately.

```java
@Entity
public class OrderLine {
    @Id @GeneratedValue private Long id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)     // override EAGER default
    private PurchaseOrder order;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    private Product product;
}

@Service
public class OrderQueryService {
    @Transactional(readOnly = true)
    public OrderView load(Long id) {
        PurchaseOrder o = em.createQuery(
                "select o from PurchaseOrder o join fetch o.customer where o.id = :id",
                PurchaseOrder.class)
            .setParameter("id", id)
            .getSingleResult();
        return OrderView.from(o);              // map to a DTO inside the transaction
    }
}

// controller, outside the transaction:
// order.getLines().size()   -> LazyInitializationException if lines were not fetched
```

## Disable open-in-view

Set `spring.jpa.open-in-view=false`. Lazy-loading errors then appear in development, pointing to exactly the use cases that need explicit fetching, instead of turning into hidden extra queries in production.

**Quiz:** What is the default fetch type of @ManyToOne in JPA?

- [x] EAGER
- [ ] LAZY
- [ ] NONE
- [ ] It depends on the database

*Answer:* EAGER. @ManyToOne and @OneToOne default to EAGER; best practice is to set them to LAZY explicitly.
