Fixing LazyInitializationException in Spring Boot: The Right Way
Writing
JAVA ENGINEERING
January 13, 20264 min read

Fixing LazyInitializationException in Spring Boot: The Right Way

The #1 error in JPA explained. Why open-in-view is bad, and how to use JOIN FETCH, @EntityGraph, and DTOs to fix it permanently.

javaspring-boothibernatejpaperformance

It’s 5:00 PM on a Friday. You deploy your code. You hit the API. 500 Internal Server Error.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.User.roles, could not initialize proxy - no Session

Every Java developer faces this.

Most people Google it, find a StackOverflow answer saying "Just set spring.jpa.open-in-view=true" or "change @OneToMany to FetchType.EAGER", and move on.

Please don't do that. You are solving the error but creating a massive performance bottleneck.

Lazy Initialization Error Timeline

Why does this happen?

Hibernate uses Proxies. When you load a User, it doesn't load their Roles list from the database immediately (that's Lazy loading). Instead, it puts a placeholder (Proxy) there.

  1. Service Layer (Transaction Open): You fetch the User. The Session is alive.
  2. Controller Layer (Transaction Closed): The transaction finishes. The Hibernate Session closes. The data is "Detached".
  3. JSON Serialization: Jackson tries to turn your User into JSON. It calls user.getRoles().
  4. Boom: The Proxy tries to call the database to load the roles, but the connection is gone. LazyInitializationException.

Why is enable_lazy_load_no_trans a dangerous workaround?

There is a Hibernate property called hibernate.enable_lazy_load_no_trans=true. Do not use this.

It tells Hibernate: "If the session is closed, just open a quick temporary new database connection to fetch this one field." If you are serializing a list of 100 users, this will open 100 tiny database connections. This is the N+1 Select Problem, and it will kill your database.

How does JOIN FETCH solve LazyInitializationException?

The most robust solution is to tell Hibernate to fetch the data eagerly for this specific query, using JOIN FETCH.

public interface UserRepository extends JpaRepository<User, Long> {
    
    // ❌ Standard findById (Lazy)
    // Optional<User> findById(Long id);
 
    // ✅ Eager Fetch (Good)
    @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
    Optional<User> findByIdWithRoles(@Param("id") Long id);
}

This generates one single SQL query that gets both User and Roles. No proxies. No errors.

Join Fetch Solution

When should you use @EntityGraph instead of JOIN FETCH?

If you don't like writing JPQL strings, Spring Data JPA provides @EntityGraph.

public interface UserRepository extends JpaRepository<User, Long> {
 
    @EntityGraph(attributePaths = {"roles", "address"})
    Optional<User> findById(Long id);
}

This does the exact same thing as JOIN FETCH but purely with annotations. You can even define named EntityGraphs on your entity class to reuse them.

Why are DTO projections the best solution for lazy loading?

Sometimes, you don't even need the Entity. If you are just building an API response, fetch a DTO (Data Transfer Object) directly.

Hibernate is smart enough to select only the columns you need.

public interface UserRepository extends JpaRepository<User, Long> {
 
    // Define a simple Java Record
    record UserSummary(String username, String roleName) {}
 
    @Query("SELECT new com.example.dto.UserSummary(u.username, r.name) " +
           "FROM User u JOIN u.roles r WHERE u.id = :id")
    List<UserSummary> findUserSummary(@Param("id") Long id);
}

Why this wins:

  1. Zero Lazy Loading issues: It's just a POJO/Record, not a Hibernate entity.
  2. Performance: You only select the 2 columns you need, instead of SELECT *.
  3. Safety: You can't accidentally trigger a LazyInitException because there are no proxies.

Summary

LazyInitializationException is not a bug; it's a feature protecting you from loading your entire database into memory.

  • Avoid OpenEntityManagerInView (it keeps DB connections open too long).
  • Use JOIN FETCH or @EntityGraph when you need the Entities.
  • Use DTO Projections for read-only API responses.

Fix it at the query level, not the config level.

For further reading, consult the Hibernate ORM User Guide on Fetching, the Spring Data JPA Reference on EntityGraphs, and the JPA 3.1 Specification.

Keep Reading

Happy Coding! 🚀

Frequently Asked Questions

What causes LazyInitializationException in Spring Boot?

LazyInitializationException occurs when you try to access a lazily-loaded JPA relationship (like @OneToMany or @ManyToMany) after the Hibernate Session has already been closed. This typically happens when you return an entity from a @Transactional service method and then try to access its lazy associations in the controller or view layer.

Is open-in-view a good fix for LazyInitializationException?

No. Setting spring.jpa.open-in-view=true keeps the Hibernate Session open for the entire HTTP request, which masks the problem but creates performance issues — it holds database connections longer than necessary and can cause N+1 query problems silently. Use JOIN FETCH, @EntityGraph, or DTO projections instead.

What is the best way to fix LazyInitializationException?

The recommended approaches are: (1) Use JOIN FETCH in JPQL queries to eagerly load the needed associations in a single query, (2) Use @EntityGraph annotations to declaratively specify which associations to fetch, or (3) Use DTO projections to select only the fields you need, avoiding lazy loading entirely.

What is the difference between FetchType.EAGER and JOIN FETCH?

FetchType.EAGER loads the association every time the entity is loaded, regardless of whether you need it — causing unnecessary queries and potential performance issues. JOIN FETCH is used in specific JPQL queries to load associations only when needed, giving you fine-grained control per use case.

Last updated: April 1, 2026

Rabinarayan Patra

Rabinarayan Patra

SDE II at Amazon. Previously at ThoughtClan Technologies building systems that processed 700M+ daily transactions. I write about Java, Spring Boot, microservices, and the things I figure out along the way. More about me →

X (Twitter)LinkedIn

Stay in the loop

Get the latest articles on system design, frontend and backend development, and emerging tech trends, straight to your inbox. No spam.