Clean • Professional
In JPA and Hibernate, fetch types determine when related entities are loaded from the database. Choosing the wrong fetch strategy is one of the most common causes of performance issues in Spring Boot applications—most notably the N+1 query problem.
Understanding fetch types and knowing how to solve the N+1 problem is critical for building scalable, high-performance Spring Data JPA applications, especially in real-world enterprise systems.
A fetch type defines when associated entities are retrieved from the database relative to the parent entity.
JPA provides two fetch strategies:

FetchType.EAGERFetchType.LAZYWith FetchType.EAGER, related entities are loaded immediately along with the parent entity, regardless of whether they are actually used.
@OneToOne(fetch = FetchType.EAGER)
private Profile profile;
Behavior
Default For
@OneToOne@ManyToOneAdvantages
LazyInitializationExceptionDisadvantages
With FetchType.LAZY, related entities are loaded only when they are accessed in the code.
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
Behavior
Default For
@OneToMany@ManyToManyAdvantages
Disadvantages
Can cause LazyInitializationException when:
In JPA, each relationship type has a default fetch strategy that determines when related data is loaded from the database.
@OneToOne → EAGER
The related entity is loaded immediately with the parent entity.
@ManyToOne → EAGER
The parent reference is fetched instantly, as it is usually required.
@OneToMany → LAZY
Child collections are loaded only when explicitly accessed, improving performance.
@ManyToMany → LAZY
Associated collections are fetched on demand to avoid heavy joins and large result sets.
FetchType.EAGER and FetchType.LAZY in JPA:| Feature | FetchType.EAGER | FetchType.LAZY |
|---|---|---|
| When data is loaded | Immediately along with the parent entity | Only when accessed in code (on-demand) |
| Default for | @OneToOne, @ManyToOne | @OneToMany, @ManyToMany |
| SQL behavior | May use JOIN or additional SELECT; loads all related entities upfront | Uses proxy objects; queries executed only when accessed |
| Memory usage | Higher, loads unnecessary data | Lower, loads only what’s needed |
| Performance | Can cause N+1 query problem and slower performance with large datasets | More efficient for large datasets; reduces unnecessary DB access |
| Risk of LazyInitializationException | No | Yes, if accessed outside transaction/session |
| Use case | Small datasets or always-required relationships | Large datasets or optional relationships |
| Control over queries | Limited | Full control |
The N+1 query problem is a common performance issue in JPA and Hibernate that occurs when related entities are fetched inefficiently.
How It Happens
Hibernate executes:

List<User> users = userRepository.findAll();
for (User user : users) {
System.out.println(user.getOrders().size());
}
If the orders relationship is LAZY-loaded, Hibernate triggers a separate query for each user when getOrders() is accessed.
SQL Executed
SELECT*FROM users;-- 1 query
SELECT*FROM ordersWHERE user_id=1;
SELECT*FROM ordersWHERE user_id=2;
SELECT*FROM ordersWHERE user_id=3;
...
Result:
If 1,000 users are loaded → 1,001 SQL queries are executed.
The N+1 issue causes:
Common causes include:
—> The N+1 problem is not a Hibernate bug—it’s a design issue.
Enable SQL Logging
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Warning Signs
🔍 Tools like Hibernate Statistics, Datasource Proxy, or APM tools can help detect it early.
Solution 1: Fetch Join (Best Performance)
@Query("SELECT u FROM User u JOIN FETCH u.orders")
List<User>findAllWithOrders();
Solution 2: EntityGraph (Clean & Flexible)
@EntityGraph(attributePaths = "orders")
List<User>findAll();
Solution 3: Batch Fetching
spring.jpa.properties.hibernate.default_batch_fetch_size=10
Solution 4: DTO Projection (Best for APIs)
@Query("SELECT new com.example.dto.UserDTO(u.id, u.name) FROM User u")
List<UserDTO>fetchUsers();
@OneToMany(fetch = FetchType.EAGER)
Why Avoid?
—> Never use EAGER blindly on collections
Fetch strategies in JPA and Hibernate play a crucial role in determining application performance, scalability, and database efficiency. Properly choosing between LAZY and EAGER loading, and addressing the N+1 problem using techniques like fetch joins, EntityGraphs, batch fetching, and DTO projections, ensures your Spring Data JPA applications remain fast, efficient, and production-ready.