我正在使用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
答案 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