springhibernatewebspherejtaspring-transactions

Spring Transactions - Prevent rollback after unchecked exceptions (RuntimeException)


I can't manage to prevent a transaction from rolling back after a RuntimeException.

My env is Spring 4.1 + Hibernate 3.6 + JTA (WebSphereUowTransactionManager) running on Websphere 8.0.

"doStuff" case: works

First off, a simple case that behaves as expected. Since I catch the RuntimeException, the transaction commits and the new resource is created successfully.

@Service("fooService")
public class FooServiceImpl implements IFooService {

    @Transactional
    @Override
    public void doStuff(Resource res){
        authService.createResource(res, "ADMIN");
        try {
            throw new RuntimeException("SOMETHING");
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
    }

"doStuff2" case: works

The next one is OK as well. I declare the noRollbackFor and it let's the transaction commit:

    @Transactional(noRollbackFor=RuntimeException.class)
    @Override
    public void doStuff2(Resource res){
        authService.createResource(res, "ADMIN");
        throw new RuntimeException("SOMETHING");
    }

"doStuff12" case: does NOT work

And finally the problematic one. The difference is that in this case the exception is raised by the second call to authService.createResource. FYI, authService.createResource is only marked as @Transactional, so the default Propagation configuration applies and it should be joining the calling service's transaction.

    @Transactional(noRollbackFor=RuntimeException.class)
    @Override
    public void doStuff12(Resource res){
        
        authService.createResource(res, "ADMIN");
        try{
            res.setName("EXISTING-RESOURCE");
            authService.createResource(res, "ADMIN");
        }catch(RuntimeException e){
            e.printStackTrace();
        }
    }

Despite catching the RuntimeException and declaring the noRollbackFor attribute the transaction always rolls back. Any explanation??

Log trace info:

org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',+com.myorg.webapps.exception.ElementoYaExistente
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Invoking WebSphere UOW action: type=1, join=false
org.springframework.transaction.support.TransactionSynchronizationManager TRACE - Initializing transaction synchronization
org.springframework.transaction.interceptor.TransactionInterceptor        TRACE - Getting transaction for [com.myorg.test.service.impl.FooServiceImpl.doStuff12]
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Invoking WebSphere UOW action: type=1, join=true
org.springframework.transaction.interceptor.TransactionInterceptor        TRACE - Getting transaction for [com.myorg.authmgr.service.impl.AuthorizationServiceImpl.createResource]
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Returned from WebSphere UOW action: type=1, join=true
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Creating new transaction with name [null]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Invoking WebSphere UOW action: type=1, join=true
org.springframework.transaction.interceptor.TransactionInterceptor        TRACE - Getting transaction for [com.myorg.authmgr.service.impl.AuthorizationServiceImpl.createResource]
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute TRACE - Applying rules to determine whether transaction should rollback on java.lang.Runtime: Couldn't create the resource, it already exists: EXISTING-RESOURCE
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute TRACE - Winning rollback rule is: null
org.springframework.transaction.interceptor.RuleBasedTransactionAttribute TRACE - No relevant rollback rule found: applying default rules
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Returned from WebSphere UOW action: type=1, join=true
org.springframework.transaction.jta.WebSphereUowTransactionManager        TRACE - Triggering beforeCommit synchronization
org.springframework.transaction.jta.WebSphereUowTransactionManager        TRACE - Triggering beforeCompletion synchronization
org.springframework.transaction.support.TransactionSynchronizationManager TRACE - Clearing transaction synchronization
org.springframework.transaction.jta.WebSphereUowTransactionManager        DEBUG - Returned from WebSphere UOW action: type=1, join=false

Solution

  • As far as I know, as soon as a runtime exception is thrown from a transactional method and is intercepted by the transaction interceptor, the transaction is marked as rollback only. Even if this transactional method is called from another transactional method.

    This makes sense to me: if the inner method can't recover from an exception, it can't recover, and an outer method shouldn't do as if nothing happened.

    If you're expecting the transaction not to rollback, you could