Let's get started with a Microservice Architecture with Spring Cloud:
Eager/Lazy Loading in Hibernate
Last updated: December 14, 2024
1. Overview
When working with an ORM, data fetching/loading can be classified into two types: eager and lazy.
In this quick tutorial, we’re going to point out differences and show how we can use these in Hibernate.
2. Maven Dependencies
To use Hibernate, let’s first define the main dependency in our pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.6.3.Final</version>
</dependency>
We can find the latest version of Hibernate here.
3. Eager and Lazy Loading
The first thing that we should discuss here is what lazy loading and eager loading are:
- Eager Loading is a design pattern in which data initialization occurs on the spo
- Lazy Loading is a design pattern that we use to defer the initialization of an object as long as it’s possible.
Let’s see how this works.
First, we’ll look at the UserLazy class:
@Entity
@Table(name = "USER")
public class UserLazy implements Serializable {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long userId;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<OrderDetail> orderDetail = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
Next, we’ll see the OrderDetail class:
@Entity
@Table (name = "USER_ORDER")
public class OrderDetail implements Serializable {
@Id
@GeneratedValue
@Column(name="ORDER_ID")
private Long orderId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="USER_ID")
private UserLazy user;
// standard setters and getters
// also override equals and hashcode
}
One User can have multiple OrderDetails. In the eager loading strategy, if we load the User data, it will also load up all orders associated with it and will store it in memory.
However, when we enable lazy loading, Hibernate won’t initialize and load OrderDetail data into memory when we pull up a UserLazy, until we make an explicit call to it.
In the next section, we’ll see how we implement the example in Hibernate.
4. Loading Configuration
Let’s look at how to configure fetching strategies in Hibernate.
We can enable Lazy Loading by using this annotation parameter:
fetch = FetchType.LAZY
For Eager Fetching, we use this parameter:
fetch = FetchType.EAGER
To set up Eager Loading, we have used UserLazy‘s twin class called UserEager.
In the next section, we will look at the differences between the two types of fetching.
5. Differences
A Persistence provider differentiates the two types of fetching according to when it loads the data into memory.
Let’s have a look:
List<UserLazy> users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());
When we use the lazy initialization approach, it initializes orderDetailSet only when we explicitly call it, using a getter or some other method:
UserLazy userLazyLoaded = users.get(3);
But when we use the eager approach in UserEager, it initializes orderDetailSet immediately:
List<UserEager> user = sessionEager.createQuery("From UserEager").list();
For lazy loading, we use a proxy object and fire a separate SQL query to load the orderDetailSet.
The idea of disabling proxies or lazy loading is considered a bad practice in Hibernate. It can result in fetching and storing a lot of data, irrespective of the need for it.
We can use the following method to test the functionality:
Hibernate.isInitialized(orderDetailSet);
Now let’s have a look at the queries generated in either case:
<property name="show_sql">true</property>
The above setting in the fetching.hbm.xml shows the generated SQL queries. If we look at a console output, we can see generated queries.
For Lazy Loading, here’s the query generated to load the User data:
SELECT user0_.USER_ID as USER_ID1_0_, ...
FROM USER user0_
However, in eager loading, we saw a JOIN made with USER_ORDER:
SELECT orderdetai0_.USER_ID as USER_ID4_0_0_, orderdetai0_.ORDER_ID as ORDER_ID1_1_0_, orderdetai0_ ...
FROM USER_ORDER orderdetai0_
WHERE orderdetai0_.USER_ID=?
The above query is generated for all Users, which results in much more memory use than in the other approach.
6. Advantages and Disadvantages
Let’s discuss the factors determining the choice of each of the loading strategies.
6.1. Lazy Loading
The Lazy Loading strategy has its advantages and disadvantages:
| Advantages | Disadvantages |
|---|---|
| Much smaller initial load time than the other approach | Deferred initialization of related entities might introduce a delay and thus impact performance |
| Less memory consumption than the other approach | When a lazily fetched association of an entity is accessed before being initialized, it may cause the LazyInitializationException exception |
| Avoids secondary selects and the related N+1 query issue |
6.2. Eager Loading
The Eager Loading strategy has its advantages and disadvantages:
| Advantages | Disadvantages |
|---|---|
| Avoids delay and the related performance impacts due to deferred initialization | Long initial loading time |
| Always used for @ManyToOne and @OneToOne associations annotated with @NotFound, which causes Hibernate to assume that there is no physical foreign key | Loading too much unnecessary data might impact performance |
| When using the EntityGraphs feature all attributes in entity graphs use the EAGER fetch type | May require secondary selects and the related N+1 query issue when EAGER associations aren’t JOIN FETCHed |
7. JPA Default FetchType
As we’ve seen earlier, the fetch attribute can be either FetchType.LAZY or FetchType.EAGER. Jakarta Persistence specification defines that by default, @OneToMany and @ManyToMany associations use the FetchType.LAZY strategy, while the @OneToOne and @ManyToOne use the FetchType.EAGER strategy instead.
Hibernate applies these defaults. However, Hibernate recommends that we statically mark all associations as lazy and use dynamic fetching strategies for eagerness when needed. An example entity includes overriding the default fetch type:
@Entity(name = "Employee")
public class Employee {
@Id private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
@ManyToMany(mappedBy = "employees")
private List projects = new ArrayList<>();
@OneToOne(fetch = FetchType.LAZY)
private Phone phone;
// Getters and setters
}
8. Lazy Loading in Hibernate
Hibernate applies a lazy loading approach on entities and associations by providing a proxy implementation of classes.
Hibernate intercepts calls to an entity by substituting it with a proxy derived from an entity’s class. In our example, missing requested information will be loaded from a database before control is ceded to the User class implementation.
We should also note that when the association is represented as a collection class (in the above examples, it is represented as Set<OrderDetail> orderDetailSet), a wrapper is created and substituted for an original collection.
To know more about proxy design patterns, refer here.
9. Conclusion
In this article, we showed examples of the two main types of fetching used in Hibernate.
For advanced expertise, check the official website of Hibernate.
The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.

















