“没有绑定到线程的休眠会话”,事务方法中断下一个事务的执行

Posted

技术标签:

【中文标题】“没有绑定到线程的休眠会话”,事务方法中断下一个事务的执行【英文标题】:"No Hibernate Session bound to thread", transactional method breaks execution of the next transaction 【发布时间】:2011-09-19 13:08:46 【问题描述】:

我的问题隐藏在课堂上,有两种方法。

@Repository
@Transactional(isolation = Isolation.READ_COMMITTED)
public class RetentionController 
    @Autowired
    private SessionFactory sessionFactory;
        ...

    @Transactional(readOnly = true)
    public BaseResource getResource(String pPath)throws ResourceNotFoundException
        ...
        Criteria critDirExists = sessionFactory.getCurrentSession().createCriteria(Directory.class);
        critDirExists.add(Restrictions.eq(PATH, pPath));
        Directory dir = (Directory) critDirExists.uniqueResult();

        ...processing results...
    

    @Transactional(readOnly = false)
    public void addDirectory(String pPath) 

        modelValidator.validateDirectoryURI(pPath);

        //check if such exists
        Criteria critExists = sessionFactory.getCurrentSession().createCriteria(Directory.class);
        critExists.add(Restrictions.eq("path", pPath));
        ...
     

...

SessionFactory 被正确注入 - 第一个方法正常执行。问题是: addDirectory 在“Criteria critExists = sessionFactory.getCurrentSession().createCriteria(Directory.class);”处抛出“No Hibernate Session bound to thread”,当它在“getResource()”方法之后被调用时。

日志(我添加了标记以查看方法的开始和结束位置:GET RESOURCE: START/END, ADD DIRECTORY: START/END):

  DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'transactionManager'
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Creating new transaction with name [com.asg.tciclientadapters.webdav.dal.RetentionController.getResource]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly; ''
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Opened new Session [org.hibernate.impl.SessionImpl@1be3bb2] for Hibernate transaction
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@1be3bb2]
  DEBUG [org.springframework.jdbc.datasource.DataSourceUtils] - Setting JDBC Connection [jdbc:sqlserver://******\****:****;selectMethod=direct;lastUpdateCount=true;, UserName=*********, Microsoft SQL Server 2005 JDBC Driver] read-only
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Exposing Hibernate transaction as JDBC transaction [jdbc:sqlserver://******\****:****;selectMethod=direct;lastUpdateCount=true;, UserName=*********, Microsoft SQL Server 2005 JDBC Driver]
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@13b8f62] for key [org.apache.commons.dbcp.BasicDataSource@115512] to thread [http-9080-1]
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Bound value [org.springframework.orm.hibernate3.SessionHolder@299629] for key [org.hibernate.impl.SessionFactoryImpl@1429498] to thread [http-9080-1]
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Initializing transaction synchronization
  TRACE [org.springframework.transaction.interceptor.TransactionInterceptor] - Getting transaction for [com.asg.tciclientadapters.webdav.dal.RetentionController.getResource]
  DEBUG [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Returning cached instance of singleton bean 'com.asg.tciclientadapters.webdav.aspect.TraceLog#0'
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - ---GET RESOURCE: START
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@299629] for key [org.hibernate.impl.SessionFactoryImpl@1429498] bound to thread [http-9080-1]
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - Getting ILM object: /CADPWebDAV
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - RequestType is MKCOL
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - EmbryoResource was created.
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@299629] for key [org.hibernate.impl.SessionFactoryImpl@1429498] bound to thread [http-9080-1]
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - ---GET RESOURCE: END
  TRACE [com.asg.tciclientadapters.webdav.aspect.TraceLog] - RetentionController.getResource(..) Executed at: 78 ms
  TRACE [com.asg.tciclientadapters.webdav.aspect.TraceLog] - Method Argument [1]: com.asg.tciclientadapters.webdav.servlet.CADPRequest@10241ae
  TRACE [com.asg.tciclientadapters.webdav.aspect.TraceLog] - Method Argument [2]: com.asg.tciclientadapters.webdav.tci.TCIWrapper@1ca203
  TRACE [org.springframework.transaction.interceptor.TransactionInterceptor] - Completing transaction for [com.asg.tciclientadapters.webdav.dal.RetentionController.getResource]
  TRACE [org.springframework.orm.hibernate3.HibernateTransactionManager] - Triggering beforeCommit synchronization
  TRACE [org.springframework.orm.hibernate3.HibernateTransactionManager] - Triggering beforeCompletion synchronization
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Initiating transaction commit
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@1be3bb2]
  TRACE [org.springframework.orm.hibernate3.HibernateTransactionManager] - Triggering afterCommit synchronization
  TRACE [org.springframework.orm.hibernate3.HibernateTransactionManager] - Triggering afterCompletion synchronization
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Clearing transaction synchronization
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Removed value [org.springframework.orm.hibernate3.SessionHolder@299629] for key [org.hibernate.impl.SessionFactoryImpl@1429498] from thread [http-9080-1]
  TRACE [org.springframework.transaction.support.TransactionSynchronizationManager] - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@13b8f62] for key [org.apache.commons.dbcp.BasicDataSource@115512] from thread [http-9080-1]
  DEBUG [org.springframework.orm.hibernate3.HibernateTransactionManager] - Closing Hibernate Session [org.hibernate.impl.SessionImpl@1be3bb2] after transaction
  DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - ---ADD DIRECTORY: START
  DEBUG [com.asg.tciclientadapters.webdav.dal.RetentionController] - Validation: OK
  DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Opening Hibernate Session
  DEBUG [org.springframework.orm.hibernate3.SessionFactoryUtils] - Closing Hibernate Session
  ERROR [com.asg.tciclientadapters.webdav.servlet.CADPServlet] - org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

