I am using ORMLite to access a h2 database in Java. To perform transactions i'm using the static method TransactionManager.callInTransaction.
In case of single independent transactions this works fine. However in case the transactions are nested within each other the inner transactions get committed even if the outer transaction fails.
As in this piece of pseudo-code:
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
}
}
So what I would expect is that if any of the inner transactions fails the outer transaction fails too. And if the outer transaction fails none of the inner transactions get committed. At the moment it seems that each of the inner transactions are committed individually and do not for example share the same Savepoint as the outer transaction.
UPDATE (Thanks dened)
Looking at the trace reveals the following
[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
Stepping into the source reveals that during rollback of OuterDatabaseTransaction the exception is thrown in the h2 source in org.h2.engine.Session.java within the function below. The reason behind that however I don't understand yet.
private void checkCommitRollback() {
if (commitOrRollbackDisabled && locks.size() > 0) {
throw DbException.get(ErrorCode.COMMIT_ROLLBACK_NOT_ALLOWED);
}
}
UPDATE 2
Posted ORMLite Bug Report
To find the cause you may turn trace level logging on. Working nested transactions should produce the log like this:
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
in this example ORMLITE1 is save point for outer transaction, ORMLITE2 and ORMLITE3 are for inner transactions. The first inner transaction is initially commited, the second transaction is rolled back to ORMLITE3 and caused rollback of the outer transaction to ORMLITE1, it in turn implicitly cancelled the first inner transaction.
But if you see this in the log:
TRACE: connection supports save points is false
then save points are not supported by the JDBC driver and nested transaction wouldn't work. Theoretically this shouldn't happen as H2 states support of save points.
If you see this:
ERROR: after commit exception, rolling back to save-point also threw exception
then rollback to a save point is failed for some reason. Check you use same ConnectionSource
for outer and inner transactions.
Or you my find in the log something else that caused the problem. Also it may be helpful to attach the log to your question. And I suggest to replace the pseudo-code with the real Java code.
Here is a official description of the error you got:
COMMIT_ROLLBACK_NOT_ALLOWED = 90058
The error with code 90058 is thrown when trying to call commit or rollback inside a trigger, or when trying to call a method inside a trigger that implicitly commits the current transaction, if an object is locked. This is not because it would release the lock too early.
Link: http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90058
May be this will help you further to find the cause of the problem. For me, it is hard to say more not seeing your code. Good luck!
You are correct, TransactionManager commits each transaction at the end (same behaviour for inner and ounter transaction) as Gray wrote in comments.
The problem is in TransactionManager.java:170, which commits transaction even after inner transaction. But H2, Mysql, Oracle databases do not support sql COMMIT with savepoint parameter.
From Mysql documentation: "All savepoints of the current transaction are deleted if you execute a COMMIT, or a ROLLBACK that does not name a savepoint."
From Oracle documentation: "A simple rollback or commit erases all savepoints. When you roll back to a savepoint, any savepoints marked after that savepoint are erased. The savepoint to which you roll back remains."
There is also comment on this in OrmLite code : JdbcDatabaseConnection.java:94
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With