Java 如何对保存点/嵌套事务使用Hibernate Session.doWork(…)?

Java 如何对保存点/嵌套事务使用Hibernate Session.doWork(…)?,java,oracle,hibernate,transactions,savepoints,Java,Oracle,Hibernate,Transactions,Savepoints,我将JavaEE/JPA管理的事务与Oracle DB和Hibernate一起使用,需要实现某种嵌套事务。据我所知,这种东西是不支持开箱即用的,但我应该能够为此目的使用保存点 根据建议,我尝试了以下方法: @Transactional(value = TxType.REQUIRES_NEW) public boolean doImport(Import importer, Row row) throws ImportRowFailedException { // stripped cod

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

根据建议,我尝试了以下方法:

@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;
}
连接时。回滚(之前)我得到以下异常:

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.sql.SQLException:IJ031040
似乎与导入过程中的特定结果有关。它后来被另一个禁止对托管事务回滚的
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();
    }
});

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

问题在于大多数JDBC驱动程序并不真正支持嵌套事务。在您的例子中,您使用的是JBoss,它提供了JTA事务管理器,但仍然依赖于JDBC提供商提供的XA
ResourceManager
s,如中所述

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

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;
    });
}