【问题标题】:How to use Hibernate Session.doWork(...) for savepoints / nested transactions?如何将 Hibernate Session.doWork(...) 用于保存点/嵌套事务?
【发布时间】:2018-01-22 04:03:26
【问题描述】:

我在 Oracle DB 和 Hibernate 中使用 JavaEE/JPA 托管事务,需要实现某种嵌套事务。据我所知,开箱即用不支持这种东西,但我应该能够为此目的使用保存点。

按照https://stackoverflow.com/a/7626387/173689 的建议,我尝试了以下方法:

@Transactional(value = TxType.REQUIRES_NEW)
public boolean doImport(Import importer, Row row) throws ImportRowFailedException {
    // stripped code ...
    // We need to try different possibilities from which one may succeed...
    // ...former failures must be rolled back!
    for (Possibility poss : importer.getPossibilities()) {
        if (this.tryPossibility(poss, row)) break;
    }
    // stripped code ...
}

public boolean tryPossibility(Possibility possibility, Row row) {
    try {
        Session session = this.em.unwrap(Session.class);
        session.doWork(new Work() {
            @Override
            public void execute(Connection connection) throws SQLException {
                Savepoint before = connection.setSavepoint();
                if (!possibility.importRow(row)) {
                    connection.rollback(before);
                    throw new ImportRowFailedException();
                }
            }
        });
    }
    catch (ImportRowFailedException ex) {
        return false;
    }
    return true;
}

connection.rollback(before) 我得到以下异常:

Caused by: java.sql.SQLException: IJ031040: Connection is not associated with a managed connection: org.jboss.jca.adapters.jdbc.jdk8.WrappedConnectionJDK8@40a6a460
    at org.jboss.jca.adapters.jdbc.WrappedConnection.lock(WrappedConnection.java:164)
    at org.jboss.jca.adapters.jdbc.WrappedConnection.rollback(WrappedConnection.java:883)

我该如何处理?

【问题讨论】:

    标签: java oracle hibernate transactions savepoints


    【解决方案1】:

    最初的java.sql.SQLException: IJ031040 似乎与我们导入期间的特定结果有关。它后来被另一个java.sql.SQLException 取代,该java.sql.SQLException 禁止回滚托管事务。但我终于可以通过发出原生 SQL 语句来解决问题了:

    // Mark the current state as SAVEPOINT...
    Session session = this.em.unwrap(Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            connection.prepareStatement("SAVEPOINT TRY_POSSIBILITY").executeUpdate();
        }
    });
    
    // 
    // Do all the risky changes... verify... decide...
    // 
    
    // Rollback to SAVEPOINT if necessary!
    session.doWork(new Work() {
        @Override
        public void execute(Connection connection) throws SQLException {
            connection.prepareStatement("ROLLBACK TO SAVEPOINT TRY_POSSIBILITY").executeUpdate();
        }
    });
    

    这允许在较大的交易中进行“嵌套交易”并解决了我的问题。

    【讨论】:

      【解决方案2】:

      问题在于大多数 JDBC 驱动程序并不真正支持嵌套事务。在您的情况下,您正在使用 JBoss,它提供 JTA 事务管理器,但仍依赖 JDBC 提供程序提供的 XA ResoureManagers,如 this forum thread 中所述。

      因此,您最好使用doReturningWork 而不是doWork 并执行 SQL SAVEPOINT 命令,如下所示:

      public boolean tryPossibility(Possibility possibility, Row row) {
          Session session = this.em.unwrap(Session.class);
          
          return session.doReturningWork(connection -> {
              connection.prepareStatement("SAVEPOINT IMPORT_ROW").executeUpdate();
              
              boolean importHasSuceed = possibility.importRow(row);
              
              if (!importHasSuceed) {
                  connection.prepareStatement("ROLLBACK TO SAVEPOINT IMPORT_ROW").executeUpdate();
              }
              
              return importHasSuceed;
          });
      }
      

      【讨论】:

        猜你喜欢
        • 2013-12-10
        • 1970-01-01
        • 2020-02-22
        • 1970-01-01
        • 2011-11-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-17
        • 1970-01-01
        相关资源
        最近更新 更多