Я использую Hibernate с OpenSessionInViewInterceptor, так что один сеанс Hibernate будет использоваться для всего HTTP-запроса (или мне так хочется). Проблема заключается в том, что настроенные Spring границы транзакций вызывают создание нового сеанса, поэтому я сталкиваюсь со следующей проблемой (псевдокод):
- Запуск в методе, помеченном @Transactional (распространение = распространение. SUPPORTS, readOnly = false)
- Сеанс Hibernate # 1 начинается
- вызов метода DAO для обновления объекта foo; foo загружается в кеш сессии для сессии # 1
- Вызовите другой метод для обновления foo.bar, этот помечается как @Transactional (распространение = распространение. REQUIRED, readOnly = false).
- Демаркация транзакции вызывает приостановку синхронизации текущей транзакции, что временно отменяет привязку текущего сеанса Hibernate
- Сеанс Hibernate # 2 начинается, так как в данный момент сеанса не существует
- Обновить панель полей на foo (загрузка foo в кэш сессии № 2); сохранить в БД
- Транзакция завершается и метод возвращается, сессия # 1 возобновляется
- Вызвать еще один метод для обновления другого поля в foo
- Загрузка foo из кеша сессии № 1, с old, неверное значение bar
- Обновить поле foo.baz, сохранить foo в БД
- Старое значение foo.bar перезаписывает изменения, которые мы сделали на предыдущем шаге
Конфигурация выглядит так:
<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>
Я провел некоторую отладку и выяснил, где именно это происходит, вот трассировка стека:
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
Я не могу понять, почему границы транзакций (даже «вложенные» - хотя здесь мы просто переходим от SUPPORTS к REQUIRED) приводят к приостановке сеанса Hibernate, даже если OpenSessionInViewInterceptor используется.
Когда сеанс не связан, в журналах я вижу следующее:
[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]