I have the following weird spring transaction management script:
I have method A, which calls method B, which calls method C, each of which is in a different class. Methods B and C are wrapped in transactions. Both use PROPAGATION_REQUIRED, so while spring creates two logical transactions, there is one physical transaction in db.
Now, in method C, I throw a RuntimeException. This sets up the internal logical transaction as rollbackOnly and the physical transaction. In method B, I know about the possibility of an UnexpectedRollbackException, so I am not going to commit in normal mode. I catch an exception from C and I throw another RuntimeException.
I expect an external RuntimeException to cause a rollback of an external transaction. However, the actual behavior is as follows:
- An external transaction seems to be trying to capture, or at least check its status, and then throw an UnexpectedRollbackException, because the physical transaction has already been marked as rollbackOnly.
- Before throwing this exception, he prints another exception in the logs, stating that "Application exception is excluded from commit exception". So Caller A gets an UnexpectedRollbackException, not the exception that B throws.
I found a workaround for it, which is to actively set the external transaction as rollback only before throwing an exception
public ModelAndView methodB(HttpServletRequest req, HttpServletResponse resp) {
try{
other.methodC();
} catch (RuntimeException e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new RuntimeException ("outer exception");
}
return handleGetRequest(req, resp);
}
However, this workaround strongly links the code to api transactions, and I would like to avoid this. Any suggestions?
p.s.
. rollbackFor -