The Open Session In View Anti-Pattern

Introduction

The Open Session in View is an Anti-Pattern, and this post is going to demonstrate why it is so. First of all, let’s start with the Wikipedia definition of an Anti-Pattern:

An anti-pattern (or antipattern) is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive.

A LazyInitializationException band aid

When using JPA and Hibernate, the Fetching policy can have one of the biggest impacts on application performance, and, as explained in my High-Performance JDBC presentation, you should always fetch just as much data you need to fulfil the requirements of a given business logic use case. Fetching too many columns than necessary has an impact, and that’s why entities are not good candidates for read-only views. In turn, DTO projections are better suited for read-only data sets.

Entities are very useful for read-write transactions because you can benefit from the automatic dirty checking mechanism while preventing lost updates phenomena in multi-request logical transactions.

Unfortunately, many enterprise applications don’t make this distinction, and they rely solely on entities for both read-only and read-write transactions. Not only that an entity has more columns than a custom DTO projection, but the entity might have associations as well. Entity associations are convenient because it allows the application developer to access joined relationships without even needing to write a query.

Hibernate comes with Proxies that allow the application developer to defer fetching until the association is needed. This is very useful, especially from a performance perspective. The worst thing to do is to use EAGER associations because, once a relationship is set to be eagerly fetched, it cannot be changed to being fetched lazily on a per query basis. For this reason, many associations are configured with the FetchType.LAZY attribute.

However, a LAZY association needs the Session to be opened in order to initialize the Proxy. If the Persistence Context is closed, when trying to access a non-initialized LAZY association, the infamous LazyInitializationException is thrown.

For read-only views, when using DTO projections, we have to manually choose the child associations properties too, therefore, the LazyInitializationException cannot occur. For read-write transactions, entities might be fetched with the intention of being modified and saved at the end of the currently running workflow. These entities are prone to LazyInitializationException(s), so there are good ways and bad ways of dealing with this issue.

It is only the business layer responsibility to fetch all the data that’s necessary for a particular business use case. For many-to-one and one-to-one associations, as well as to at most one one-to-many relationship, JOIN FETCH directive is the best way of initializing the associations that are going to be needed in the view layer. For multiple one-to-many associations, to avoid a Cartesian Product, it’s necessary to use secondary queries. These secondary queries can be fired when the association is accessed for the first time, which can be done with the Hibernate.initialize(proxy) utility.

Open Session In View takes a different approach. Instead of letting the business layer decide how it’s best to fetch all the associations that are needed by the View layer, it forces the Persistence Context to stay open so that the View layer can trigger the Proxy initialization.

OpenSessionInView

  • The OpenSessionInViewFilter calls the openSession method of the underlying SessionFactory and obtains a new Session.
  • The Session is bound to the TransactionSynchronizationManager.
  • The OpenSessionInViewFilter calls the doFilter of the javax.servlet.FilterChain object reference and the request is further processed
  • The DispatcherServlet is called, and it routes the HTTP request to the underlying PostController.
  • The PostController calls the PostService to get a list of Post entities.
  • The PostService opens a new transaction, and the HibernateTransactionManager reuses the same Session that was opened by the OpenSessionInViewFilter.
  • The PostDAO fetches the list of Post entities without initializing any lazy association.
  • The PostService commits the underlying transaction, but the Session is not closed because it was opened externally.
  • The DispatcherServlet starts rendering the UI, which, in turn, navigates the lazy associations and triggers their initialization.
  • The OpenSessionInViewFilter can close the Session, and the underlying database connection is released as well.

At a first glance, this might not look like a terrible thing to do, but, once you view it from a database perspective, a series of flaws start to become more obvious.

The service layer opens and closes a database transaction, but afterward, there is no explicit transaction going on. For this reason, every additional statement issued from the UI rendering phase is executed in auto-commit mode. Auto-commit puts pressure on the database server because each statement must flush the transaction log to disk, therefore causing a lot of I/O traffic on the database side. One optimization would be to mark the Connection as read-only which would allow the database server to avoid writing to the transaction log.

