Finally, Rollback Your Transaction

2013-07-22 04:59UTC

Lately, I've been working with code that exhibits a fairly poor practice: programmatic transactions (namely Spring) don't always get committed or rolled-back, even though the code appears correct. Consider the following contrived scenario:

void doWork(final PlatformTransactionManager txm)
{
    final TransactionStatus tx = txm.getTransaction(new DefaultTransactionDefinition());
    try {
        // do some database work
        txm.commit(tx);
    } catch (final RuntimeException ex) {
        txm.rollback(tx);
        throw ex;
    }
}

While this isn't necessarily wrong in that it will usually work as expected, it's certainly not correct. If any invoked code, or the JVM itself, throws an Error or any other type of Throwable, the rollback branch will never get executed and the transaction will remain open. True, underlying infrastructure might clean on your behalf, but that's a dangerous assumption. Instead, an easier fix is to perform the rollback in a finally:

void doWork(final PlatformTransactionManager txm)
{
    final TransactionStatus tx = txm.getTransaction(new DefaultTransactionDefinition());
    try {
        // do some database work
        txm.commit(tx);
    } finally {
        if(!tx.isComplete()){
            txm.rollback(tx);
        }
    }
}

Now, regardless of what happens in the try, the transaction will be rolled-back if it remains open. This is general advice and not a mandate: sometimes the first example is exactly what you may need. Regardless, just be sure to document your assumptions.