Apr 11th, 2009, 06:10 PM
get @Transactional to rollback only at the cutpoint where the transaction was created
I'm using aop @Transactional (aspectj in fact) to demarcate my transactions. Spring checks for an exception and if there is one, rolls back the transaction, at every single @Transactional method in the call chain. Even for Propagation.SUPPORTED or Propagation.MANDATORY. In other words, the transaction is rolled back as early as possible. The behavior I'd prefer is that it only does the rollback at the point in the call-chain where the transaction was created.
Naturally I can accomplish this in a rather static way by adding "noRollbackFor=Throwable.class" on every @Transactional annotation that wouldn't create a new transaction ( SUPPORTS, MANDATORY. ) Thats my current solution But I'd like something more dynamic. I might have Propagation.REQUIRED nested, and only the outter one needs to rollback the transaction.
AOP created the transaction in a proxy and is wrapping the rest of the call chain in that call. Surely it knows it created the transaction there and can do the rollback only there, as a result.
Any suggestions on how I could implement this? A new transaction manager? Some clever AOP XML.
I use @Transactional rather heavily. Its great for marking your methods as 'should be transacted' or 'should never be in a transacted'. And at the same time marking precisely where the transaction boundaries should be in some cases, or letting spring decide automatically it in others. With this I don't need to have a complete transactional model for the entire code base in mind at once. I can rely on each part playing nicely with each other.
But springs transaction behavior and defaults are highly unintuitive. They don't seem modeled after an abstracted version of how programmatic transaction management is usually done.
I usually rollback on any exception, checked or otherwise. I'd never commit broadly for all checked exceptions, and then rollback only for unchecked. I'd only commit on an exception, if its a specific exception which that specific method is going commit on. Which, at that, is quite rare.
I usually build specific transaction boundaries. Where a transaction is created and rolled back at a specific place in the call-chain, and only if an exception bubbled up to _that point_ in the call chain. After all, every child call is going to want to handle their own exceptions in their own way. And I would _never_ check for an exception at _every single_ method in the call chain and rollback the transaction immediately. In between the occurrence of the exception, and the point where the transaction was created, there might be some business logical that handles that exception and can continue with the transaction.
Of course this is all moot if you only use @Transaction at a few places at the entry points in your service layer. But if your using quite heavily all through your code for its intrinsic value, it breaks rather terribly.
Apr 16th, 2009, 02:46 AM
So the idea is to get the specific proxy to remember that _it_ created the transaction, and roll it back only there, in that returning method call. instead of having every proxied method call in every proxy check for an existing transaction and roll it back. Even though none of those other guys actually created the transaction. i.e. only it can only get destructured where it was created.