Maksim Gumerov
Maksim Gumerov

Reputation: 662

noRollbackFor not always working on unchecked exceptions (not a duplicate)

I've seen a lot of similar questions but they are usually just tangential to mine.

Here is my code.

@Override //class implements interface
//can also add "rollbackFor = OutOfMemoryError.class" here, to no avail
@Transactional(noRollbackFor = IllegalArgumentException.class)
public ReportTemplate getByCode(final String code)
  //throws IllegalArgumentException - see question text
{
    if(code == null) {
        throw new IllegalArgumentException("ss");
    }
}

Consumer (non-transactional) calls this method via autowired interface. Despite noRollbackFor the transaction (consisting of this method alone) gets rolled back. Ok I understand people who say "by default it's rollbackFor any unchecked, so since the default is not overridden, that rule still applies". But that should not be true, because that would mean noRollbackFor is useless :) We can try specify rollbackFor as in the comment above, in a hope it will cancel "default" rollbackFor - but this does not help: rollback still happens; looks like this setting is simply added to "default". Worst of all, I can't find a precise specification on how it should work, when noRollbackFor and rollbackFor are covering the same descendant exception (like in my case, RuntimeException vs IllegalArgumentException, who wins?).

Well, I was able to find but one official clarification and it says "the strongest matching rule wins" - but this seems to not be universally corrent, as in my case. Also, for some reason they are overly specific: they say no-rollback-for="InstrumentNotFoundException" means, literally, "any exception other than an InstrumentNotFoundException" would be rolled back (but what about InstrumentNotFoundException's descendants?) And anyway it's greatly outdated: 2.5.x, and newer documentation just says something like "rollbackFor lists exceptions to be rolled back, and noRollbackFor lists exceptions not to be", in a quite contradictory manner, I'd say.

What is even more interesting, the code above stops rolling back if I specify IllegalArgumentException in its "throws" clause. Yes, I don't have to, since its unchecked - but if I do, noRollbackFor finally seems to notice it and behave properly! Does anyone know what manual says this should happen? Or maybe it's a bug?

UPDATE Here are spring transaction logs (in my actual code YargReportTemplateDao$TemplateNotFoundException there is everywhere instead of IllegalArgumentException)

1) with "throws"

[qtp22373939-44] TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Completing transaction for [ru.it.p321.dao.YargReportTemplateDaoImpl.getByCode] after exception: ru.it.p321.dao.YargReportTemplateDaoImpl$1: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION
[pool-28-thread-1] DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
[qtp22373939-44] TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on ru.it.p321.dao.YargReportTemplateDaoImpl$1: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION
[qtp22373939-44] TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - Winning rollback rule is: NoRollbackRuleAttribute with pattern [ru.it.p321.dao.YargReportTemplateDao$TemplateNotFoundException]

2) without "throws

[qtp21176461-48] TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Completing transaction for [ru.it.p321.dao.YargReportTemplateDaoImpl.getByCode] after exception: org.springframework.dao.InvalidDataAccessApiUsageException: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION; nested exception is ru.it.p321.dao.YargReportTemplateDaoImpl$1: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION
[qtp21176461-48] TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on org.springframework.dao.InvalidDataAccessApiUsageException: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION; nested exception is ru.it.p321.dao.YargReportTemplateDaoImpl$1: Yarg template was not found for given code: ITORG_REJECT_NOTIFICATION
[qtp21176461-48] TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - Winning rollback rule is: null
[qtp21176461-48] TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - No relevant rollback rule found: applying default rules

Upvotes: 5

Views: 8848

Answers (2)

Namdev Walture
Namdev Walture

Reputation: 21

Create your won custom exception (Extending from exception ) and apply noRollBackFor you custom exception class

Upvotes: 0

M. Deinum
M. Deinum

Reputation: 125018

The behavior you see has nothing to do with transaction handling but with exception translation. By default when a class is annotated with @Repository Spring will register a PersistenceExceptionTranslationInterceptor which will translate exceptions to one of the Spring DataAccessExceptions. By default it translates all exceptions UNLESS the exception thrown is declared in the throws clause.

This exception translation happens before the TransactionInterceptor and that way there is never an IllegalArgumentException thrown because that already got translated into something else.

Upvotes: 9

Related Questions