【问题标题】:Some transaction propagations not working with Spring/Hibernate 4一些事务传播不适用于 Spring/Hibernate 4
【发布时间】:2014-10-23 12:42:49
【问题描述】:

我正在将应用程序从 Hibernate 3.3 升级到 Hibernate 4.2。我们也在使用 Spring 3.1.3(我们目前无法/不会更新)。

我的一些单元测试现在失败了

org.hibernate.HibernateException: No Session found for current thread

在 SpringSessionContext 中。这不是 <tx:annotation-driven /> 在错误上下文中定义的问题,也不是缺少 CGLIB 库的情况。大多数测试确实有效,这意味着在大多数情况下,事务代理正在工作。

现在失败的情况似乎与使用 NOT_SUPPORTED、NEVER 和 SUPPORTED 传播类型有关。无论出于何种原因,SpringSessionContext 在这些情况下都不会创建会话。

我们的用例有时要求事务边界不与方法边界严格对齐,并且会话有时会比事务长。在 Spring 3/Hibernate 3 的情况下,会话上下文被绑定到本地线程,即使事务没有启动,对 SessionFactory.getCurrentSession() 的调用也会返回一个会话实例。这是我希望在 Hibernate 4 案例中仍然具有的行为。

有人知道解决方法吗?如果 Spring 拒绝在没有有效事务的情况下创建会话,则很难将会话边界与会话而不是事务对齐。 Session 及其持久性上下文不应绑定到打开的事务。

【问题讨论】:

  • 所提到的传播类型永远不会触发事务的创建或会话的查找。会话绑定到事务边界。如果这是否正确,已经进行了一些讨论,并且在该领域已经进行了工作,但仅适用于较新版本的 Spring。
  • 为什么不需要传播的工作?如果没有现有的交易,就会进行交易,或者使用现有的交易,这听起来像你想要的。
  • Propagation.REQUIRED 并不适用于所有情况。例如,我们有一个执行多个事务的方法。我们将方法上的事务边界设置为 NEVER 或 NOT_SUPPORTED 以确保调用此方法时没有启动事务。然后该方法使用平台事务管理器开始/提交事务。但是,即使在这种情况下 sessionFactory.getCurrentSession() 也会返回 null 并强制异常。这似乎是一个相当大的问题,因为 HibernateTransactionManager 被注入了相同的会话工厂。
  • 你确实意识到你正在设置一些东西,所以你不会有交易,然后抱怨你没有交易?我认为您需要此类事情需要新的(您可能必须将代码分解为单独的方法,以便每个方法都获得新的事务)。
  • 我不是在抱怨我没有交易,我在抱怨我没有会话。会话应该能够独立于事务存在,并且比事务存在更长的时间。 Hibernate 3 中使用的会话上下文绑定到本地线程,并且无论是否有已启动的事务,对 sessionFactory.getCurrentSession() 的调用都有效。这是我们所依赖的功能,也是我希望继续依赖的功能。

标签: spring hibernate transactions


【解决方案1】:

通过实现 CurrentSessionContext 来解决此问题,它是 SpringSessionContext 的包装器,并借用了一些使其进入 Spring Framwork 4+ 的代码更改:

public class ClassLoaderSpringSessionContext implements CurrentSessionContext {

    private final SessionFactoryImplementor sessionFactory;
    private final SpringSessionContext sessionContext;

    public ClassLoaderSpringSessionContext(final SessionFactoryImplementor sessionFactory) {
        this.sessionFactory = sessionFactory;  // This is actually some class loading logic that isn't important to this transaction problem.
        this.sessionContext = new SpringSessionContext(this.sessionFactory);
    }

    @Override
    public Session currentSession() throws HibernateException {
        try {
            return sessionContext.currentSession();
        } catch (HibernateException e) {
            if (TransactionSynchronizationManager.isSynchronizationActive()) {
                Session session = this.sessionFactory.openSession();
                if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    session.setFlushMode(FlushMode.MANUAL);
                }
                SessionHolder sessionHolder = new SessionHolder(session);
                TransactionSynchronizationManager
                        .registerSynchronization(new SpringSessionSynchronization(sessionHolder,
                            this.sessionFactory));
                TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
                sessionHolder.setSynchronizedWithTransaction(true);
                return session;
            } else {
                throw new HibernateException(
                        "Could not obtain transaction-synchronized Session for current thread");
            }
        }
    }
}

SpringSessionSynchronization 在 Spring 4 中是一个包私有类,所以我还必须将它的一个版本作为私有内部类拉到 ClassLoaderSpringSessionContext

希望这对其他人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 2017-11-22
    • 2014-06-22
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多