Welcome

TransactionTemplate and Rollback Rules

When I was playing with TransactionTemplate during one of my Spring training sessions, I promptly suggested audience with trying to change default rollback rules while using TransactionTemplate. After all TransactionTemplate encapsulates boilerplate transaction begin, commit/rollback statements and we only give business logic part which it executes inside that begin…commit/rollback block. Because of this, I’ve thought so far that TransactionTemplate’s behaviour would be same with declarative transaction management’s. In other words it would rollback if exception is unchecked, otherwise commit if exception is checked, and it would be possible to change this default behaviour. Therefore, I implemented a code very similar to following, and expected to see commit even though code throws RuntimeException.

RuleBasedTransactionAttribute rules = new RuleBasedTransactionAttribute();
rules.getRollbackRules().add(new NoRollbackRuleAttribute(RuntimeException.class));

TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager,rules);

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

    @Override
    protected void doInTransactionWithoutResult(TransactionStatus status) {

        if(true) throw new RuntimeException();

    }
});

Unfortunately, I was plain wrong! TransactionTemplate continued to rollback without taking my custom no-rollback rule defnition into account. I was surprised and looked into TransactionTemplate’s source code, and saw following code block;

public  T execute(TransactionCallback action) throws TransactionException {
    if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
        return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
    }
    else {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result;
        try {
            result = action.doInTransaction(status);
        } catch (RuntimeException ex) {
            // Transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        } catch (Error err) {
            // Transactional code threw error -> rollback
            rollbackOnException(status, err);
            throw err;
        } catch (Exception ex) {
            // Transactional code threw unexpected exception -> rollback
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
        }
        this.transactionManager.commit(status);
        return result;
    }
}

TransactionTemplate rollbacks on any exception without consulting RuleBasedTransactionAttribute at all. It was strange because after all it was expecting TransactionDefinition as constructor argument and I thought that it would be possible to give custom rollback rules instead of default one even though TransactionTemplate extends from DefaultTransactionDefinition. If you look at that constructor closely you will notice that all parameters of TransactionDefinition input argument is copied to TransactionTemplate except rollback rules. Therefore, they are simply ignored!

In summary, it appears that Spring guys probably assumed that it would be meaningless to deal with rollback rules if someone does programmatic transaction management. Programmer would decide on whether to commit or rollback on any exception after all. This would be meaningful if one uses PlatformTransactionManager for programmatic transaction management but TransactionTemplate is a bit different in my point of view. It hides common and repeating boilerplate code and it would be better this code had taken rollback rules into account when an exception occurred.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.