Overriding FetchType.EAGER with fetchgraph
Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to see how you can override the FetchType.EAGER strategy using the fetchgraph query hint.
While this feature has been available in the JPA specification since version 2.1, Hibernate has only supported this feature since version 5.5.
JPA FetchType.EAGER and FetchType.LAZY strategies
The fetching strategy can be specified either at the entity mapping level or when building a query.
At the entity mapping level, the fetch attribute of the @ManyToOne, @OneToOne, @OneToMany, @ManyToMany, or @ElementCollection can use either the FetchType.LAZY or FetchType.EAGER value.
The @ManyToOne and @OneToOne associations use the FetchType.EAGER strategy by default while the @OneToMany, @ManyToMany, and @ElementCollection associations use the FetchType.LAZY strategy.
As I explained in this article, it’s much more efficient if all associations are set to using the FetchType.LAZY strategy ate entity mapping level since we can always override it at query time using a JOIN FETCH directive.
However, while you can easily eagerly fetch an association that was set to FetchType.LAZY at the entity mapping level, the reverse was not possible until Hibernate implemented the fetchgraph property as indicated by the JPA specification.
The default behavior of FetchType.EAGER
Let’s assume we have the following Post and PostComment entities that are associated via the post reference in the PostComment child entity:

The parent Post entity is mapped as follows:
@Entity
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
}
And the PostComment child entity used the default @ManyToOne association mapping:
@Entity
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne
private Post post;
}
Because the @ManyToOne association uses the FetchType.EAGER strategy by default, every time we load the PostComment entity using the find method we can see that the post association is fetched as well:
PostComment comment = doInJPA(entityManager -> {
return entityManager.find(PostComment.class, 1L);
});
assertTrue(Hibernate.isInitialized(comment.getPost()));
assertEquals(
"High-Performance Java Persistence",
comment.getPost().getTitle()
);
When checking the query log, we can see that Hibernate executed the following SQL query:
SELECT pc.id, p.id, p.title, pc.review FROM post_comment pc LEFT JOIN post p ON p.id = pc.post_id WHERE pc.id = 1
Because of the default FetchType.EAGER strategy, Hibernate uses the LEFT JOIN to fetch the associated post table record.
However, if we don’t need the post association, then fetching it along with the PostComment is just a useless overhead.
If the find method uses a LEFT JOIN to fetch the associations that use the FetchType.EAGER strategy, an entity query will use a secondary query instead.
So, when fetching the PostComment entity using the following JPQL query, we can see that the post entity is still fetched by default even if we don’t provide an explicit JOIN FETCH directive:
PostComment comment = doInJPA(entityManager -> {
return entityManager.createQuery("""
select pc
from PostComment pc
where pc.id = :id
""", PostComment.class)
.setParameter("id", 1L)
.getSingleResult();
});
assertTrue(Hibernate.isInitialized(comment.getPost()));
assertEquals(
"High-Performance Java Persistence",
comment.getPost().getTitle()
);
When running the above JPQL query, Hibernate generates the following SQL queries:
SELECT pc.id, pc.post_id, pc.review FROM post_comment pc WHERE pc.id = 1 SELECT p.id, p.title FROM post p WHERE p.id = 1
The post entity is fetched using a secondary query because of the FetchType.EAGER strategy used by the @ManyToOne association.
Overriding FetchType.EAGER with fetchgraph
If you have a FetchType.EAGER association that you want to avoid fetching at query time, then you need to use the fetchgraph property.
In the JPA 2.1 and 2.2, the property was called javax.persistence.fetchgraph, and from JPA 3.0 is called jakarta.persistence.fetchgraph.
However, instead of using the hard-coded value in your queries, you are better off using the associated constant provided by the Hibernate
SpecHintsinterface.
If we want to override a FetchType.EAGER association, we have to do two things:
- First, we have to create an Entity Graph that does not include the association we want to avoid fetching.
- Afterward, we need to provide the newly created Entity Graph using the
fetchgraphproperty to the fetching method.
So, if we create an empty PostComment Entity Graph and set it via the fetchgraph property when fetching the PostComment entity via the find method:
PostComment comment = doInJPA(entityManager -> {
return entityManager.find(PostComment.class, 1L,
Map.of(
SpecHints.HINT_SPEC_FETCH_GRAPH,
entityManager.createEntityGraph(PostComment.class)
)
);
});
assertFalse(Hibernate.isInitialized(comment.getPost()));
try {
comment.getPost().getTitle();
fail("Should throw LazyInitializationException");
} catch(LazyInitializationException expected) {}
We can see that the post association is no longer fetched eagerly and a Proxy will be used instead.
When checking the executed SQL query, we can see that there’s no LEFT JOIN to the post table:
SELECT pc.id, pc.post_id, pc.review FROM post_comment pc WHERE pc.id = 1
The post_id column value will be used to set the identifier value on a Post proxy object that looks as follows:
comment = {PostComment@6445}
id = {Long@6456} 1
review = "The first part is about JDBC"
post = {Post$HibernateProxy$zLsYXLhX@6458}
$$_hibernate_interceptor = {ByteBuddyInterceptor@6461}
interfaces = {Class[1]@6462}
persistentClass = {Class@3334} "class com.vladmihalcea.hpjp.hibernate.fetching.Post"
...
entityName = "com.vladmihalcea.hpjp.hibernate.fetching.Post"
id = {Long@6456} 1
...
initialized = false
readOnly = false
unwrap = false
session = null
readOnlyBeforeAttachedToSession = null
sessionFactoryUuid = "171c4da9-9e3f-42cf-a5db-2319f2f3e63c"
sessionFactoryName = null
allowLoadOutsideTransaction = false
id = null
title = null
The fetchgraph is not limited to find method calls. If we are executing a JPQL, Criteria API, or native SQL query, then we can set the fetchgraph via the setHint method:
PostComment comment = doInJPA(entityManager -> {
return entityManager.createQuery("""
select pc
from PostComment pc
where pc.id = :id
""", PostComment.class)
.setHint(
SpecHints.HINT_SPEC_FETCH_GRAPH,
entityManager.createEntityGraph(PostComment.class)
)
.setParameter("id", 1L)
.getSingleResult();
});
assertFalse(Hibernate.isInitialized(comment.getPost()));
try {
comment.getPost().getTitle();
fail("Should throw LazyInitializationException");
} catch(LazyInitializationException expected) {}
The difference between fetchgraph and loadgraph
If you take a look at the JPA specification, you will see that there is also a loadgraph option. However, this property is not suitable for overriding the FetchType.EAGER strategy.
The loadgraph is used to specify which FetchType.LAZY associations are to be fetched eagerly at query time. However, it leaves all the FetchType.EAGER associations to be fetched either via a LEFT JOIN or a secondary query.
So, in our case, if we use the loadgraph property, we can see that the post entity will be fetched eagerly:
PostComment comment = doInJPA(entityManager -> {
return entityManager.find(PostComment.class, 1L,
Map.of(
SpecHints.HINT_SPEC_LOAD_GRAPH,
entityManager.createEntityGraph(PostComment.class)
)
);
});
assertTrue(Hibernate.isInitialized(comment.getPost()));
assertEquals(
"High-Performance Java Persistence",
comment.getPost().getTitle()
);
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
Starting with Hibernate 5.5, the FetchType.EAGER fetching strategy can be overridden at query time via the fetchgraph property.
While you are still better off to avoid mapping your entity association using the FetchType.EAGER strategy, if you are dealing with a legacy project, then you can use the fetchgraph property to avoid fetching the associations that are not required by a given business use case, therefore improving transaction response time.







I read nearly 20 of your articles in 3 days, I learned a lot. Thank you very much for these wonderful articles.
You’re welcome. If you liked the article, you are going to love my book or the video course.