In Hibernate (and Spring Data JPA), lazy loading is the recommended strategy for controlling when associated entities are fetched. This helps improve performance by avoiding unnecessary database access when relationships aren't needed.
This article explains how to ensure Hibernate does not automatically load associated entities when the main entity is fetched, and how to apply proper lazy loading strategies for different relationship types.
⚙️ Step-by-Step Guide to Lazy Loading in Hibernate
✅ 1. Use FetchType.LAZY
in Relationships
🔹 For @OneToMany
and @ManyToMany
(default: LAZY)
java
@Entity
public class MainEntity {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "mainEntity")
private Set<AssociatedEntity> associatedEntities = new HashSet<>();
}
🔹 For @ManyToOne
and @OneToOne
(default: EAGER — must override)
java
@Entity
public class MainEntity {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "associated_id")
private AssociatedEntity associatedEntity;
}
✅ 2. Enable Bytecode Enhancement for Advanced Lazy Loading
To lazily load basic fields or @OneToOne
relations, Hibernate bytecode enhancement is needed.
🔸 Maven Configuration:
xml
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<goals>
<goal>enhance</goal>
</goals>
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
</execution>
</executions>
</plugin>
🔸 Gradle Configuration:
groovy
plugins {
id 'org.hibernate.orm' version '6.0.0.Final'
}
hibernate {
enhance {
enableLazyInitialization = true
}
}
✅ 3. Allow Hibernate to Create Proxies
Hibernate uses proxies to lazily load entities. Ensure:
- Entity classes are not final
- They have non-final, no-arg constructors
Otherwise, Hibernate cannot generate proxy classes.
✅ 4. Avoid FETCH JOIN
in JPQL or HQL
Using JOIN FETCH
forces eager loading.
java
// EAGER loading (to be avoided for lazy)
String query = "SELECT m FROM MainEntity m JOIN FETCH m.associatedEntities";
// Use this instead for lazy
String query = "SELECT m FROM MainEntity m";
📌 Summary Table
StrategyPurposefetch = FetchType.LAZY
Prevents auto-loading of associationsBytecode enhancementEnables field-level and @OneToOne lazyAvoiding JOIN FETCH
in JPQLPrevents eager loading via queryAllow Hibernate proxiesSupports lazy-loading mechanisms
🧠 Bonus Tip: Handle LazyInitializationException
Lazy loading requires an active Hibernate session. If accessed outside a session (e.g., in a controller), you'll get a LazyInitializationException
. To handle this:
- Use
@Transactional
on service methods - Or explicitly fetch needed associations before returning from service layer
With these strategies, you gain full control over when and how associated entities are loaded, helping you optimize database interactions and application performance.