我在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)
我该如何处理?
答案 0 :(得分:1)
这是一个非常常见的问题,因此此答案基于我在博客上写的关于
doWork
和doReturningWork
方法的this article。
问题是大多数JDBC驱动程序实际上并不支持嵌套事务。在您的情况下,您正在使用JBoss,它提供JTA事务管理器,但仍依赖JDBC提供程序提供的XA ResoureManager
,如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;
});
}
有关Hibernate
doWork
和doReturningWork
方法如何工作的更多详细信息,请查看this article。
答案 1 :(得分:0)
The initial java.sql.SQLException: IJ031040
seems related to a specific outcome during our import. It later has been replaced with another java.sql.SQLException
that forbid the rollback for managed transactions. But I could finally solve the problem by issuing native SQL statements:
// 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();
}
});
This allows a "nested Transaction" within the larger one and solved my problems.