Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hibernate 4 Autoflush does not work with Spring OpenSessionInViewInterceptor [SPR-13848] #18421

Closed
spring-projects-issues opened this issue Jan 7, 2016 · 5 comments
Assignees

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Christof Patzwald opened SPR-13848 and commented

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 #14115 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.


Affects: 4.2 GA

Issue Links:

  • #14115 Hibernate 4 smart flushing does not work unless CMTTransactionFactory is being specified

2 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 7, 2016

Juergen Hoeller commented

So it sounds like this would affect any such early opened Sessions? All that OpenSessionInViewInterceptor really does is to open a request-scoped Session early, with transactions started and completed against that existing Session. I'm surprised that Hibernate's CMT transaction mechanism doesn't support this out of the box... Is there maybe an auto-join flag in Hibernate 4 that needs to be set for re-attempting a JTA join there?

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 11, 2016

Christof Patzwald commented

Hello Jürgen,
thank you for your quick reply!
We didn‘t find any join-properties in Hibernate 4.2. In our applications we just overwrite the method openSession() of OpenSessionInViewInterceptor to set the attribute flushMode to AUTO. In Spring 3.0.5 we did this by setting a property, but this is no longer available in Spring 4.2.

public class KkhOpenSessionInViewInterceptor extends OpenSessionInViewInterceptor {

@Override
public Session openSession() throws DataAccessResourceFailureException {
try {
Session session = getSessionFactory().openSession();
session.setFlushMode(FlushMode.AUTO);
return session;
} catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}

}

Now, after having searched for the properties available in Hibernate 4 once more, and after performing some tests with different properties, we found the property hibernate.current_session_context_class. We tested this property with jta and thread. This seems to connect the former session to the session within the transaction. Now the autoflush is being performed correctly when using the defined OpenSessionInViewInterceptor.
Unfortunately, with the termination of the transaction the hibernateSession seems to terminate as well (even the earlier session). Consequently, the OpenSessionInViewInterceptor has no session at it’s hand and reloading is no longer possible.
The result is that either the OpenSessionInViewInterceptor or autoflush works. But the combination oft he two just won’t work.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jan 11, 2016

Juergen Hoeller commented

So this indicates that Hibernate's out-of-the-box CurrentSessionContext implementations do allow for auto-joining correctly... The irony is that our default SpringSessionContext actually delegates to a subclass of Hibernate's own JTASessionContext named SpringJtaSessionContext as of our Hibernate 4 support, whereas we used to have our own impl for Hibernate 3. The only thing that our subclass really does is to switch to flush mode MANUAL for read-only transactions, so I'm surprised that auto-flush behaves differently there!

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Feb 13, 2018

Bjørn Hilstad commented

We have run into this problem when migrating an application to Spring 4.x + Hibernate 4.x.
We have been using the testapplication from #14115 to test this, but have added the OpenSessionInViewFilter to the hibernate4 application in that case. This provokes the autoflush problem discussed in this case.
We also tested the same application on Spring 5.x + Hibernate 5.x and Spring 4.x + Hibernate 5.x and in both cases the autoflushing works as it should.
So whatever has been done to make this work seems to have been done in Hibernate 5.x and the Hibernate documentation does mention that major changes has been made to the Transaction SPI.
So maybe this is not the right forum to be asking about this, but since this seems to be a Spring+Hibernate integration issue and you still have this open issue I will ask anyway .
Do you have any knowledge of how this has been resolved?

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Feb 13, 2018

Juergen Hoeller commented

I'm afraid I have no further insight here either. Hibernate 5 came with lots of internal refactoring, so I'm not surprised it works differently there.

Since Hibernate 4.x isn't supported by Red Hat anymore, we have no intentions to spend extra cycles on this from our side either. I strongly recommend an upgrade to Hibernate 5.1 or 5.2 at this point; even Hibernate 5.3 is just around the corner. With Spring Framework 5.0, we are more or less enforcing Hibernate 5.0+; with Spring Framework 4.3.x, we keep supporting Hibernate 4.2 and 4.3 but only on a best-effort basis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.