9

In the case of REQUIRED propagation when the caller method itself is transactionnal does the current method overrides enclosing transaction properties (for example rollbackFor) if they are different ?

illustration :

Class A {
    @Transactional(propagation = Propagation.REQUIRED,
        rollbackFor = { SomeException.class})
    void foo() {
        try {
           b.bar();
        } catch (OtherException e) {
           // is the transaction marked as rollback-only at this point ?
        }
    }
}

Class B {
    @Transactional(propagation = Propagation.REQUIRED,
        rollbackFor = { OtherException.class})
    void bar() {
        [...]
    }
}

edit :

Well, i'd like to avoid trivial out of scope answers, so let's be clear, I'am aware of spring propagation handling.

If you're not, below is the relevant part of the documentation, I just would like to clarify the first part regarding my example above :

PROPAGATION_REQUIRED

When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, all these scopes 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 (as you would expect it to).

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.

My question can be reworded as this :

Does the logical transaction scope holds the transaction properties ?

Gab
  • 7,869
  • 4
  • 37
  • 68
  • You can refer http://stackoverflow.com/questions/8490852/spring-transactional-isolation-propagation/32223597#32223597 for different propagation with multiple scenarios – NIrav Modi Oct 22 '15 at 09:43

3 Answers3

3

So, I set up a test case, the short answer is yes.

The transaction logical scope holds the transaction properties and its boundaries are indeed the annotated method ones.

So even if the underlying physical transaction is the same for both methods, the logical properties are proper to each method and the inner method can so force the rollback of the outer method transaction. If this last trigger a commit however it will lead to a UnexpectedRollbackException.

cf. Spring TransactionInterceptor (comments are mine)

try {
        retVal = invocation.proceed();
}
catch (Throwable ex) {
        completeTransactionAfterThrowing(txInfo, ex);
        throw ex;
}

completeTransactionAfterThrowing() :

// txinfo is proper to the invocation target method
if (txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }

AbstractPlatformTransactionManager.processRollback() :

else if (status.isNewTransaction()) { //requiresnew
    doRollback(status);
}
else if (status.hasTransaction()) { //requiered
        [...]
        doSetRollbackOnly(status);
    }
}
Gab
  • 7,869
  • 4
  • 37
  • 68
  • NOTA : if you use a debugger do not trust inner variable state, call explicitely the getter TransactionAspectSupport.currentTransactionStatus().isRollbackOnly() – Gab Oct 27 '15 at 14:01
  • immagine that you should do something else after catch, your throwable way is inconvenient – Tiago Medici May 18 '20 at 12:28
0

In my understanding of the specifications, I would say in this example :

Class A {
    @Transactional(propagation = Propagation.REQUIRED,
        rollbackFor = { SomeException.class})
    void foo() {
        try {
           b.bar();
        } catch (OtherException e) {
           // the transaction is marked as rollback-only by the inner call as it thrown an OtherException
           // XXX --- or not if inner logical scope does not handle overridden property 'rollbackFor' ? ---
           // anyway, we avoid UnexpectedRollbackException by enforcing physical rollback to outter scope programmatically, by throwing :
           throw new SomeExeption(e);
        }
    }
}

Class B {
    @Transactional(propagation = Propagation.REQUIRED,
        rollbackFor = { OtherException.class})
    void bar() {
        [...]
    }
}

So we can reformulate the question as : does overridden "rollbackFor" property is handled by inner logical transaction scope management ?

By the way, what is the exact transaction manager class and version you use ?

Donatello
  • 3,486
  • 3
  • 32
  • 38
-1

Refer Section 16.5.7 of spring documentation. Even though inner methods are annotate with REQUIRED when it is called within in transaction context, it will mapped to same physical transaction.