Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-13848

Hibernate 4 Autoflush does not work with Spring OpenSessionInViewInterceptor

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Minor
    • Resolution: Won't Fix
    • Affects Version/s: 4.2 GA
    • Fix Version/s: None
    • Component/s: Transaction
    • Labels:
    • Last commented by a User:
      true

      Description

      Hibernate 4 Autoflush does not work with Spring OpenSessionInViewInterceptor

      We found a Bug while migrating our applications with Spring 3.0.5 and Hibernate 3.6.5 to Spring 4.2 and Hibernate 4.2: Autoflush of the objects does not work anymore.

      This was our testcase:
      // select all not canceled persons
      final Query query = currentSession().createQuery(selectSQL);
      List<TOPerson> personenListe = query.list();

      // canceled first person
      TOPerson person = personenListe.get(0);
      person.setKnStornoPerson("S"); // canceled Person

      // select - select all not canceled persons once again
      final Query query2 = currentSession().createQuery(selectSQL2);
      List<TOPerson> personenListe2 = query2.list();

      Result: The just canceled person still exists in the result set. We expected that query2.list(() performs an autoflush in Hibernate and the result set would not contain the just canceled person. This would be the correct behavior.

      We found the cause to be in the OpenSessionInViewInterceptor. Without it the autoflush works correctly.
      We used the following configuration:
      <util:properties id="hibernateProperties">
      <prop key="hibernate.dialect">${env:hibernate.dialect}</prop>
      <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
      <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache.xml</prop>
      <prop key="hibernate.cache.use_query_cache">${env:hibernate.useQueryCache}</prop>
      <prop key="hibernate.cache.use_second_level_cache">${env:hibernate.useSecondLevelCache}</prop>
      <prop key="hibernate.connection.release_mode">after_statement</prop>
      <prop key="hibernate.transaction.factory_class">org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory</prop>
      </util:properties>

      <bean
      id="sessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
      p:dataSource-ref="dataSource"
      p:mappingJarLocations="/WEB-INF/lib/Domain.jar"
      p:entityInterceptor-ref="benutzerInfoEntityInterceptor"
      p:hibernateProperties-ref="hibernateProperties"
      p:jtaTransactionManager-ref="transactionManager"
      />

      <bean
      id="openSessionInViewInterceptor"
      class="de.kkh.fr2.gui.filter.KkhOpenSessionInViewInterceptor"
      p:sessionFactory-ref="sessionFactory" />

      <!-- Konfiguration für Transaktionen -->
      <bean
      id="transactionManager"
      class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />

      <tx:advice
      id="transactionAdvice"
      transaction-manager="transactionManager">
      <tx:attributes>
      <tx:method
      name="*"
      propagation="REQUIRED"
      rollback-for="java.lang.Throwable" />
      </tx:attributes>
      </tx:advice>

      <!-- Die oben angegebenen Transaktionsregeln gelten für alle AFK-Klassen -->
      <aop:config>
      <aop:advisor
      advice-ref="transactionAdvice"
      pointcut="execution(public * de.kkh.comp..afk..AFKImpl.(..))" />
      </aop:config>

      <bean
      id="urlMapping"
      class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"
      p:alwaysUseFullPath="true">
      <property name="interceptors">
      <list>
      <ref bean="openSessionInViewInterceptor" />
      </list>
      </property>
      </bean>

      We already considered the information from Bug SPR-9480 in our configuration.
      As recommended, we set the property hibernate.transaction.factory_class to the CMTTransactionFactory and added the TransactionManager as a property of SessionFactory. Having done that, the autoflush works fine without the OpenSessionInViewInterceptor.

      The autoflush does not work with the OpenSessionInViewInterceptor

      Furthermore, we found the method preHandle of OpenSessionInViewInterceptor to be executed right at the beginning of a request. The method preHandle creates a new hibernateSession by calling the method openSession.
      This hibernateSession is bount to the thread by the TransactionSynchronizationManager. While creating the hibernateSession, the TransactionCoordinator creates a CMTTransaction-Object. At this point, no active transaction is available, therefore, the join status in CMTTransaction has been set to NOT_JOINED.

      Database access within a transaction
      According to our architecture, a transaction is being started by AOP in order to execute database access in the persistence tier. This transaction only begins after the execution of preHandle of OpenSessionInViewInterceptor.
      The join status of the CMTTransaction that was set in preHandle, stays the same within the actual transaction. Therefore, it looks like the preHandle method corrupts the transactional behavior. Consequently, the autoflush is not being executed later on.
      When performing the method query2.list (), we expect the method autoFlushIfRequired to recognize that it is running within a transaction.
      Unfortunately, the method isTransactionInProgress returns FALSE! As mentioned before, the cause is the join status to be NOT_JOINED.

      protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException {
      errorIfClosed();
      if ( ! isTransactionInProgress() )

      { // do not auto-flush while outside a transaction return false; }

      AutoFlushEvent event = new AutoFlushEvent( querySpaces, this );
      for ( AutoFlushEventListener listener : listeners( EventType.AUTO_FLUSH ) )

      { listener.onAutoFlush( event ); }

      return event.isFlushRequired();
      }

      The OpenSessionInViewFilter shows the incorrect behavior as well.

      In the test case without the OpenSessionInViewInterceptor the hibernateSession is being created in the currentSession method immediately before the first database access. At this point, a transaction by AOP has already started, therefore the join status of CMTTransaction object is set to JOINED. This leads to the autoflush, which is the expected behavior.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              juergen.hoeller Juergen Hoeller
              Reporter:
              Christof_Patzwald Christof Patzwald
              Last updater:
              Spring Issues Spring Issues
              Votes:
              2 Vote for this issue
              Watchers:
              5 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Days since last comment:
                3 years, 23 weeks, 6 days ago