Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 511
codependent Avatar asked Jan 22 '15 13:01

codependent


People also ask

Does Spring rollback on checked exception?

For any checked exception, you have to configure it. According to Spring documentation: In its default configuration, the Spring Framework's transaction infrastructure code marks a transaction for rollback only in the case of runtime, unchecked exceptions.

Which type of exceptions does Spring transaction Manager automatically mark for rollback?

Note however that the Spring Framework's transaction infrastructure code will, by default, only mark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. (Errors will also - by default - result in a rollback.)

Does transaction rollback on exception?

Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.


1 Answers

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

  • make the inner method non-transactional
  • configure the inner method not to rollback on this exception
  • have two inner methods:
    • one that is transactional, and is intended to be called when there is no transaction yet, and which simply delegates to the second one
    • one which is not transactional, and is intended to be called as part of an already existing transaction
like image 148
JB Nizet Avatar answered Oct 08 '22 00:10

JB Nizet