【问题标题】:Spring transaction demarcation causes new Hibernate session despite use of OSIV尽管使用了 OSIV,但 Spring 事务划分会导致新的 Hibernate 会话
【发布时间】:2011-01-17 06:54:18
【问题描述】:

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

  • 从标记为 @Transactional(propagation = Propagation.SUPPORTS, readOnly = false) 的方法开始
  • 休眠会话 #1 开始
  • 调用DAO方法更新对象foo; foo 被加载到会话 #1 的会话缓存中
  • 调用另一个方法更新foo.bar,这个标记为@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    • 事务划分导致当前事务同步暂停,这会暂时解除当前 Hibernate 会话的绑定
    • 由于没有当前存在的会话,休眠会话 #2 开始
    • 更新 foo 上的字段栏(将 foo 加载到会话缓存 #2);持久化到数据库
    • 事务完成,方法返回,会话 #1 恢复
  • 调用另一个方法来更新 foo 上的另一个字段
    • 从会话缓存 #1 加载 foo,bar 的值旧且不正确
    • 更新字段 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]

【问题讨论】:

    标签: java hibernate spring session transactions


    【解决方案1】:

    首先,您的openSessionInViewInterceptor 必须注入sessionFactory,否则无法完成工作:

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

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

    那么,如果使用Spring-MVC,你必须为SimpleUrlHandlerMapping(或者你使用的任何一个)配置拦截器,这样才能实际应用:

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

    如果使用其他任何东西,我认为你必须使用&lt;aop&gt; 标签来定义它(你使用什么网络框架?)

    【讨论】:

    • openSessionInViewInterceptor 上的 singleSession 属性默认为 true。我尝试将其切换为 false,从而解决了这个特殊问题;但是后来我遇到了许多关于将集合与多个会话相关联的错误。 sessionFactory bean 正在自动连接到 OSIVI byName。将其显式添加为属性对行为没有影响。拦截器被配置为用作我们的 ParameterizedUrlHandlerMapping 的一部分。我没有把它包括在内,因为它是定制的。但只要说拦截器确实被使用就足够了。
    【解决方案2】:

    我也有同样的问题。我最初认为 DB 事务边界推动了休眠会话的创建。经过一些调试后,我现在意识到我并不真正理解它们——或者它们是如何“应该”设置的。

    我正在使用带有两个关联 DAO 的 spring 和 @Transactional 服务。我还在全面使用默认传播(必需)。

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

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

    【讨论】:

    • 所以实际上你粘贴在这里的代码也有与问题相同的问题,你也没有提交解决方案?
    猜你喜欢
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多