Hibernate
LazyInitializationException
Java
ORM
Exception Handling

org.hibernate.LazyInitializationException failed to lazily initialize a collection of role FQPropretyName, could not initialize proxy - no Session

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This Hibernate exception means a lazy association was accessed after the session that could load it was already gone. In practice, the entity survived, but the database-backed collection proxy no longer had an open persistence context behind it. The fix is not to “turn lazy loading off everywhere”; the fix is to load the needed data inside a transaction boundary that matches the use case.

Why the Exception Happens

Hibernate often represents associated collections as lazy proxies. The proxy delays the database query until code actually asks for the collection contents. That is useful because not every request needs every association.

The problem appears when code returns an entity from a service, closes the transaction, and only later touches the lazy collection.

java
Author author = authorRepository.findById(id).orElseThrow();
transactionEndsHere();
author.getBooks().size();

At that point Hibernate knows the books collection exists conceptually, but it no longer has an active session to fetch it from the database. That is exactly what the exception message is telling you.

Fix It by Fetching What You Need Inside the Transaction

The cleanest fix is usually to load the entity together with the association required by the caller. In JPA, that often means a JOIN FETCH query or an entity graph.

java
1public interface AuthorRepository extends JpaRepository<Author, Long> {
2    @Query("select a from Author a left join fetch a.books where a.id = :id")
3    Optional<Author> findByIdWithBooks(@Param("id") Long id);
4}

Then use that repository method inside a transactional service method.

java
1@Service
2public class AuthorService {
3    private final AuthorRepository authorRepository;
4
5    public AuthorService(AuthorRepository authorRepository) {
6        this.authorRepository = authorRepository;
7    }
8
9    @Transactional(readOnly = true)
10    public Author loadAuthorWithBooks(Long id) {
11        return authorRepository.findByIdWithBooks(id).orElseThrow();
12    }
13}

Now the service returns an entity whose books collection was already initialized while the session was still active.

DTOs Are Often Better Than Returning Entities

In many applications the real problem is that controllers, views, or serializers are reaching too far into persistence-managed entities. A more robust design is to map entities to DTOs inside the transaction and return only the fields the caller needs.

java
1public record AuthorDto(Long id, String name, List<String> bookTitles) {}
2
3@Service
4public class AuthorQueryService {
5    private final AuthorRepository authorRepository;
6
7    public AuthorQueryService(AuthorRepository authorRepository) {
8        this.authorRepository = authorRepository;
9    }
10
11    @Transactional(readOnly = true)
12    public AuthorDto getAuthorDto(Long id) {
13        Author author = authorRepository.findByIdWithBooks(id).orElseThrow();
14        List<String> titles = author.getBooks().stream()
15            .map(Book::getTitle)
16            .toList();
17        return new AuthorDto(author.getId(), author.getName(), titles);
18    }
19}

This avoids leaking lazy proxies outside the service layer and usually produces clearer API boundaries.

Hibernate.initialize Is a Tactical Tool, Not the Design

You can explicitly initialize a lazy association while the session is open.

java
1@Transactional(readOnly = true)
2public Author loadAuthor(Long id) {
3    Author author = authorRepository.findById(id).orElseThrow();
4    Hibernate.initialize(author.getBooks());
5    return author;
6}

This works, but it is more of a tactical fix than an architectural answer. If many associations are initialized ad hoc, the codebase often turns into a collection of hidden loading rules that are hard to reason about.

Avoid the Temptation to Switch Everything to Eager Fetching

Setting every association to eager fetching looks like a shortcut, but it often shifts the problem rather than solving it. You may eliminate one exception while creating larger queries, more joins, worse memory behavior, and accidental N+1 query patterns elsewhere.

Lazy loading is not the bug. Lazy loading used outside the correct transaction scope is the bug.

Open Session in View Has a Tradeoff

Some web applications keep the Hibernate session open for the whole request, often called Open Session in View. That can suppress LazyInitializationException because the view layer can still trigger lazy queries.

It is convenient, but it also hides data access in rendering code and makes query behavior harder to predict. For simple applications it may be acceptable. For systems where query control matters, explicit fetch plans and DTO mapping are usually better.

Common Pitfalls

  • Returning entities to layers that access lazy collections after the transaction has ended.
  • Solving the exception by changing every association to eager fetching.
  • Relying on JSON serialization to walk entity graphs safely after detachment.
  • Using Open Session in View without understanding that view rendering may now execute database queries.
  • Adding Hibernate.initialize everywhere instead of designing explicit fetch requirements.

Summary

  • 'LazyInitializationException means a lazy association was accessed without an active Hibernate session.'
  • The durable fix is to load the required data inside the transaction that owns the use case.
  • 'JOIN FETCH, entity graphs, and DTO mapping are the usual clean solutions.'
  • 'Hibernate.initialize can help, but it is a tactical tool rather than a general design strategy.'
  • Do not disable lazy loading globally just to silence one exception.

Course illustration
Course illustration

All Rights Reserved.