代码之家  ›  专栏  ›  技术社区  ›  M A

Hibernate5.2.x“事务已经激活”的怪异行为

  •  0
  • M A  · 技术社区  · 6 年前

    从Hibernate4.x迁移到Hibernate5的最新版本时,我遇到了一个事务管理方面的问题。

    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" . 这种行为似乎是在Hibernate5.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();
    

    2 回复  |  直到 6 年前
        1
  •  1
  •   aschoerk    6 年前

    我想它是按照JTA运作的。因此,您为openSession接收的连接预期将参与transactionmanagers已经启动的事务。

        2
  •  1
  •   M A    6 年前

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