Lazy vs eager fetching

Hibernate · lesson 8 of 32 · 4 min read

Control when Hibernate goes back to the database to load an association.

Open this lesson in the learning hub

Key points

  • Lazy gives you a proxy now and a SELECT on first use. Eager loads the association immediately, every time.
  • The defaults are inconsistent: @ManyToOne and @OneToOne are EAGER, collections are LAZY. Override the singles to LAZY.
  • Eager is a promise you cannot take back: every query that loads the entity drags the association along, even when nobody reads it.
  • Touching a lazy association after the transaction ends throws LazyInitializationException. Load what the caller needs while the context is open.
  • Spring Boot leaves spring.jpa.open-in-view on, hiding that error by holding the context open during rendering. Turn it off.
  • Lazy on the inverse side of a @OneToOne needs bytecode enhancement - Hibernate must query to learn whether the row exists.

Example

@Entity
public class Book {

    @ManyToOne(fetch = FetchType.LAZY)   // default is EAGER - always override
    @JoinColumn(name = "author_id")
    private Author author;

    @OneToMany(mappedBy = "book")        // collections are LAZY already
    private List<Review> reviews = new ArrayList<>();
}

// Then fetch deliberately, per use case:
@Query("select b from Book b join fetch b.author where b.id = :id")
Optional<Book> findWithAuthor(@Param("id") Long id);

Make everything lazy, then fetch on purpose for each use case.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Hibernate course, and every lesson in it is listed on the Hibernate contents page.