The hibernate.enable_lazy_load_no_trans Anti-Pattern

Introduction

I’ve already written about the Open Session in View Anti-Pattern, so now it’s time to add another Hibernate fetching bad practices. Although the hibernate.enable_lazy_load_no_trans configuration property is a lesser known setting, it’s good to know why you shouldn’t employ it in your data access layer code.

What does it do?

By default, this property is disabled and, to enable it, you need to provide the following configuration property:

<property 
    name="hibernate.enable_lazy_load_no_trans" 
    value="true"/>

Considering the following entities:

PostAndPostCommentLazyLoadNoTrans

With this configuration property in place, the following code snippets can be executed without throwing any LazyInitializationException:

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();
    }
}
for(PostComment comment : comments) {
    LOGGER.info("The post title is '{}'", 
        comment.getPost().getTitle());
}

Without the hibernate.enable_lazy_load_no_trans configuration property in place, the comment.getPost().getTitle() line would throw a LazyInitializationException because the comments collection was not initialized, and the Persistence Context is already closed, along with the database connection that fetched the post entity.

Behind the scenes, a temporary Session is opened just for initializing every post association. Every temporary Session implies acquiring a new database connection, as well as a new database transaction.

The more association being loaded lazily, the more additional connections are going to be requested which puts pressure on the underlying connection pool. Each association being loaded in a new transaction, the transaction log is forced to flush after each association initialization.

If you enjoyed this article, I bet you are going to love my book as well.

Conclusion

Just like Open Session in View, the hibernate.enable_lazy_load_no_trans configuration property is an anti-pattern as well because it only treats the symptoms and does not solve the actual cause of the LazyInitializationException.

By properly initializing all lazy associations prior to closing the initial Persistence Context, and switching to DTO projections where entities are not even necessary, the LazyInitializationException is prevented in a much more efficient way.

Enter your email address to follow this blog and receive notifications of new posts by email.

Advertisements

6 thoughts on “The hibernate.enable_lazy_load_no_trans Anti-Pattern

  1. Great tip!

    Seems like this configuration can be more dangerous than an Open Session In View Filter.

    If I’m not wrong this is the default behavior of EclipseLink, although I don’t know how it’s implemented.

  2. I kind of disagree. Not enabling enable_lazy_load_no_trans seems like premature optimization and rather harmful since optimization problems are not as bad as lazy loading exceptions. I haven’t made any benchmarks, but in real-live projects there has not been visible performance hit.

    Properly initializing all lazy associations prior to closing the initial Persistence Context is laborious and error-prone and thus impractical without using DTOs.

    Also this option can save legacy projects having problems with lazy loading exceptions.

    1. This is not premature optimization. This is proper data access logic. The Persistence Context is a transactional write-behind cache, and it’s designed to operate within the scope of a database transaction. This property is cheating the initial design goal, with the consequences that I mentioned.

      Just because you use it in production, it does not mean that it’s a good thing. To experience significant performance degradation you have to have many uninitialized associations, and you might not have got to that point. Anyway, the fetching policy is a query-time responsibility because that’s where you have all the business context related to what info is needed to be retrieved from the DB. If you let the UI drive the data access logic, you’ll break layer encapsulation, and it’s going to be much more difficult to tune your read operations.

      1. I agree, data should be fully populated at service layer, in a transaction.
        One thing that is holding us back is the lack of proper “javax.persistence.fetchgraph” implementation from hibernate – there are plans to have it fixed? Currently it loads all associations, there is no way to tell hibernate to NOT load some eager defined assocs at query time.

      2. Personally, I find the Entity Graph superfluous. Why not use the FETCH directive with JPQL and Criteria API? The Entity Graph is less expressive and allows the developer to separate the fetching policy from the actual query. Each query should be expressed in terms of the current business logic requirement, hence the fetching policy should not be separated from the query itself.

        As for the Entity Graph improvements, I think they might be addressed in 6.0, after all the current refactorings are being done.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s