REQUIRES_NEW is rolling back all transactions:
I have a method marked #Transactional(propagation = REQUIRES_NEW)
within a bean that also has methods marked #Transactional(propagation = REQUIRED). These methods never call each other. They are called from another bean individually. However when my method that has been marked with REQUIRES_NEW fails, it rolls back the whole transaction, including the methods that are marked REQUIRED (which is problematic).
My understanding is that if factored this way Spring AOP will have a chance to intercept the REQUIRES_NEW method and start a new logical transaction.
The general idea looks like this:
#Transactional
class TransactionalBean{
#Transactional(propagation = REQUIRED)
public void methodA(){ //do transactional work }
#Transactional(propagation = REQUIRES_NEW)
public void methodB(){ //do transactional work }
}
And then the calling bean looks like so:
#Transactional
class CallingBean{
#Autowired
TransactionalBean
public void doWork(){
TransactionalBean.methodA();
TransactionalBean.methodB();
}
}
So if method A and B succeed, everything is fine. However when method B fails work done in method A gets rolled back. My understanding is that when methodB() is invoked that it 'should' get intercepted by AOP and start a new transaction and suspend the other transaction, but it's not working. How do I fix this? I'm using Grails 2.5, Hibernate, JPA
The problem is with the way the CallingBean was marked as #Transactional. What is happening is that the CallingBean is creating a transaction, let's say t1. The CallingBean is invoking the two transactional methods methodA and methodB. Since methodA requires a transaction, it will make use of the existing transaction t1. However, methodB will create a new transaction as the annotation requires a new one, let's say t2. The transactional boundary of methodA does not end when the control exits methodA, but is kept alive until methodB is completed since the transaction started at the calling bean. Now that the transaction t2 is failing in methodB, the exception would bubble up to the calling bean and the transaction t1 would fail.
In order to resolve this, you can do one of the following based on your needs.
Turn off the #Transactional annotation in the CallingBean class.
//#Transactional
class CallingBean{
...
}
Update the annotation to use noRollbackFor option
#Transactional(noRollbackFor={WhateverException.class})
class CallingBean{
...
}
Related
I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
#Transactional(rollbackFor = Exception.class)
public methodA() {
// do some stuff
methodB();
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public methodB() {
try {
methodC();
} catch (Exception e) {
// only log
}
}
#Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public methodC() {
// do some stuff
}
What I want to achieve is this: when methodA is called, everything executed before calling methodB should be persisted, independently to what happens afterwards. For this reason I added methodB, the only purpose of which is to create a new transaction, so if the transaction of methodC rollbacks, the original transaction of methodA does not rollback as well. So to sum this up, I want the first transaction to commit and the second to rollback in case of failure. Note that methodC cannot be changed in any way.
This code however does not work and I still get the exception: "TransactionException: Transaction was marked for rollback only; cannot commit"
Any ideas on what I'm doing wrong?
I am using Spring 4, JPA and Hibernate 5. I have a service as follows:
So you're using three methods inside same service
This highlight is important because of how spring AOP works.
If you call method within the same class, then the other methods do not get proxied by #Transaction so they're still using the same transaction of methodA.
Therefore you will see exception when the transaction is marked as rollbackOnly by methodB
So to solve this: You have to move the method methodB to another service so it will proxy-able by Spring.
I am having a method annotated with spring #Transactional and another custom annotation #Counter. #counter is used by many other methods from different classes.
#Transactional
#Counter
public int myMethod() {
....
}
#Counter is intercepted by an aspect which does some count related logic and saves to DB and is marked with #Transactional, hence completes in different transaction than the myMethod().
when I call this myMethod(), first this aspect is called and after it completes then the transaction for myMethod() is created and then executes the business logic in myMethod().
Issue here is, if any exception thrown in myMethod(), it rolls back all the changes. But since #Counter is in different transaction, it still is processing and doing counter related updates and does not roll back the counter related changes.
My question is: is it possible to bring the aspect of #Counter also within the same trancaction boundary of myMethod()? Is it possible to create the transaction for myMethod() even before the #Counter is called?
Edit 1: I have tried using #Order (value 5) for the around aspect and for the myMethod() transactional order wil be inferred from context xml
<tx:annotation-driven transaction-manager="transactionManager" order = "1"/>
Still transaction gets created only after aspect completes
Edit 2:
#Aspect
public class CounterHandler {
#Autowired
private counterOperation CounterOperation;
#Around("#annotation(com.prime.monitor.counter.aspect.Counter)")
public Object aroundAdvice(final ProceedingJoinPoint methodInvocation) {
//counter related operation
return counterOperation.prepareCounter(methodInvocation);
}
}
I want to know what actually happens when you annotate a method with #Transactional with ScheduledExecutorService?
Assume that the methodA is called externally. Am I correct in assuming that when methodA is called, someDao.methodDao() joins in a transaction and scheduleMethodB() returns immediately.
Later after 2 seconds, the scheduler calls the methodB(). What would this hold in this case? Would it hold the TransactionProxy and execute methodB in a separate transaction? If not, then how would we able to achieve this.
I am aware that #Transactional is based on proxies so is methodB call a self invocation under scheduler.
Note: Since this mechanism is based on proxies, only 'external' method
calls coming in through the proxy will be intercepted. This means that
'self-invocation', i.e. a method within the target object calling some
other method of the target object, won't lead to an actual transaction
at runtime even if the invoked method is marked with #Transactional!
public class ServiceABImpl implements ServiceAB {
#Autowired
private ScheduledExecutorService scheduledExecutorService;
#Transactional
public void methodA() {
//do some work in a transaction.
someDao.methodDao();
//schedule a methodB
scheduleMethodB();
}
public void scheduleMethodB() {
scheduledExecutorService.schedule(() -> {
this.methodB();
return "";
},
2,
TimeUnit.SECONDS);
}
#Transactional
public void methodB() {
}
}
Since the class is not annotated with #Transactional, the decision of whether an invoked method participates in the transaction of the parent invokee method depends on whether you annotate the invoked methods also with #Transactional and what propagation level you configure it with I think. So for example
#Transactional(propagation=Propagation.REQUIRED)
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() {
}
Suppose , I have transaction t1 from the class level and t2
transaction from doSomething()- REQUIRES_NEW.
Method that will execute in t1 - findJob() and 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()?
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.
i want to make somethings clear about #Transactional spring annotation.
Suppose i have written my code something as under,
case-1
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
.....method body....
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
try{
method1()
}
catch(Exce...){
}
}
case 2:
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public void method1(){
try{
}
catch(Excep..){
}
}
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor=Throwable.class)
public String method2(){
method1()
}
What would happen in case-1 if some exception occurs in excecution of method1, does it rollback transaction?
and what would happen for the same in case-2.
Thanx in advance
There will be no rollback in any of your use cases regardless of the exception type or propagation as long as you catch the exception in method2.
Why?
Spring creates a proxy class for the Transactional class or members, this is not visible for you. This proxy makes it possible to Spring to execute code before and after your function, basically open transaction, commit or rollback. This proxy is injected into your classes when you autowire them.
Of course the Transactional annotations only work when you're calling them through the proxy!
Your method1 call is always a local function call, so as long as you catch the exception in method2 it won't cause any rollback.
But please read the documentation.
You are free to specify. Using annotations for instance you can write like this:
#Transactional(rollbackFor=Exception.class, timeout=10)
Default behavior is not to rollback when an exception is thrown across the transaction demarcation line.
The Spring #Transactional default configuration (as you used it here) is rolling back only for java.lang.RuntimeException s and the propagation is REQUIRED.
So in case you are calling method2():
CASE 1: no rollback as you are catching in method2() java.lang.Exception (which is the parent of java.lang.RuntimeException)
CASE 2: same - as you are catching in method1() java.lang.Exception
The meaning of the propagation here is that when you've started a transaction if you encounter another annotation of #Transaction another transaction is not started and the application uses the "outer" transaction. So do not expect that the rollback logic will get executed before the method2() finishes. Anyway, one may assume from your snippet that the methods are in the same Spring Bean so calling a method internally in a bean (method2() calls method1() here) the AOP annotation are not "active" meaning that the f/w will not even "see" the #Transactionl on method1().
EDIT
Question changed so that the rollback is for all java.lang.Throwable s. So the answer is the same except for the fact that if an java.lang.Error is thrown then there is a rollback in both cases after the method2() is finished.