The best way to handle the LazyInitializationException

(Last Updated On: January 29, 2018)

Introduction

The LazyInitializationException is undoubtedly one of the most common exceptions you can get when using Hibernate. This article is going to summarize the best and the worst ways of handling lazy associations.

Fetching 101

With JPA, not only you can fetch entities from the database, but you can also fetch entity associations as well. For this reason, JPA defines two FetchType strategies:

  • EAGER
  • LAZY

The problem with EAGER fetching

EAGER fetching means that associations are always retrieved along with their parent entity. In reality, EAGER fetching is very bad from a performance perspective because it’s very difficult to come up with a global fetch policy that applies to every business use case you might have in your enterprise application.

Once you have an EAGER association, there is no way you can make it LAZY. This way, the association will always be fetched even if the user does not necessarily need it for a particular use case. Even worse, if you forget to specify that an EAGER association needs to be JOIN FETCH-ed by a JPQL query, Hibernate is going to issue a secondary select for every uninitialized association, leading to N+1 query problems.

Unfortunately, JPA 1.0 decided that @ManyToOne and @OneToOne should default to FetchType.EAGER, so now you have to explicitly mark these two associations as FetchType.LAZY:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

LAZY fetching

For this reason, it’s better to use LAZY associations. A LAZY association is exposed via a Proxy, which allows the data access layer to load the association on demand. Unfortunately, LAZY associations can lead to LazyInitializationException.

For our next example, we are going to use the following entities:

postpostcommentlazyinitializationexception

When executing the following logic:

List<PostComment> comments = null;

EntityManager entityManager = null;
EntityTransaction transaction = null;
try {
    entityManager = entityManagerFactory()
        .createEntityManager();
    transaction = entityManager.getTransaction();
    transaction.begin();

    comments = entityManager.createQuery(
        "select pc " +
        "from PostComment pc " +
        "where pc.review = :review", PostComment.class)
    .setParameter("review", review)
    .getResultList();

    transaction.commit();
} catch (Throwable e) {
    if (transaction != null && 
        transaction.isActive())
        transaction.rollback();
    throw e;
} finally {
    if (entityManager != null) {
        entityManager.close();
    }
}

try {
    for(PostComment comment : comments) {
        LOGGER.info(
            "The post title is '{}'", 
            comment.getPost().getTitle()
        );
    }
} catch (LazyInitializationException expected) {
    assertEquals(
        "could not initialize proxy - no Session", 
        expected.getMessage()
    );
}

Hibernate is going to throw a LazyInitializationException because the PostComment entity did not fetch the Post association while the EntityManager was still opened, and the Post relationship was marked with FetchType.LAZY:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

How NOT to handle LazyInitializationException

Unfortunately, there are also bad ways of handling the LazyInitializationException like:

These two Anti-Patterns are very inefficient from a database perspective, so you should never use them in your enterprise application.

JOIN FETCH to the rescue

Entities are only needed when the current running application-level transaction needs to modify the entities that are being fetched. Because of the automatic dirty checking mechanism, Hibernate makes it very easy to translate entity state transitions into SQL statements.

Considering that we need to modify the PostComment entities, and we also need the Post entities as well, we just need to use the JOIN FETCH directive like in the following query:

comments = entityManager.createQuery(
    "select pc " +
    "from PostComment pc " +
    "join fetch pc.post " +
    "where pc.review = :review", PostComment.class)
.setParameter("review", review)
.getResultList();

The JOIN FETCH directive instructs Hibernate to issue an INNER JOIN so that Post entities are fetched along with the PostComment records:

SELECT pc.id AS id1_1_0_ ,
       p.id AS id1_0_1_ ,
       pc.post_id AS post_id3_1_0_ ,
       pc.review AS review2_1_0_ ,
       p.title AS title2_0_1_
FROM   post_comment pc
INNER JOIN post p ON pc.post_id = p.id
WHERE  pc.review = 'Excellent!'

That’s it! It’s as simple as that!

DTO projection to the rescue

Now, we are not done yet. What if you don’t even want entities in the first place. If you don’t need to modify the data that’s being read, why would you want to fetch an entity in the first place? A DTO projection allows you to fetch fewer columns and you won’t risk any LazyInitializationException.

