Asoub
Asoub

Reputation: 2371

Transaction rollbacked previous transaction

I have this weird behavior with transactions in Spring.

I have two classes:

The service class that is first called by doGenericServiceStuff (by another object that already posses a transaction):

@Service("myService")
public class ServiceClass {

    public void doGenericServiceStuff(Object someBean){
        BusinessI business = BusinessFactory.getBusinessForObject(someBean); //Here, returns the commonBusinessClass bean 
        business.doGenericBusinessStuff(someBean);
    }

    /*@Transactional (readOnly=false, rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW)*/
    public void firstOperation(Object o){
        //multiple database insert and stuff
    }

    /*@Transactional (readOnly=false, rollbackFor=Exception.class, propagation=Propagation.REQUIRES_NEW)*/
    public void secondOperation(Object o){
        //also multiple database insert and stuff
    }
}

Then the CommonBusinessClass that calls some of the ServiceClass' methods:

@Bean("commonBusinessClass")
public class CommonBusinessClass implements BusinessI{
    @Autowired
    @Qualifier("myService")
    protected ServiceClass = null;

    @Transactional(rollBackFor=Exception.class, Propagation=Propagation.MANDATORY)
    public Object doGenericBusinessStuff(Object o){
        service.firstOperation(o);
        service.secondOpertion(o);
        //There can be stuff here in other BusinessClass
        return o;
    }
}

Disclaimer: Obviously, I don't manipulate Objects like that and I don't call my method doGenericStuff(). However, ServiceClass does call CommonBusinessClass, which in turn, calls the ServiceClass. Maybe it's bad design, maybe it's the reason it's not working, but this is what I have for the moment.

The methods in ServiceClass are not Transactional (thus the comments), and the doGenericBusinessStuff() is (with Propagation.MANDATORY).

The new requirement is that if secondOperation() fails (and rollbacks) firstOperation() should be commited. So I added @Transactional for firstOperation() and secondOperation() (those in comment). I was expecting it to work normally (an exception in secondOperation() wouldn't rollback what has been done in firstOperation()), but it does not. However if I remove the @Transactional on doGenericBusinessStuff(), it works.

Is this behavior normal, or there is something else that comes into play in my real application that I didn't put here (because I simplified it) ? Why would the newly created transaction be linked to each other in any way ? Any ideas of what might happen ?

Thanks in advance.

Upvotes: 0

Views: 155

Answers (1)

reos
reos

Reputation: 8334

You need to understand the context of a transaction. If you mark a method as transactional, it means that every operation inside that method will be considered as one thing. If everything works ok then the transaction is committed but if something goes wrong then the whole transaction is rollbacked.

In you case you have:

  @Transactional(rollBackFor=Exception.class, Propagation=Propagation.MANDATORY)
    public Object doGenericBusinessStuff(Object o){
        service.firstOperation(o);
        service.secondOpertion(o);
        //There can be stuff here in other BusinessClass
        return o;
    }

So, if service.firstOperation(o) fails then the whole transaction is rollbacked and it's the same for service.secondOpertion(o). If service.secondOpertion(o) fails the all the operations inside doGenericBusinessStuff will be tollbacked.

Edit: The REQUIRES_NEW might not work in some cases, if your using JtaTransactionManager for example:

NOTE: Actual transaction suspension will not work out-of-the-box on all transaction managers. This in particular applies to JtaTransactionManager

From http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html

Upvotes: 1

Related Questions