LazyInitializationException demystified

Are you a Hibernate user? Have you ever got a LazyInitializationException? Let’s clarify this topic a bit. You can find plenty of solutions for the exception, however they lack explanation and might suggest a risk-prone way of solving the problem.

Let’s see what are the possibilities when you want to describe an entity relation. We want to represent a Company with have multiple Products which they are selling. One can come up with the following classes:

@Entity
public class Company {
    @Id
    private UUID id;

    @OneToMany(mappedBy = "company", cascade = CascadeType.PERSIST)
    private Set<Product> products = new HashSet<>();

    public Company() {
        this.id = UUID.randomUUID();
    }

    // getters setters omitted
}
@Entity
public class Product {
    @Id
    private UUID id;

    @Column(name = "name")
    private String name;

    @ManyToOne
    private Company company;

    public Product() {
        this.id = UUID.randomUUID();
    }

    // getters setters omitted
}

Simple classes with a simple relation. Talking about relations. Hibernate offers two way of relations, eager loaded relations and lazy loaded relations. The behavior can be defined through the annotation for the relation, i.e. @OneToMany, @ManyToOne, @OneToOne, @ManyToMany.

The main difference between the two is the time of loading the relation. Eager loading means that if you load a Company from the database, Hibernate will automatically load the products relation for it before you are able to use it. Lazy loading means that Hibernate will defer the loading until the relation is accessed. This is implemented through Proxy classes, for example you can take a look on PersistentSet, PersistentBag, etc. classes.

Usually it’s a good idea to make every relation lazy because each use case in an application needs different set of data. If I’m showing all the companies in a table, I only need the Company entities and in a detailed Company view, I might need the products sold by this Company. In the first case, it’s clear that there is no point loading the Products for a certain Company as it wouldn’t be used anyhow, plus it slows down the application.

Yes, loading relations can be done in various ways, but all of them implies some sort of performance degradation. Also, there are multiple ways to load lazy relations as well. You can use simple loading-on-access, fetching through Criteria API, fetching through JPQL, etc.

Let’s see the first, loading the lazy relation on access. As I mentioned earlier, lazy loading is implemented through Proxy objects. When you try to call any method on the Proxy object, it will try to fetch the relation from the database. In case of the Company/Product example, if you fetch a Company and call the getProducts method, it will return a Proxy object (some kind of Collection proxy). If you call any method on this object, Hibernate will try to load this collection from the database to make it usable. Accessing the database of course requires a Transaction. If no transaction present when accessing the relation, Hibernate will throw the well-known LazyInitializationException with the following message:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.arnoldgalovics.blog.repository.Company.products, could not initialize proxy - no Session

Check out the test case for it:

@Test(expected = LazyInitializationException.class)
public void shouldFailWithLazyInitException() {
    Company company = underTest.getCompany(companyId);
    company.getProducts().size();
}

First, we retrieve the Company based on the companyId and after that we access the relation through the getProducts method. This method returns the Proxy object on which we call the size method which triggers the loading. The loading is done by executing the following SQL statement:

select
        products0_.company_id as company_3_1_0_,
        products0_.id as id1_1_0_,
        products0_.id as id1_1_1_,
        products0_.company_id as company_3_1_1_,
        products0_.name as name2_1_1_ 
    from
        Product products0_ 
    where
        products0_.company_id=?

The company_id parameter is the id of the Company which we are loading the relation for. This is done by Hibernate automatically.

The solution for this exception is either loading the relation in a Transaction or allowing Hibernate to create an ad-hoc transaction only for loading the relation.

The test case for loading the relation within a Transaction is the following:

@Test
public void shouldLoadWithinTransaction() {
    helper.doInTransaction(em -> {
        Company company = underTest.getCompany(companyId);
        assertFalse(isInitialized(company.getProducts()));
        int productCount = company.getProducts().size();
        assertEquals(2, productCount);
    });
}

The helper class is basically just a wrapper to utilize Spring’s declarative transaction management.

The other way – which is allowing Hibernate to create temporary Transactions – is not recommended as it might cause performance issues quickly. You can read more about it in this blog post.

Of course, this is not the best way of loading lazy relations because if you think about loading multiple Companies, let’s say all of them, Hibernate will execute the following query:

select
        company0_.id as id1_0_ 
    from
        Company company0_

If you try to print out the number of Products sold by one Company in the following way:

for (Company c : companies) {
    System.out.println(c.getProducts().size());
}

You will end up executing this query for each Company:

select
        products0_.company_id as company_3_1_0_,
        products0_.id as id1_1_0_,
        products0_.id as id1_1_1_,
        products0_.company_id as company_3_1_1_,
        products0_.name as name2_1_1_ 
    from
        Product products0_ 
    where
        products0_.company_id=?

This means, you have to execute 1 query to retrieve all the Companies and N – numer of companies – queries to retrieve all the Products for a single Company. This is often referred to as N+1 problem.

With this implementation you will have several queries to execute which is obviously not the most efficient way of doing this. A better idea is to load the relation at the time you are loading the parent – Company – entity. This can be done with a Fetch Join.

In Criteria API, you can do the following:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Company> query = cb.createQuery(Company.class);
Root<Company> root = query.from(Company.class);
query.select(root);
root.fetch("products");
query.where(cb.equal(root.get("id"), id));
return entityManager.createQuery(query).getSingleResult();

The important part is the root.fetch(“products”) line. It will instruct Hibernate to load the products relation with the initial query. Now the query is the following:

select
        company0_.id as id1_0_0_,
        products1_.id as id1_1_1_,
        products1_.company_id as company_3_1_1_,
        products1_.name as name2_1_1_,
        products1_.company_id as company_3_1_0__,
        products1_.id as id1_1_0__ 
    from
        Company company0_ 
    inner join
        Product products1_ 
            on company0_.id=products1_.company_id 
    where
        company0_.id=?

As you can see, it’s joined to the root entity and the select part contains all the necessary attributes of the Product entity. This can be done through JPQL as well, there is a special JOIN FETCH statement to do it. In this way you will have only 1 query instead of N+1 which is a lot more better.

The source can be found on GitHub. Feel free to reach me out in case of questions in the comments or on Twitter.

2 Replies to “LazyInitializationException demystified”

  1. O.Martin says:

Leave a Reply

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