【问题标题】:Hibernate 5.2.x "Transaction already active" weird behaviorHibernate 5.2.x“事务已经激活”的奇怪行为
【发布时间】:2019-04-06 15:39:38
【问题描述】:

在从 Hibernate 4.x 迁移到最新的 Hibernate 5 版本时,我遇到了关于事务管理的问题。

在我的代码中,有一个事务管理器开始一个 JTA 事务,然后调用 Session.beginTransaction。以下是重现该问题的示例(该场景未使用 Spring 或任何其他容器管理的事务管理):

transactionManager.begin();
saveOrUpdate(entity1);
saveOrUpdate(entity2);
...
transactionManager.commit();

private void saveOrUpdate(SomeEntity entity) {
    try (Session session = sessionFactory.openSession()) {
        session.setFlushMode(FlushMode.AUTO);
        session.beginTransaction();   // throws IllegalStateException "Transaction already active"
        try {
            session.saveOrUpdate(entity);
            session.getTransaction().commit();
        } catch (Exception ex) {
            session.getTransaction().rollback();
            throw RuntimeException(ex);
        }
    }
}

这导致 IllegalStateException 与消息 "Transaction already active" 一起被抛出。这种行为似乎是在 Hibernate 5.2.0 (this is the commit) 中引入的。 以前,Hibernate 只是忽略了物理事务本身的开始,因为它知道存在封闭事务:它只是创建了一个包装器 JtaTransaction 并将 isInitiator 设置为 false。

这个异常是在org.hibernate.engine.transaction.internal.TransactionImpl中抛出的,具体是begin()方法:

@Override
public void begin() {
    if ( !session.isOpen() ) {
        throw new IllegalStateException( "Cannot begin Transaction on closed Session/EntityManager" );
    }

    if ( transactionDriverControl == null ) {
        transactionDriverControl = transactionCoordinator.getTransactionDriverControl();
    }

    // per-JPA
    if ( isActive() ) {   // *** This is the problematic part *** //
        throw new IllegalStateException( "Transaction already active" );
    }

    LOG.debug( "begin" );

    this.transactionDriverControl.begin();
}

这也与user manual 相矛盾,它的内容如下:

// Note: depending on the JtaPlatform used and some optional settings,
// the underlying transactions here will be controlled through either
// the JTA TransactionManager or UserTransaction

Session session = sessionFactory.openSession();
try {
    // Assuming a JTA transaction is not already active,
    // this call the TM/UT begin method.  If a JTA
    // transaction is already active, we remember that
    // the Transaction associated with the Session did
    // not "initiate" the JTA transaction and will later
    // nop-op the commit and rollback calls...
    session.getTransaction().begin();

这是 Hibernate 中的错误吗?在引发异常的代码中,“per-JPA”注释到底意味着什么?有没有办法恢复旧的行为?

【问题讨论】:

  • 你能把你使用交易的方法的所有代码贴出来吗?你有什么运行时环境?
  • @SimonMartinelli 感谢您的评论。我详细说明了代码。该环境是使用带有自定义 TM 的 Hibernate 的独立应用程序。
  • 好的,但是为什么在 transactionManager.begin() 之后调用 session.beginTransaction()?你已经有一个来自 TransactionManager 的事务,所以 Hibernate 在它抱怨时是正确的。
  • 其实代码是单元测试的一部分,开启和关闭Session的代码是在一个被重复调用的方法中(再次更新代码)。目标是验证所有调用是否重用由TransactionManager 维护的相同XA 连接。我知道这可能并不完全有意义,但我无法理解的是用户手册是如何解释它的,而当 JTA 事务已经处于活动状态时它实际上不起作用。
  • 我建议在 Hibernate JIRA hibernate.atlassian.net/secure/Dashboard.jspa提出问题

标签: java hibernate jpa transactions jta


【解决方案1】:

这实际上是我在https://hibernate.atlassian.net/browse/HHH-13076 中报告的“回归”,现在已修复 (here's the PR link)。

【讨论】:

    【解决方案2】:

    行为符合预期:您在事务管理器中开始了事务。 我认为它是根据 JTA 工作的。因此,您收到的 openSession 连接应该会参与到 transactionmanagers 已经开始的事务中。

    开始一个额外的事务必须导致这个异常。

    【讨论】:

    • 感谢您的回答。我也认为这是意料之中的。但是,它曾经在 Hibernate 4 中工作(实际上是在 Hibernate 5.2.0 之前),并且问题中引用的用户手册部分暗示我的代码应该可以工作(即session.beginTransaction() 知道它已经在开放交易)。我也找不到确认异常确实是预期的参考(例如在 JTA 中)。仅供参考,我在 Hibernate 的项目中插入了 hibernate.atlassian.net/browse/HHH-13076 来跟踪它。
    猜你喜欢
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    相关资源
    最近更新 更多