Search code examples
javaspringhibernatespring-datahibernate-mapping

Theoretical question on Hibernate Lazy Loading


I've learned that Hibernate allows the child elements to be queried only when it's necessary through the use of Lazy Loading.

So if we have:

@Entity
public class Payment {

 ...

 @ManyToOne(fetch = FetchType.LAZY)
 private Debtor debtor;

}

I expect that when I fetch a Payment from the Database Hibernate set a placeholder in the debtor attribute and fetches the Debtor only when it's strictly required.

So if I use a getter method in order to get a Debtor from my Payment Object:

Debtor debtor = payment.getDebtor();

I expect the thread blocks until Hibernate performs the SELECT query and return the Debtor object.

So why the heck I always get an HibernateLazyLoading exception which obliges me to write a custom fetch query in the PaymentRepository, slowing down my initial query AS I would have used an EAGER FetchType?

So why does this FetchType.LAZY exists if it doesn't work as naturally expected?


Solution

  • I would like to specify @Andronicus answer because the answer is not accurate.

    LazyInitializationException

    It is not strictly related to @Transactional, transactions or open / closed connections. Behavior is pretty simply (there is pseudo code below)

    Without LazyInitializationException

    Context context = Hibernate.openPersistentContext();
    
    Payment payment = context.getById(1L, Payment.class);
    Debtor debtor = payment.getDebtor();
    
    Hibernate.closePersistentContext();
    

    With LazyInitializationException

        Context context = Hibernate.openPersistentContext();
    
        Payment payment = context.getById(1L, Payment.class);
    
        Hibernate.closePersistentContext();
    
        Debtor debtor = payment.getDebtor();
    

    Questions

    So why the heck I always get an HibernateLazyLoading exception which obliges me to write a custom fetch query in the PaymentRepository, slowing down my initial query AS I would have used an EAGER FetchType?

    Because Hibernate.closePersistentContext() happened somewhere before.

    So why does this FetchType.LAZY exists if it doesn't work as naturally expected?

    Because we don't always need the entirely net of an entity graph. We can use JPQL (HQL), criteria and projections to load parts of the entity. We need to explain to Hibernate how the entities are related, so we need to add the associations, like @ManyToOne.

    There is a little problem here: mapping serves for two purposes

    1. Explain to Hibernate how the entities are related
    2. Save / Load entities

    So the simplest way to disconnect loading from objects mapping is FetchType.LAZY.

    The simple rule

    Always use FetchType.LAZY everywhere and fetch necessary parts of the entity graph in the place where it is neeed.