Spring配置xml文件:

<context:property-placeholder location="classpath:spring.properties"/>

<context:annotation-config/>
<tx:annotation-driven/>
<aop:aspectj-autoproxy/>
<context:mbean-export/>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
      p:driverClassName="$jdbc.driverClassName"
      p:url="$jdbc.url"
      p:username="$jdbc.username"
      p:password="$jdbc.password"/>

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
      p:dataSource-ref="dataSource" p:mappingResources="CADPWebDAV_hbm.xml">
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">$hibernate.dialect</prop>
            <prop key="hibernate.show_sql">$hibernate.show_sql</prop>
            <prop key="hibernate.generate_statistics">$hibernate.generate_statistics</prop>
            <prop key="hibernate.cache.use_structured_entries">true</prop>
        </props>
    </property>
    <property name="eventListeners">
        <map>
            <entry key="merge">
                <bean class="org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener"/>
            </entry>
        </map>
    </property>
</bean>'

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
      p:sessionFactory-ref="sessionFactory"/>

这里是异常的 StackTrace:

[com.asg.tciclientadapters.webdav.servlet.CADPServlet] - org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

[com.asg.tciclientadapters.webdav.servlet.CADPServlet] - org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
[com.asg.tciclientadapters.webdav.servlet.CADPServlet] - org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:687)
[com.asg.tciclientadapters.webdav.servlet.CADPServlet] - com.asg.tciclientadapters.webdav.dal.RetentionController.addDirectory(RetentionController.java:189)
...

【问题讨论】:

您能否添加堆栈跟踪的相关部分,表明异常源自 addDirectory 方法,从日志中看不出它来自 addDirectory 方法。日志与 servlet 相关联。 我添加了抛出异常的 StackTrace - 它肯定是 'addDirectory()'。 你能检查一下你是否在从 spring 检索的对象(context.getBean 或 @Autowired)上调用 addDirectory 方法。从日志看来,addDirectory 调用正在绕过事务代理 - 可能发生的一个原因是该对象不是 Spring bean。您可以设置断点并进行调试 - 检查对象是否真的是代理,如果是,为什么它没有开始新事务。 是的,有 bean:&lt;bean id="decisionsRep" class="com.asg.tciclientadapters.webdav.dal.RetentionController"/&gt; 并且所有类都通过 @autowired 字段或构造函数参数接收 RetentionController(这导致类,它是由 @autowired 获得的) 【参考方案1】:

解决方案是 - 添加到 web.xml:

<filter>
  <filter-name>hibernateFilter</filter-name>
  <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>


<filter-mapping>
  <filter-name>hibernateFilter</filter-name>
  <url-pattern>/*</url-pattern> 
</filter-mapping>

在那个异常消失之后。

【讨论】:

【参考方案2】:

我认为您需要将 addDirectory 方法的 @Transactional 设置更改为以下内容:

@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)

根据我对 Spring/Hibernate 中事务管理过程如何工作的理解,您当前的设置告诉 hibernate 您将以只读方式执行所有内容,因此当您尝试以非只读方式执行时它爆炸了。我在以下位置找到了支持文档:http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#tx-propagation

我假设如果您在 getResource 之前调用 addDirectory 方法,那么您的代码可以正常工作。

【讨论】:

谢谢回复,但是这些方法是被另一个类调用的,所以事务没有传播。我认为它可能在第一种方法之后仍未完成(因此试图在返回结果之前刷新它),但这与 @transactional 注释的本质相矛盾。另外我不得不提到 - 我使用 Spring3.0 很抱歉将您引导至旧文档...我已经更新了我的原始帖子,以便它转到 3.0 版本而不是 2.0。不过我没有任何额外的建议,当你弄清楚时,我希望你能发布解决方案。【参考方案3】:

如果 Hibernate 的版本在类属性中不完全一致,就会发生这种情况。

transactionManager 类 org.springframework.orm.hibernateX 必须匹配 SessionFactory 类 org.springframework.orm.hibernateX 版本,也需要支持您在 Maven 依赖项中使用的版本。

【讨论】:

以上是关于“没有绑定到线程的休眠会话”,事务方法中断下一个事务的执行的主要内容,如果未能解决你的问题,请参考以下文章

分布式事务那些事

浅谈分布式事务那些事

mysql事务基本概念

Verilog基础计算机体系架构CPU发生中断要做哪些事?什么是中断向量表?(面试常问)

关于事务的那些事

数据库事务未提交网络中断,锁表了,怎么办