Class A{
#transactional
public Void methodA(){
methodB();
int i=10/0;
}
#transactional
public void methodB(){
session.save(student)
}
Here there is an exception in methodA but it is not rolling back and inserting student data.why?
}
By default, #Transactional rolls back on runtime exceptions.
You need to use rollbackFor().
#Transactional(rollbackFor = {MyException.class})
Method marked with #Transactional has to be public.
This is documented in Spring Manual chapter 10.5.6:
Method visibility and #Transactional
When using proxies, you should apply
the #Transactional annotation only
to methods with public visibility. If
you do annotate protected, private or
package-visible methods with the
#Transactional annotation, no error
is raised, but the annotated method
does not exhibit the configured
transactional settings. Consider the
use of AspectJ (see below) if you need
to annotate non-public methods.
Beacuse A and B separate transactions !
When you call a method without #Transactional within a transaction block, the parent transaction will continue to the new method. It will use the same connection from the parent method(with #Transactional) and any exception caused in the called method(without #Transactional will cause the transaction to rollback as configured in the transaction definition.
If you call a method with a #Transactional annotation from a method with #Transactional within the same instance, then the called methods transactional behavior will not have any impact on the transaction. But if you call a method with a transaction definition from another method with a transaction definition, and they are in different instances, then the code in the called method will follow the transaction definitions given in the called method.
You can find more details in the section Declarative transaction management of spring transaction documentation.
Spring declarative transaction model uses AOP proxy. so the AOP proxy is responsible for creation of the transactions. The AOP proxy will be active only if the methods with in the instance are called from out side the instance.
The answer depends on what you already know.
Do you know how Spring works when you add a #Transactional annotation?
Ans: It does so by creating a proxy class for the class which has annotated methods.
Do you know how Spring Proxy object works when one method in the proxied class calls another method in the same proxied class?
Ans: Sprig is not able to handle this scenario implicitly. Any annotation on the called method would be ignored (since the call happens on 'this' rather than on the Proxy)
You need to switch to AspectJ to handle such scenario's
If you really like to understand this behavior I recommend reading this section of the Spring documentation.
Related
I have a few questions about #Transactional annotation. If I have an implementation similar as below:
#Transactional
public Interface ExampleRepo {
SampleClass createRecord();
}
public class RepoImpl implements ExampleRepo {
public SampleClass createRecord() {
return saveRecord();
}
private saveRecord() {
//Saving record to Database.
}
}
Will the transaction rollback as Spring will ignore if the #Transactional was called on a private method? But what if a public method annotated with #Transactional calls a private method that actually does the database operation.
Will this transaction be marked as rollback?
As per Spring Documentation
Spring recommends that you only annotate concrete classes (and methods
of concrete classes) with the #Transactional annotation, as opposed to
annotating interfaces. You certainly can place the #Transactional
annotation on an interface (or an interface method), but this works
only as you would expect it to if you are using interface-based
proxies. The fact that Java annotations are not inherited from
interfaces means that if you are using class-based proxies (
proxy-target-class="true") or the weaving-based aspect (
mode="aspectj"), then the transaction settings are not recognized by
the proxying and weaving infrastructure, and the object will not be
wrapped in a transactional proxy, which would be decidedly bad.
So, I would recommend to use #Transaction at method createRecord() level or RepoImpl class level.
All code within a transaction scope runs in that transaction. However, you can specify the behavior if a transactional method is run when a transaction context already exists.
When a method without #Transactional is called within a transaction block, the parent transaction will continue to exist for the new method. It will use the same connection from the parent method (method with #Transactional) and any exception caused in the called method (method without #Transactional) will cause the transaction to rollback as configured in the transaction definition.
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.
I have a class that is invoking a public method on another class via reflection. The invoking class already has an active transaction, and the public method on the invoked class is marked with
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleProcess() { ..}
The invocation looks like
Runnable runnable = null;
Method handleMethod = config.handleProcessMethod;
Object handler = autowireHandler(process);
runnable = () -> {
LOGGER.info("executing method {} on {}",handleMethod,handler);
handleMethod.invoke(handler);
};
runnable.run();
The method is called correctly, but the log indicates that it is particpating in the current transaction instead of creating a new one.
08:54:33.452 [process-executor-2] DEBUG o.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager ... for JPA transaction
08:54:33.453 [process-executor-2] DEBUG o.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
Edit: Actually the logs from above are being created within the invoked method, caused by a JPA repository, not prior to it. So it looks like the #Transactional annotation isn't being processed at all.
Is the reflection causing spring to miss the #Transactional annotation? I need the invoked method to use it's own transaction so that it's committed prior to returning from the invocation.
The annotation tells Spring what to wrap with a proxy that implements the transactional behavior. Reflection is bypassing that proxy and calling the wrapped method directly. Looking at the logs should confirm that, you may need to dial up log level for spring stuff.
Don't use reflection for this, because that's going behind Spring's back and it can't help you. You can autowire a list of services that implement a common interface. Your code can go through the list and figure out which one is relevant to what you need to do, then call the method on the chosen service.
If you are submitting tasks to an executor, you are going to have trouble when there is no entitymanager found on the worker thread. If you make these services use Spring async methods instead, that will let Spring handle the transactions, entityManager, etc.
If one Service class has method with #Transactional, then spring will use proxy to handle it.
But if one Transactional method call another one
#Transactional
public FeedBackModel getOne() {
///..
return getTwo();
}
#Transactional
public FeedBackModel getTwo() {
return null;
}
like this.
if it is jdk proxy, then second #Transactional will not work.
But spring PROPAGATION will handle this correctly.
How it works?
If you are trying to commit a transaction in getTwo() which is called from getOne(), that will not work, not even when both are #Transactional. Refer to the documentation:
...please do take the Spring team's advice and only annotate concrete
classes (and the methods of concrete classes) with the #Transactional
annotation.
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!
Just to understand the workings of Spring transactions I want to know what happens in the following case where one method marked as #Transactional calls another method marked as #Transactional.
Assume a the configuration uses all default settings.
#Service("myService")
#Transactional
public MyService{
public void myServiceMethod(){
myDAO.getSomeDBObjects();
}
}
#Repository("myDAO")
#Transactional
public MyDAOWithUsesBeyondMyService{
public void getSomeDBObjects(){...}
}
Now if I were to enter MyService.myServiceMethod() it would clearly start a transaction. Then, upon drilling into myDAO.getSomeDBObjects() what would happen? Would the fact that a transaction already exist cause no new transaction to be born, or am I creating two transactions here?
The documentation (quoted below) on Propagation seems to cover this, but I'd like to verify my understanding, it was a little much for my virgin brain to comprehend all at once.
Propagation: Typically, all code
executed within a transaction scope
will run in that transaction. However,
you have the option of specifying the
behavior in the event that a
transactional method is executed when
a transaction context already exists.
For example, code can continue running
in the existing transaction (the
common case); or the existing
transaction can be suspended and a new
transaction created. Spring offers all
of the transaction propagation options
familiar from EJB CMT. To read about
the semantics of transaction
propagation in Spring, see Section
10.5.7, “Transaction propagation”.
Two answers:
a) don't do it. Use #Transactional in the service layer or the dao layer, but not both (the service layer is the usual choice, as you probably want one transaction per service method)
b) if you do it, what happens depends on the propagation attribute of the #Transactional annotation and is described in this section: 10.5.7 Transaction propagation. Basically: PROPAGATION_REQUIRED means the same transaction will be used for both methods, while PROPAGATION_REQUIRES_NEW starts a new transaction.
About your comments:
Of course I kept reading and realized that, as I'm using proxies, this second method won't be managed by the transactional proxy, thus it's like any other method call.
That's not true in your situation (only if both methods were within the same class).
If a bean has methods a and b, and a calls b, then b is called on the actual method, not the proxy, because it is called from within the proxy (a bean doesn't know that it is proxied to the outside world).
proxy bean
a() --> a()
|
V
b() --> b()
In your situation, however, a service would have an injected dao object, which would be a proxy itself, so you'd have a situation like this:
proxy bean
service a() --> a()
|
/---------/
|
V
dao b() --> b()
in one dao I have 2 #Transactional methods.
if i do not provide any explicit properties,
then what will happen, if
I run one method in the body of another?
Both methods will run within THE SAME ONE TRANSACTION?
Proxies in Spring AOP
When using Transactional, you're dealing with proxies of classes, so in this scenario:
#Transactional
public void doSomeThing(){ // calling this method targets a proxy
doSomeThingElse(); // this method targets the actual class, not the PROXY,
// so the transactional annotation has no effect
}
#Transactional
public void doSomeThingElse(){
}
you are calling the proxy from outside, but the second method call is made from inside the proxied object and therefor has no transactional support. So naturally, they run in the same transaction, no matter what the values of the #Transactional annotation in the second method are
so if you need separate transactions, you have to call
yourservice.doSomething();
yourservice.doSomethingElse();
from outside.
The whole scenario is explained pretty well in the chapter Spring AOP > Understanding AOP proxies, including this "solution":
Accessing the Current AOP Proxy object from the inside
public class SimplePojo implements Pojo {
public void foo() {
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();
}
public void bar() {
// some logic...
}
}
The default value of the propagation attribute of #Transactional is REQUIRED, which means:
Support a current transaction, create a new one if none exists.
So yes - both methods will run in the same transaction.
But one important advice: don't make your DAO transactional. The services should be transactional, not the DAO.
Spring doc
one note:
In proxy mode (which is the default),
only external method calls coming in
through the proxy are intercepted.
This means that self-invocation, in
effect, a method within the target
object calling another method of the
target object, will not lead to an
actual transaction at runtime even if
the invoked method is marked with
#Transactional.