Reputation: 2371
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
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
Upvotes: 1