For instance, we can have the following DTO class:

public class PostCommentDTO {

    private final Long id;

    private final String review;

    private final String title;

    public PostCommentDTO(
        Long id, String review, String title) {
        this.id = id;
        this.review = review;
        this.title = title;
    }

    public Long getId() {
        return id;
    }

    public String getReview() {
        return review;
    }

    public String getTitle() {
        return title;
    }
}

If the business logic only needs a projection, DTOs are much more suitable than entities. The previous query can be rewritten as follows:

List<PostCommentDTO> comments = doInJPA(entityManager -> {
    return entityManager.createQuery(
        "select new " +
        "   com.vladmihalcea.book.hpjp.hibernate.fetching.PostCommentDTO(" +
        "       pc.id, pc.review, p.title" +
        "   ) " +
        "from PostComment pc " +
        "join pc.post p " +
        "where pc.review = :review", PostCommentDTO.class)
    .setParameter("review", review)
    .getResultList();
});

for(PostCommentDTO comment : comments) {
    LOGGER.info("The post title is '{}'", comment.getTitle());
}

And Hibernate can execute a SQL query which only needs to select three columns instead of five:

SELECT pc.id AS col_0_0_ ,
       pc.review AS col_1_0_ ,
       p.title AS col_2_0_
FROM   post_comment pc
INNER JOIN post p ON pc.post_id = p.id
WHERE  pc.review = 'Excellent!'

Not only that we got rid of the LazyInitializationException, but the SQL query is even more efficient. Cool, right?

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Conclusion

LazyInitializationException is a code smell because it might hide the fact that entities are used instead of DTO projections. Sometimes, fetching entities is the right choice, in which case, a JOIN FETCH directive is the simplest and the best way to initialize the LAZY Hibernate proxies.

Subscribe to our Newsletter

* indicates required
10 000 readers have found this blog worth following!

If you subscribe to my newsletter, you'll get:
  • A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
  • 3 chapters from my book, High-Performance Java Persistence, 
  • a 10% discount coupon for my book. 
Get the most out of your persistence layer!

Advertisements

46 thoughts on “The best way to handle the LazyInitializationException

  1. Hi Vlad, I did purchase and go through your book and it did give me a lot of learnings. I have a question regarding the fetch types.

    In my application, I have a portfolio entity which has multiple other entities marked. Earlier, it querying portfolio was taking a considerable amount of time leading to performance issues. After reading here, I marked the mappings to fetch LAZY and now I get resultset list in milliseconds.

    Post getting the resultset, I have a business processing logic, where I have to call the attributes of other entity like BusinessUnit which is part of Portfolio. Now, businessunit.getName() triggers a lot of queries and again causing the delay in processing. Since this BusinessUnit was marked as LAZY in Portfolio.

    Can you please advise how to address this issue of calling child attributes once getting the parent entity resultset.

    1. It’s actually very simple. As explained in the book, you have to use JOIN FETCH to initialize the LAZY associations on a per business use case. For ToOne is simple since you can JOIN FETCH as many associations you want. For collections, only one should be JOIN FETCHED. The remaining ones, should be initialized with Hibernate.initialize and secondary queries.

  2. i have this promblem,i use hibernate second cache,but it send a large of command to redis to get some lazy attributes,this spends a lot time. so I want use spring cache to cache the entity at once; but I found the lazy attributes cant`t get at the second to get which throws this exception: ‘org.hibernate.LazyInitializationException: failed to lazily initialize a collection, could not initialize proxy – no Session’;
    So how can I solve this promble? thank you very mutch.

    1. In my book, High-Performance Java Persistence, you will find two chapters that can help you with your problem: the Caching chapter describes how you can get an entire aggregate without going to the DB while the Fetching one explains the best way to handle the LazyInitializationException.

  3. Thank you for the article, I have a question regarding the DTO projection, what if the DTO contains et’s say 2 lists, I cannot pass 2 lists to the constructor of PostCommentDTO.

    What I’m doing is iterate through the query result (without the projection) and create the DTO list myself. Is there a better alternative?

Leave a Reply

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