JPA Default Fetch Plan

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Hypersistence Optimizer is that tool!


In this article, I’m going to explain what the JPA Default Fetch Plan is and how it differs from the Query Fetch Plan when using FetchType EAGER associations.

JPA FetchType

A JPA association can be fetched lazily or eagerly. The fetching strategy is controlled via the fetch attribute of the @OneToMany, @OneToOne, @ManyToOne, or @ManyToMany.

The fetch attribute can be either FetchType.LAZY or FetchType.EAGER. By default, @OneToMany and @ManyToMany associations use the FetchType.LAZY strategy while the @OneToOne and @ManyToOne use the FetchType.EAGER strategy instead.

As I explained in this article, the FetchType.EAGER strategy is terrible default. Never in my life, I’ve seen a good use case that required an association to use the FetchType.EAGER strategy. That’s because it’s unlikely that every possible business use case will require fetching a given association and the fact that Hibernate cannot override the FetchType.EAGER strategy with FetchType.LAZY at query execution time.

Default Fetch Plan

Every entity has a default fetch plan that’s given by the fetch strategies configured at mapping time. For instance, the following PostComment entity has a post association that uses the default FetchType.EAGER strategy given by the @ManyToOne annotation.

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    private Long id;

    private Post post;

    private String review;
    //Getters and setters omitted for brevity

When fetching the PostComment entity using the find method:

PostComment comment = entityManager.find(PostComment.class, 1L);

The default fetch plan will be used and a LEFT JOIN will be added in order to ensure that the post association is fetched eagerly:

SELECT AS id1_1_0_,
       pc.post_id AS post_id3_1_0_, AS review2_1_0_, AS id1_0_1_,
       p.title AS title2_0_1_
FROM post_comment pc
LEFT OUTER JOIN post p ON pc.post_id =

Query Fetch Plan

However, if you fetch the entity via a JPQL query:

PostComment comment = entityManager.createQuery("""
    select pc
    from PostComment pc
    where = :id
    """, PostComment.class)
.setParameter("id", 1L)

Then, the default fetch plan is overridden by the query fetch plan, so two queries are executed instead of just one:

SELECT AS id1_1_,
       pc.post_id AS post_id3_1_, AS review2_1_
FROM post_comment pc

SELECT AS id1_0_0_,
       p.title AS title2_0_0_
FROM post p

A query defines a fetch plan explicitly, hence the default fetch plan is overridden. However, an association that has to be fetched eagerly will still be fetched prior to returning the query result set, and that’s why secondary queries are executed for FetchType.EAGER associations when executing a JPQL, Criteria API, or native SQL query that fetched entities.

That’s why FetchType.EAGER is a dangerous mapping. It forces you to use JOIN FETCH in all your queries that fetch the entity containing the FetchType.EAGER association.

Online Workshops

If you enjoyed this article, I bet you are going to love my upcoming 4-day x 4 hours High-Performance Java Persistence Online Workshop


The default JPA fetch plan is the one you provide when mapping your entities. Queries override the default fetch plan and provide their own plan.

However, while FetchType.LAZY associations can be fetched eagerly at query execution time via a JOIN FETCH directive, FetchType.EAGER associations cannot be fetched lazily, as secondary queries will always be executed in order to ensure that the FetchType.EAGER associations are always initialized.

Transactions and Concurrency Control eBook

2 Comments on “JPA Default Fetch Plan

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.