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]