Alex Rivera | Logout

UnexpectedRollbackException - a full scenario analysis

Asked 2010-01-05T15:42:46.883
30

All I know about this exception is from Spring's documentation and some forum posts with frostrated developers pasting huge stack traces, and no replies.

From Spring's documentation:

Thrown when an attempt to commit a transaction resulted in an unexpected rollback

I want to understand once and for all

  1. Exactly what causes it?

    • Where did the rollback occur? in the App Server code or in the Database?
    • Was it caused due to a specific underlying exception (e.g. something from java.sql.*)?
    • Is it related to Hibernate? Is it related to Spring Transaction Manager (non JTA in my case)?
  2. How to avoid it? is there any best practice to avoid it?

  3. How to debug it? it seems to be hard to reproduce, any proven ways to troubleshoot it?
Edit
Report

3 Answers

24

I found this to be answering the rest of question: https://jira.springsource.org/browse/SPR-3452

I guess we need to differentiate between 'logical' transaction scopes and 'physical' transactions here...

What PROPAGATION_REQUIRED creates is a logical transaction scope for each method that it gets applied to. Each such logical transaction scope can individually decide on rollback-only status, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, they will be mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction's chance to actually commit. However, since the outer transaction scope did not decide on a rollback itself, the rollback (silently triggered by the inner transaction scope) comes unexpected at that level - which is why an UnexpectedRollbackException gets thrown.

PROPAGATION_REQUIRES_NEW, in contrast, uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions will be different and hence can commit or rollback independently, with an outer transaction not affected by an inner transaction's rollback status.

PROPAGATION_NESTED is different again in that it uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back. This is typically mapped onto JDBC savepoints, so will only work with JDBC resource transactions (Spring's DataSourceTransactionManager).

answered 2010-01-09T10:07:15.603
10

Scroll a little more back in the log (or increase it's buffer-size) and you will see what exactly caused the exception.

If it happens not to be there, check the getMostSpecificCause() and getRootCause() methods of UnexpectedRollbackException- they might be useful.

answered 2010-01-05T15:52:27.353
5

Well I can tell you how to reproduce the UnexpectedRollbackException. I was working on my project, and I got this UnexpectedRollbackException in following situation. I am having controller, service and dao layers in my project. What I did is in my service layer class,

class SomeServiceClass {
    void outerTransaction() {
        // some lines of code
        innerTransaction();
        //AbstractPlatformTransactionManager tries to commit but UnexpectedRollbackException is thrown, not here but in controller layer class that uses SomeServiceClass.
    }

    void innerTransaction() {
        try {
            // someDaoMethod or someDaoOperation that throws exception
        } catch(Exception e) {
            // when exception is caught, transaction is rolled back, outer transaction does not know about it.
            // I got this point where inner transaction gets rolled back when I set HibernateTransactionManager.setFailEarlyOnGlobalRollbackOnly(true)
            // FYI : use of following second dao access is wrong, 
            try {
                // again some dao operation
            } catch(Exception e1) {
                throw e2;
            }
        }
    }
}
answered 2012-04-10T13:46:58.247

Your Answer