There is no separation of concerns anymore because statements are generated both by the service layer and by the UI rendering process. Writing integration tests that assert the number of statements being generated requires going through all layers (web, service, DAO), while having the application deployed on a web container. Even when using an in-memory database (e.g. HSQLDB) and a lightweight web server (e.g. Jetty), these integration tests are going to be slower to execute than if layers were separated and the back-end integration tests used the database, while the front-end integration tests were mocking the service layer altogether.

The UI layer is limited to navigating associations which can, in turn, trigger N+1 query problems. Although Hibernate offers @BatchSize for fetching associations in batches, and FetchMode.SUBSELECT to cope with this scenario, the annotations are affecting the default fetch plan, so they get applied to every business use case. For this reason, a data access layer query is much more suitable because it can be tailored for the current use case data fetch requirements.

Last but not least, the database connection is held throughout the UI rendering phase which increases connection lease time and limits the overall transaction throughput due to congestion on the database connection pool. The more the connection is held, the more other concurrent requests are going to wait to get a connection from the pool.

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

Conclusion

The Open Session in View is a solution to a problem that should not exist in the first place, and the most likely root cause is relying exclusively on entity fetching. If the UI layer only needs a view of the underlying data, then the data access layer is going to perform much better with a DTO projection.

A DTO projection forces the application developer to fetch just the required data set and is not susceptible to LazyInitializationException(s). This way, the separation of concerns is no longer compromised, and performance optimizations can be applied at the data access layer level since all statements are confined to the boundaries of the currently executing transaction.

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

Advertisements

11 thoughts on “The Open Session In View Anti-Pattern

  1. When you say “For multiple one-to-many associations, to avoid a Cartesian Product, it’s necessary to use secondary queries. These secondary queries can be fired when the association is accessed for the first time, which can be done with the Hibernate.initialize(proxy) utility.”, are you talking about multiple one to many associations declared by the same underlying entity, or a nested set of relationships (i.e. the underlying entity has a oneToMany association and that assocation has another oneToMany association)?

    1. For sibling collections, secondary queries are more suitable. For nested collections, secondary queries with joins are a much better approach, but that requires using JPQL queries and reconstructing the graph yourself.

  2. Hello Vlad,
    If an application uses OSIVFilter, when using SessionFactory.getCurrentSession in the request lifecycle
    will that session span in multiple requests? I mean if I evict an object from that session any OTHER concurrent requests that may have modified the object’s properties lose their modifications due to the evict ?

  3. Hello Vlad,
    If an application uses OSIVFilter, when using SessionFactory.getCurrentSession in the request lifecycle
    will that session span in multiple requests? I mean if I evict an object from that session any OTHER concurrent requests that may have modified the object’s properties lose their modifications due to the evict ?

    1. If an application uses OSIVFilter, when using SessionFactory.getCurrentSession in the request lifecycle
      will that session span in multiple requests?

      If the application uses OSIV, which is a bad choice anyway, then it’s the job of the OSIV implementation to handle the Session registration so that SessionFactory.getCurrentSession gives the same instance while being inside the life cycle of the Web Filter that implements OSIV.

      The session should never span over multiple requests. That’s not the goal for OSIV. That’s what @PersistenceContext(type = PersistenceContextType.EXTENDED) would give you in a Java EE application that uses Stafeul EJB.

      I mean if I evict an object from that session any OTHER concurrent requests that may have modified the object’s properties lose their modifications due to the evict ?

      As I explained in my book, High-Performanace Java Persitence, the Session is single-threaded. If you use OSIV, the Session is bound to an HttpRequest, which should also be single-threaded.

      If you bind the Session to the HttpSession, or you use @PersistenceContext(type = PersistenceContextType.EXTENDED) and expose the Session to multiple threads, then you are in trouble.

      Now, if you evict an entity, the entity will be detached and no further synchronization will be done. So, you don’t lose any changes until you dereference the entity. You lose durability for those changes.

      1. Thanks Vlad,
        I know that this OSIVFilter should not be used but the application is designed like this and a big change is not scheduled very soon. My worries were that if no further synchronization will be done in that session after the evict this will affect other requests that use the same object (same database table row) at the same time.

      2. If you are sharing the Session or thee entities between multiple concurrent requests, then an unforeseen application architecture refactoring will soon be justified.

        The only way to fix it is to avoid sharing entities, proxies, Session and EntityManager because they are not guaranteed to be thread-safe, so strange bugs will lurk when you least expect them.

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