Spring transaction demarcation causes new Hibernate session despite use of OSIV
- by Kelly Ellis
I'm using Hibernate with OpenSessionInViewInterceptor so that a single Hibernate session will be used for the entire HTTP request (or so I wish). The problem is that Spring-configured transaction boundaries are causing a new session to be created, so I'm running into the following problem (pseudocode):
Start in method marked @Transactional(propagation = Propagation.SUPPORTS, readOnly = false)
Hibernate session #1 starts
Call DAO method to update object foo; foo gets loaded into session cache for session #1
Call another method to update foo.bar, this one is marked @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
Transaction demarcation causes suspension of current transaction synchronization, which temporarily unbinds the current Hibernate session
Hibernate session #2 starts since there's no currently-existing session
Update field bar on foo (loading foo into session cache #2); persist to DB
Transaction completes and method returns, session #1 resumes
Call yet another method to update another field on foo
Load foo from session cache #1, with old, incorrect value of bar
Update field foo.baz, persist foo to DB
foo.bar's old value overwrites the change we made in the previous step
Configuration looks like:
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor" autowire="byName">
    <property name="flushModeName">
        <value>FLUSH_AUTO</value>
    </property>
</bean>
<bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="myDataSource" />
</bean>
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="useTransactionAwareDataSource" value="true" />
    <property name="mappingLocations">
        <list>
            <value>/WEB-INF/xml/hibernate/content.hbm.xml</value>
        </list>
    </property>
    <property name="lobHandler">
        <ref local="oracleLobHandler" />
    </property>
    <!--property name="entityInterceptor" ref="auditLogInterceptor" /-->
    <property name="hibernateProperties"
        ref="HibernateProperties" />
    <property name="dataSource" ref="myDataSource" />
</bean>
I've done some debugging and figured out exactly where this is happening, here is the stack trace:
Daemon Thread [http-8080-1] (Suspended (entry into method doUnbindResource in TransactionSynchronizationManager))   
TransactionSynchronizationManager.doUnbindResource(Object) line: 222    
TransactionSynchronizationManager.unbindResource(Object) line: 200  
SpringSessionSynchronization.suspend() line: 115    
DataSourceTransactionManager(AbstractPlatformTransactionManager).doSuspendSynchronization() line: 620   
DataSourceTransactionManager(AbstractPlatformTransactionManager).suspend(Object) line: 549  
DataSourceTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 372    
TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(TransactionAttribute, String) line: 263   
TransactionInterceptor.invoke(MethodInvocation) line: 101   
ReflectiveMethodInvocation.proceed() line: 171  
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204   
$Proxy14.changeVisibility(Long, ContentStatusVO, ContentAuditData) line: not available  
I can't figure out why transaction boundaries (even "nested" ones - though here we're just moving from SUPPORTS to REQUIRED) would cause the Hibernate session to be suspended, even though OpenSessionInViewInterceptor is in use.
When the session is unbound, I see the following in my logs:
[2010-02-16 18:20:59,150] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager Removed value [org.springframework.orm.hibernate3.SessionHolder@7def534e] for key [org.hibernate.impl.SessionFactoryImpl@693f23a2] from thread [http-8080-1]