hibernate/hibernate-orm · error · LazyInitializationException
Cannot lazily initialize collection
Error message
Cannot lazily initialize collection
What it means
A lazy persistent collection stores its role and key; on first access it asks its owning session to load the data. throwLazyInitializationException fires when initialization is impossible because the session is closed or the collection is detached, producing the classic 'Cannot lazily initialize collection of role X with key Y' error. The message names the exact association that was touched outside a live session.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/collection/spi/AbstractPersistentCollection.java:667
private void throwLazyInitializationExceptionIfNotConnected() {
if ( !isConnectedToSession() ) {
throwLazyInitializationException( "no session or session was closed" );
}
if ( !session.isConnected() ) {
throwLazyInitializationException( "session is disconnected" );
}
}
private void throwLazyInitializationException(String message) {
final var error = new StringBuilder( "Cannot lazily initialize collection" );
if ( role != null ) {
error.append( " of role '" ).append( role ).append( "'" );
}
if ( key != null ) {
error.append( " with key '" ).append( key ).append( "'" );
}
error.append( " (" ).append( message ).append( ")" );
throw new LazyInitializationException( error.toString() );
}
public static void checkPersister(PersistentCollection<?> collection, CollectionPersister persister) {
if ( !collection.wasInitialized() && persister == null ) {
throw new LazyInitializationException( "Cannot lazily initialize collection"
+ " (collection is being removed)" );
}
}
protected final void setInitialized() {
this.initializing = false;
this.initialized = true;
}
@Override
public boolean isInitializing() {
return initializing;
}View on GitHub (pinned to fad1729dce)
Solutions
- Initialize the collection inside the session: Hibernate.initialize(parent.getChildren()) or a touch like size() within the transaction
- Fetch what you need in the query: JOIN FETCH, @EntityGraph, or @Fetch(JOIN) for that read path
- Project to DTOs inside the service layer instead of returning entities
- Widen the @Transactional boundary so the whole read happens in one open session
Example fix
// before
@Transactional(readOnly = true)
public Order getOrder(Long id) { return repo.findById(id).orElseThrow(); }
// caller after commit: order.getLines().size(); -> LazyInitializationException
// after
@Transactional(readOnly = true)
public Order getOrder(Long id) {
return em.createQuery(
"select o from Order o join fetch o.lines where o.id = :id", Order.class)
.setParameter("id", id).getSingleResult();
} Defensive patterns
Strategy: validation
Validate before calling
if (order.getLines() instanceof PersistentCollection pc
&& !pc.wasInitialized()
&& !session.isOpen()) {
throw new IllegalStateException(
"order.lines is lazy and the session is closed - initialize inside the transaction");
} Type guard
static boolean isSafeToAccess(Collection<?> c, SharedSessionContract session) {
return !(c instanceof PersistentCollection pc)
|| pc.wasInitialized()
|| (session != null && session.isOpen());
} Try / catch
try {
return order.getLines().size();
} catch (LazyInitializationException e) {
// recover by reloading the owner in a fresh session
try (Session s = sessionFactory.openSession()) {
return s.find(Order.class, order.getId()).getLines().size();
}
} Prevention
- Initialize lazy associations inside the owning transaction (Hibernate.initialize or a touch)
- Use join fetch / @EntityGraph for read paths that need the data
- Return DTO projections from API layers instead of managed entities
- Make OSIV (open-in-view) decisions explicit and documented per project
When it happens
Trigger: Calling size(), iterator(), stream(), contains() or get() on a lazy @OneToMany collection after the owning session/EntityManager closed; serializing detached entities to JSON; touching the collection in another thread or after transaction end with OSIV disabled.
Common situations: Spring MVC controllers returning JPA entities with lazy relations after the service transaction ended; async jobs or executors receiving detached entities; unit tests reading collections outside transactional scope.
Related errors
- Illegal attempt to associate a collection with two open sess
- Session/EntityManager is closed
- Updating immutable entity that is not in session yet
- No provided connection
- Property '<propertyName>' may not be annotated '@BatchSize'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/974ebf8d3a5b3deb.
Report an issue: GitHub.