尽管使用OSIV,Spring事务划分会导致新的Hibernate会话

时间:2010-02-16 07:05:54

标签: java hibernate spring session transactions

我正在使用Hibernate和OpenSessionInViewInterceptor,因此单个Hibernate会话将用于整个HTTP请求(或者我希望如此)。问题是Spring配置的事务边界导致创建一个新会话,所以我遇到了以下问题(伪代码):

  • 从标记为@Transactional的方法开始(propagation = Propagation.SUPPORTS,readOnly = false)
  • Hibernate会话#1启动
  • 调用DAO方法更新对象foo; foo被加载到会话#1的会话缓存中
  • 调用另一个方法来更新foo.bar,这个方法标记为@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    • 事务划分导致当前事务同步暂停,暂时解除当前Hibernate会话的绑定
    • Hibernate会话#2启动,因为当前没有会话
    • 更新foo上的字段栏(将foo加载到会话缓存#2);坚持到DB
    • 交易完成,方法返回,会话#1恢复
  • 调用另一种方法来更新foo上的另一个字段
    • 从会话缓存#1加载foo,旧,条形值不正确
    • 更新字段foo.baz,将foo持久保存到DB
    • 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 个答案:

答案 0 :(得分:2)

首先,您的openSessionInViewInterceptor必须注明sessionFactory,否则无法完成任务:

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

此外,还有一个名为singleSession的属性 - 默认情况下为true,但调整其值以防万一。

然后,如果使用Spring-MVC,则必须为SimpleUrlHandlerMapping(或者您使用的任何一个)配置拦截器,以便可以实际应用它:

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

如果使用其他任何内容,我认为您必须使用<aop>标记来定义它(您使用的是哪种Web框架?)

答案 1 :(得分:0)

我有同样的问题。我首先想到数据库事务边界驱动了hibernate会话的创建。经过一些调试后,我意识到我并不真正了解它们 - 或者它们应该如何“设置”。

我正在使用spring和一个带有两个相关DAO的@Transactional服务。我也在全面使用默认传播(REQUIRED)。

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
 }
}

从我在日志中看到的,看起来通过事务代理的函数调用似乎打开和关闭了hibernate会话。