外部事务失败时回滚内部事务

时间:2014-04-11 10:14:24

标签: sql orm transactions h2 ormlite

我正在使用ORMLite访问Java中的h2数据库。 要使用静态方法TransactionManager.callInTransaction执行交易。

如果是单个独立交易,这个工作正常。但是,如果事务彼此嵌套,则即使外部事务失败,内部事务也会被提交。

就像在这段伪代码中一样:

OuterDatabaseTransaction
{
  Loop
  {
    InnerDatabaseTransaction
    {
      //Multiple update or create statements
      //One of the InnerDatabaseTransactions throws a random exception
    }
    //Alternatively the OuterDatabaseTransaction throws a random
    //exception but all commited InnerDatabaseTransactions should rollback still
  }
}

所以我期望的是,如果任何内部事务失败,外部事务也会失败。如果外部事务失败,则没有任何内部事务被提交。 目前似乎每个内部事务都是单独提交的,并且不会与外部事务共享相同的Savepoint

更新(谢谢定义)

查看跟踪显示以下内容

[TRACE] JdbcDatabaseConnection connection is closed returned false
[TRACE] JdbcDatabaseConnection connection autoCommit is true
[TRACE] JdbcDatabaseConnection connection set autoCommit to false
[DEBUG] TransactionManager had to set auto-commit to false
[TRACE] JdbcDatabaseConnection save-point sp14: id=0 name=ORMLITE15 set with name ORMLITE15
[DEBUG] TransactionManager started savePoint transaction ORMLITE15
[TRACE] JdbcDatabaseConnection connection is closed returned false
[TRACE] JdbcDatabaseConnection connection autoCommit is false
[TRACE] JdbcDatabaseConnection save-point sp15: id=0 name=ORMLITE16 set with name ORMLITE16
[DEBUG] TransactionManager started savePoint transaction ORMLITE16
[TRACE] JdbcDatabaseConnection connection is closed returned false
[TRACE] JdbcDatabaseConnection update statement is prepared and executed returning 1: <...>
[DEBUG] BaseMappedStatement update data with statement <...> changed 1 rows
[TRACE] BaseMappedStatement update arguments: <...>
[TRACE] JdbcDatabaseConnection connection is committed for save-point ORMLITE16
[DEBUG] TransactionManager committed savePoint transaction ORMLITE16
-> [ERROR] TransactionManager after commit exception, rolling back to save-point also threw exception
[TRACE] JdbcDatabaseConnection connection set autoCommit to true
[DEBUG] TransactionManager restored auto-commit to true
[TRACE] JdbcDatabaseConnection connection is closed returned false

进入源代码会发现,在回滚 OuterDatabaseTransaction 期间,异常会在下面函数中的 org.h2.engine.Session.java 中的h2源中抛出。然而,背后的原因我还不了解。

private void checkCommitRollback() {
  if (commitOrRollbackDisabled && locks.size() > 0) {
    throw DbException.get(ErrorCode.COMMIT_ROLLBACK_NOT_ALLOWED);
  }
}

更新2

发布ORMLite Bug Report

2 个答案:

答案 0 :(得分:4)

要查找原因,您可以启用跟踪级别登录。工作嵌套事务应该生成如下日志:

TRACE: connection supports save points is true
TRACE: save-point <...> set with name ORMLITE1
DEBUG: started savePoint transaction ORMLITE1
...
TRACE: save-point <...> set with name ORMLITE2
DEBUG: started savePoint transaction ORMLITE2
...
TRACE: connection is committed for save-point ORMLITE2
...
TRACE: save-point <...> set with name ORMLITE3
DEBUG: started savePoint transaction ORMLITE3
...
TRACE: save-point ORMLITE3 is rolled back
...
TRACE: save-point ORMLITE1 is rolled back

在此示例中,ORMLITE1是外部事务的保存点,ORMLITE2和ORMLITE3是内部事务的保存点。第一个内部事务最初被提交,第二个事务被回滚到ORMLITE3并导致外部事务回滚到ORMLITE1,它反过来隐式取消了第一个内部事务。

但是如果你在日志中看到这个:

TRACE: connection supports save points is false

然后JDBC驱动程序不支持保存点,并且嵌套事务不起作用。从理论上讲,这不应该发生,因为H2状态支持保存点。

如果你看到这个:

ERROR: after commit exception, rolling back to save-point also threw exception

然后由于某种原因回滚到保存点失败。检查您对外部和内部交易使用相同的ConnectionSource

或者你我在日志中找到导致问题的其他内容。将日志附加到您的问题可能也会有所帮助。我建议用真正的Java代码替换伪代码。

更新

以下是您收到的错误的官方说明:

  

COMMIT_ROLLBACK_NOT_ALLOWED = 90058

     

尝试在触发器内调用提交或回滚时,或者在尝试调用隐式提交当前事务的触发器内的方法(如果对象被锁定)时,抛出代码90058的错误。这不是因为它会过早释放锁定。

链接:http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90058

这可能会帮助您进一步找到问题的原因。对我来说,很难说没有看到你的代码。祝你好运!

答案 1 :(得分:1)

你是对的, 正如Gray在评论中写的那样,TransactionManager最后提交每个事务(内部和异常事务的行为相同)。

问题在于TransactionManager.java:170,它甚至在内部事务之后提交事务。 但H2,Mysql,Oracle数据库不支持带保存点参数的sql COMMIT。

来自Mysql文档: &#34;如果执行COMMIT或没有命名保存点的ROLLBACK,则删除当前事务的所有保存点。&#34;

来自Oracle文档: &#34;简单的回滚或提交会删除所有保存点。回滚到保存点时,将删除在该保存点之后标记的所有保存点。您回滚的保存点仍然存在。&#34;

在OrmLite代码中也有这个评论:JdbcDatabaseConnection.java:94