Разграничение транзакций Spring вызывает новый сеанс Hibernate, несмотря на использование OSIV - PullRequest
2 голосов
/ 16 февраля 2010

Я использую 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]

Ответы [ 2 ]

2 голосов
/ 16 февраля 2010

Во-первых, вашему openSessionInViewInterceptor необходимо ввести sessionFactory, в противном случае он не сможет выполнить свою работу:

<property name="sessionFactory">
    <ref bean="sessionFactory" />
</property>

Также есть свойство, называемое singleSession - по умолчанию оно true, но на всякий случай отлаживает его значение.

Затем, если вы используете Spring-MVC, вам нужно настроить перехватчик для SimpleUrlHandlerMapping (или того, что вы используете), чтобы он мог быть применен:

<property name="interceptors">
    <list>
       <ref bean="openSessionInViewInterceptor"/>
    </list>
</property> 

Если вы используете что-то еще, я думаю, вы должны определить это с помощью тегов <aop> (какой веб-фреймворк вы используете?)

0 голосов
/ 04 апреля 2010

У меня точно такая же проблема. Сначала я подумал, что границы транзакций БД привели к созданию сеансов гибернации. После небольшой отладки я понимаю, что на самом деле не понимаю их - или как они «должны» быть настроены.

Я использую Spring и сервис @Transactional с двумя связанными DAO. Я также использую распространение по умолчанию (ОБЯЗАТЕЛЬНО) по всем направлениям.

public class MyService {
 public MyPersonDao personDao;  // injected by spring
 public MyAddressDao addressDao; // injected by spring

 @Transactional
 public void create(Person p) {
  Address a = addressDao.findOrCreate(p.getAddressData());
  boolean inSession = personDao.getHibernateTemplate.contains(a);  // false

  p.setAddress(adressDao.create();

  personDao.store(p); // fails because a is transient
 }
}

Из того, что я вижу в своих журналах, похоже, что вызовы функций через транзакционные прокси открывают и закрывают сеансы гибернации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...