Spring OpenSessionInViewFilter with @Transactional annotation

11,765

Solution 1

OpenSessionInView is a servlet filter than just Open a hibernate session and store it in the SessionHolder for the thread that is serving the request. With this session opened, hibernate can read the Lazy initialized collections and objects when you use this in the rendering stage of the request. This session can be accessed when you invoke SessionFactory.getCurrentSession().

But, OpenSessionInView just opens the session and it doesn't begin any transactions. With a session opened you can read objects from database but, if you want to do something in a transaction you need @Transactional annotations or other mechanism to demarcate the begin and the end of the transaction when you want.

Then the answer of the questions:

Is it bad practice to use OpenSessionInViewFilter in application having complex schema.

This is a good practice if you need avoid the LazyInitializationException and the overload is just open new Hibernate Session and close it at the end of the request for each request.

Using this filter can cause N+1 problem

I use this filter in many projects and not cause any problem.

if we are using OpenSessionInViewFilter does it mean @Transactional not required?

No. You only have a Hibernate Session opened in the SessionHolder of the thread, but if you need Transactions you need put @Transactional.

Solution 2

Throwing in my 0.02c here (and expanding on Fernando Rincon's excellent answer):

You shouldn't be using a OpenSessionInView filter just because you need to get around a LazyInitializationException. Its just going to add another layer of confusion and complexity to your system. You should know from your system design exactly where you are going to need to access collections on the front end. From there, it's easy and (in my experience) more logical to build a controller method to call a service method to retrieve your collection.

However if you have another problem that using the OpenSessionInView filter solves, and as a happy side effect you then have a session open, then I don't see the harm in using it to access your collections. However, I'd say that if you use the OpenSessionInView to fetch a collection object in one place, you should refactor your code in other places to do the same thing so as the strategy used to fetch collections is standardised across your application.

Weigh up the costs of this refactor against the cost of writing the controller & service methods to determine if you should be using a OpenSessionInView filter.

Solution 3

The typical usage pattern for OpenSessionInViewFilter is that some Entity is lazily loaded but during the view rendering phase the view needs some attribute of this Entity that was not loaded initially thus necessitating the need to fetch this data from the database. Now typically the transaction demarcation is made to happen in the service layer of your web application so by the time the view rendering takes place the view is working with a detached entity which results in a LazyInitializationException when accessing the unloaded attribute.

From this url https://developer.jboss.org/wiki/OpenSessionInView :

The problem
A common issue in a typical web-application is the rendering of the view, after the main logic of the action has been completed, and therefore, the Hibernate Session has already been closed and the database transaction has ended. If you access detached objects that have been loaded in the Session inside your JSP (or any other view rendering mechanism), you might hit an unloaded collection or a proxy that isn't initialized. The exception you get is: LazyInitializationException: Session has been closed (or a very similar message). Of course, this is to be expected, after all you already ended your unit of work.

A first solution would be to open another unit of work for rendering the view. This can easily be done but is usually not the right approach. Rendering the view for a completed action is supposed to be inside the first unit of work, not a separate one. The solution, in two-tiered systems, with the action execution, data access through the Session, and the rendering of the view all in the same virtual machine, is to keep the Session open until the view has been rendered.

As an alternative, consider loading the Entity with just the right amount of data required by your view. This can be accomplished by using DTO projections. This article lists some of the downsides of using the Open Session In View pattern : https://vladmihalcea.com/the-open-session-in-view-anti-pattern/

Solution 4

OpenSessionInViewFilter is a servlet filter that binds a hibernate session to http request and for all db operations, transactional and non transactional, same hibernate session is used for a given http request. This exposes db layer to web layer that makes it anti-pattern.

My experience is that this makes the code difficult to debug when we want to make changes to java objects and do not want those to get reflected in database. Since the hibernate session is always open, it expects to flush the data in database.

This should be used only when JS base rest services are there with no service layer in between.

Share:
11,765
Gautam
Author by

Gautam

Java developer

Updated on June 05, 2022

Comments

  • Gautam
    Gautam almost 2 years

    This is regarding Spring OpenSessionInViewFilter using with @Transactional annotation at service layer.

    i went through so many stack overflow post on this but still confused about whether i should use OpenSessionInViewFilter or not to avoid LazyInitializationException It would be great help if somebody help me find out answer to below queries.

    • Is it bad practice to use OpenSessionInViewFilter in application having complex schema.
    • using this filter can cause N+1 problem
    • if we are using OpenSessionInViewFilter does it mean @Transactional not required?

    Below is my Spring config file

    <context:component-scan base-package="com.test"/>
    <context:annotation-config/>
     <bean id="messageSource"
            class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
            <property name="basename" value="resources/messages" />
            <property name="defaultEncoding" value="UTF-8" />
        </bean>
     <bean id="propertyConfigurer"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
            p:location="/WEB-INF/jdbc.properties" />
     <bean id="dataSource"
            class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
            p:driverClassName="${jdbc.driverClassName}"
            p:url="${jdbc.databaseurl}" p:username="${jdbc.username}"
            p:password="${jdbc.password}" />
           <bean id="sessionFactory"
            class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />     
            <property name="configLocation">
                <value>classpath:hibernate.cfg.xml</value>
            </property>
            <property name="configurationClass">
                <value>org.hibernate.cfg.AnnotationConfiguration</value>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">${jdbc.dialect}</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <!--
                    <prop key="hibernate.hbm2ddl.auto">create</prop>
                     -->
                </props>
            </property>
        </bean>
     <tx:annotation-driven /> 
     <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
    
      </bean>
    
  • Gautam
    Gautam almost 10 years
    +1 Thanks for detailed answer.Sorry i forgot to mention one more thing in my quetion.Do i need to change session mode as well? i have seen people are facing issues in update and save when working with OpenSessionInView filter
  • Fernando Rincon
    Fernando Rincon almost 10 years
    What do you mean with session mode?
  • Gautam
    Gautam almost 10 years
    @Fernanado thanks for reply i checked few post and noticed this code session.setFlushMode(FlushMode.AUTO); why we explicitly need to set session flush mode? sorry it was session flush mode.
  • Fernando Rincon
    Fernando Rincon almost 10 years
    By default, when a session is opened by OpenSessionInView the flushMode is stablished to MANUAL because its purpose is only read objects but not write to database anything. If you use HibernateTransactionManager and all of the modifications are enclosed in a transaction you don't need to change the flush mode because HibernateTransactionManager change this when start a new transaction. But, if you change the objects outside a transaction (this is not a good practice) you need to put the flush mode to AUTO .
  • Gautam
    Gautam about 9 years
    Thanks for your suggestion.Really appreciate.