Our system is set up to automagically wrap each call coming into the service layer in a transaction (using AOP transaction advice), automatically committing the transaction if the call completes normally and automatically rolling back the transaction if an exception is thrown… a RuntimeException or Error, that is.
Rolling back on RuntimeException or Error but not on a (checked) Exception is Spring’s default behavior:
Note however that the 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.
Exactly which Exception types mark a transaction for rollback can be configured.
(from the Rolling Back section of the Transaction Management chapter of the Spring 2.5 Manual)
While we framework guys assumed that any exceptions thrown would extend RuntimeException, the application guys didn’t share our assumption. Their assumption was, “If an exception is thrown, the transaction rolls back.” So the question was, should the transaction advice be changed to also apply to checked exceptions, or should the application-level exceptions be changed to extend RuntimeException?
Seems Ok to Change from Spring’s Default
I thought perhaps it was for some reason a bad idea to change this default rollback behavior — that perhaps there was an important reason Spring did it this way. But a quick look at the Spring manual told me otherwise:
While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.
(from the Declarative Transaction Management section of the Spring 2.5 Manual)
Changing our Advice
There being no blockage from that quarter and not being able to think of a good reason why we’d want to commit our transactions when a checked exception was thrown, we changed our transactional advice bean from this:
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice>
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" rollback-for="Exception"/> </tx:attributes> </tx:advice>
And, after some fits and starts, the integration test showed that transactions were rolling back for RuntimeException, Error, and Exception. (Why the fits and starts? That explanation will have to wait till next time.)