Here is the problem I got in an application I have to maintain:
I have a first class with the annotation @Transactional(rollbackFor = CustomExceptionA.class)
Then in the following code I call a method of @Transactional(noRollbackFor = CustomExceptionB.class)
NB : CustomExceptionA
or CustomExceptionB
have only one common ancestor which is Exception
.
And of course, when I execute the code an exception is raised which is neither of type CustomExceptionA
or CustomExceptionB
nor does it subclasses them.
So the question is simple:
What happens to the transaction in that case? Does it commit? Does it rollback? Does it hold on an unfinished state waiting for the application to do something (which is actually an answer that might explain some ugly things seen in this application)? and moreover: why?
The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.
This is because @Transactional does not work when calling a method from within the same class. It would work if you would call method2() from another class. In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted.
@Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.
You certainly can place the @Transactional annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies.
Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. (Errors will also - by default - result in a rollback.) Checked exceptions that are thrown from a transactional method will not result in the transaction being rolled back.
Why? That makes a perfect sense: checked exceptions are mandatory for handling or throwing out, so if you're throwing the checked exception out of the transactional method, then framework supposes you know what you're doing. In case of unchecled exceptionit's likely to be a bug, or exception handling flaw, so transaction is rolled back to avoid data corruption.
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