Reputation: 65
I am new with spring/grails transaction and i am still not clear after reading so i am posting it in here,
I have a service class which is annotated as @Transactional and i have methods some of them are annotated as
@Transactional(propagation = Propagation.REQUIRES_NEW)
and some of them are not.
@Transactional
class SomeService {
def findJob() {
MyInstance myInstance = getMeAJob();
if (myInstance) {
doSomeThing(myInstance)
doTask()
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception)
private doSomeThing(MyInstance myInst) {
myObj = MyInstance.lock(myInst.id)
try {
differentObj = doTask(myObj)
myObj.save()
doAnotherThing()
}
} catch (Exception e) {
log("Error in doAnotherTask")
}
}
private doAnotherThing(MyInstace myInst) {
perform some update on myInst
myInstant.save(flush: true)
}
private doTask() {
}
Method that will execute in t2 - doSomeThing() - doesn't affect the "t1" in case of exception (Rollback)
Which one will be for doAnotherThing() method? Since I am calling it from doSomething()?
Upvotes: 0
Views: 672
Reputation: 75681
When you have a class-scope annotation, any method that's not annotated uses those settings, and annotated methods override the class-level settings with the settings from that annotation.
Note however that when using Spring's annotation support (with the @org.springframework.transaction.annotation.Transactional
) annotation only public methods are supported. There won't be an error, but the annotations are silently ignored.
But even if you make doSomeThing public (either implicitly by removing the private
keyword or explicitly by changing it to public
) it won't do what you expect when you call doSomeThing
from findJob
. This is because Spring's annotation triggers the creation of a proxy of your class at runtime. The Spring bean that's registered for your service in that case is a proxy instance that has a "real" instance of your class as its delegate. All transactional public methods are intercepted by the proxy, which starts/joins/etc. a transaction and then calls your service method. Once you're "underneath" the proxy in the service instance all method calls are direct and any annotation settings have no effect. To get it to work you would need to call the method on the Spring bean and go through the proxy to start a new transaction.
To avoid this proxy issue, use the Grails @grails.transaction.Transactional
annotation instead. It supports the same transaction features as the Spring annotation, but instead of creating a proxy, an AST transformation rewrites your methods to wrap them within a transaction. This makes it possible to make direct method calls like you're doing and create a new transaction or run under other transaction semantics as defined by the annotation attributes.
I did a talk a while back on using transactions in Grails that might shed some light.
Upvotes: 6