I have a question about spring transaction propagation. I understand different Spring transaction propagation i.e. Propagation.REQUIRED, Propagation.REQUIRES_NEW etc. What I am not clear about is how would it behave in case of nested transactions with different propagation values.
For example If I use #Transactional(propagation = Propagation.REQUIRED) to annotate a method m1. If I call a method m2 which is annotated as REQUIRES_NEW. What would be the behaviour? If an exception occurs in m2 will it rollback the changes in m2 and as well as m1 or only the changes of m1.
Similarly m1 annotated as REQUIRES_NEW and m2 as REQUIRED.
It's explained pretty well in the spring documentation for Transaction Propagation.
The propagation behavior of a method defines how Spring behaves upon entry/exit of that method and what happens if a transaction already exists (or does not exist) at that time.
The propagation behaviors of methods up the stack (whatever is calling your method) are irrelevant.
For your examples:
Outer (m1): REQUIRED
Inner (m2): REQUIRES_NEW
Result: A completely separate transaction will be created for m2 which can commit/rollback separately. m1 having REQUIRED is not relevant to m2.
Outer (m1): REQUIRES_NEW
Inner (m2): REQUIRED
Result: Spring will check that a transaction exists upon entering m2 or create one if none exists.
Related
I have a Java, Spring Data application with a PostgreSQL DB and I have a code similar to this in my service layer:
public void method1() {
PersonDto dto = method2();
method3(dto);
}
#Transactional
public PersonDto method2() {
Person p1 = personRepository.saveAndFlush(new Person());
return createPersonDto(p1);
}
#Transactional
public void method3(PersonDto p1) {
Person p2 = personRepository.findById(p1.getId());
if (p2 == null) {
System.out.println("Not in the DB");
} else {
System.out.println("In the DB");
}
}
Sometimes this code prints "Not in the DB", when I was expecting that it would always print "In the DB". My questions are:
This saveAndFlush is even necessary? Since I'm not reusing that Entity in the same transaction, I guess it would have the same effect as a save, right?
How to guarantee that the changes in method2 will be committed before method 3 calls it? So it will always print "In the DB"?
The fact that I'm converting my Entity to a DTO is what is messing this up? Maybe if I was passing the Entity itself it would work properly?
I'm thinking about #Transactional only in method1, or replacing #Transactional in method3 with #Transactional(isolation = Isolation.READ_UNCOMMITTED).
Any advice?
I can see a couple of reason why you're getting the results you describe.
One possibility is that #Transactional doesn't necessarily mean "execute this method in a totally separate transaction." The default propagation is REQUIRED, which will use an existing transaction if one is already active. So if method2() is called in a stack where a transaction was opened earlier, it won't create a new one (and thus the new entity won't be committed) until that original transaction is committed. If this is what's happening, one solution would be to change the propagation to REQUIRES_NEW:
#Transactional(propagation = REQUIRES_NEW)
Another possibility, which seems more likely, is that these methods are all in the same class. To understand why that fails, you have to understand how Spring implements handling of #Transactional (and most other behavioral annotations): using proxies. That means that only method calls that come through the proxy get the transactional behavior. That's the typical scenario when one object calls a reference it has to another; the reference is actually to the proxy, which intercepts the call and wraps the behavior (in this case, transactionality) around it. However, when calling methods within the same instance, there is no proxy in play (Spring doesn't/can't replace this with a reference to the proxy), and thus no transactional behavior.
There are a few ways to work around that, but first I would consider how to restructure my classes and methods to avoid it. If that's not reasonable, things like AspectJ, TransactionTemplate, and maybe some other ideas are discussed in this SO Q&A.
Addendum:
This question and its answers also talks about options to work around the proxying problem.
Spring boot, jdbc.
I feel a bit slippery when it comes to nested transaction..
Say there is MyService that uses MyRepository.
#Named
#Transactional(isolation=Isolation.READ_COMMITTED) // READ_COMMITTED < SERIALIZABLE
class MyService {
void do() {
myRepository.add(entity);
...
}
}
#Repository
class MyRepository {
#Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE)
void add(entity) {...}
}
Say, in this case i want to make sure that MyRepository takes care about the add() method, making sure it set a maximum level lock Isolation.SERIALIZABLE over the table it deals with.
I do not want anyone to override it, since it would lead to concurrency issues.
(same applies to propagation level)
Question:
In case of nested transaction, will transaction's of myService.do() override Isolation level of myRepository.add and maker it less strict ?
What are the precautions when dealing with nested transactions?
Hope this helps:
As per java docs of #Transactional annotation:
The transaction isolation level. Defaults to Isolation.DEFAULT.
Exclusively designed for use with Propagation.REQUIRED or
Propagation.REQUIRES_NEW since it only applies to newly started
transactions. Consider switching the "validateExistingTransactions"
flag to "true" on your transaction manager if you'd like isolation
level declarations to get rejected when participating in an existing
transaction with a different isolation level.
What happens when I call one transactional method from another transactional method, now my second transactional method completed, and it came back in 1st transactional method, and unfortunately it fail, so will it roll back everything, means will it roll back 2nd transactional method changes..?? Note: both method are in same class
#Transactional
public void method1(){
//do something
call method2();
//do something
...
...
failed here
}
#Transactional
public void method2(){
//do something
save()
}
So in above example will it rollback whatever I saved in 2nd transactional method?
It depends on the txType. By default it is REQUIRED. So the first method starts the transaction and the same transaction is used for the call to method2.
Also be careful that a method call inside the same object does not trigger the transaction processing. As typically the transaction handling is handled as proxy that only works when calling an injected other bean. In your example you would not notice a difference though.
A case where this would matter is if method1 is not #Transactional and method2 is. In this case there would be no transaction at all.
If both methods are in the same class, the #Transactional annotation won't even be considered when calling from another method of the same class. It doesn't matter what you put there, or even if you leave it out. There still will be a transaction started by method1(), but then you're stuck in that transaction.
If the second method were in a separate class, you could use Propagation.REQUIRES_NEW to have it run in its own transaction, meaning that even if method1() later on failed, any changes in method2() would still have made.
The default transaction propagation of REQUIRED starts a new transaction if none exist, or joins an existing transaction. Again, in the separate class situation it results in the rollback of any changes made in method2() when method1() fails.
Spring boot provides concept of propagation with #Transactions. Propagation level decides that inner transaction should be part of same outer transaction or it should be different isolated one. By default propagation level is REQUIRED which means inner transaction will be part of same outer transaction, so if inner transaction fails whole transaction will get rollback.
Now its important to know that Rollback works for only Runtime exceptions by default. For checked Exceptions you have to specify that explicitly #Transcations(rollbackFor = Exception.class)
So to answer for your question is YES! It does rollback changes made by inner transaction.
this is a typical question:
You should call method2() by self injected object or use this notation, because another ways will call method2 and avoid call proxy object method, which contain all transactional logic.
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. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct
Nested Transaction behaviour depends on #Transactional parameters. For example - Use PROPAGATION_REQUIRES_NEW for create new transaction for nested methods with #Transactional. See more info in official doc
What happens depends on the chosen transaction propagation. Default is: "REQUIRED" which means that if no transaction exists it will be started.
So in your code method2 will join existing transaction created for method1.
As for your case with both methods in the same class, be careful as this will not work as you expect because of the way Spring proxy objects work.
It depends on your transaction propagation config.
Related official doc is here
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#tx-propagation
Just note the graph, propagation define a transaction's "transaction context".
I know that having transaction attribute on a static method inside an EJB is incorrect. There won't be any exception thrown but the transaction attribute will not have any effect.
However, if an EJB has a method callMe() with a transaction attribute (let's say REQUIRED) and this method invokes a static method that is within the same EJB or another helper class, will the static method be part of the container transaction?
Yes, it will be inside the transaction. The transaction is open for that entire method and anything called inside of it will execute inside the transaction unless you use special transaction attribute methods like : NOT_SUPPORTED or REQUIRES_NEW obviously.
When one talks about EJBs, one talk about beans' instances and not about the class itself.
Furthermore, in an enterprise bean with container-managed transaction demarcation, the EJB container sets the boundaries of the transactions associated to the bean instance, either session or message-driven.
Finally, as the EJB 3.2 spec states, since the transaction attributes for the methods of a bean class may be specified on the class, the business
methods of the class, or both, and these methods can't be static, any static methods declared on the bean will not take part on the container-managed transactions.
As a side note, on the Revision History section of the EJB 3.2 specification (part A.8 Final Release Candidate), is mentioned the following:
Clarified that no-interface view and no-methods interface message listener methods are non-static public methods
I'm using a Interface with #Transaction, and I have a method that performs a loop and check some information, but this loop takes long time to finish it, and i added a parameter in the database. And inside of the loop, is checked the parameter, and if is equal true, the loop will stop.
My problem is at the moment that check the parameter, it not get the parameter updated, it keep with the old value of the parameter.
is there a way to keep the transaction on interface and disable for specific method?
Try to split in two your method and you put "Requires new" on that check the parameters, in order to create a new transaction and suspend the current transaction if one exists.
#Transactional(propagation = Propagation.REQUIRES_NEW)
I hope I've given you all the answers about your question.
The first thing is undestand why you are using #Transaction on interfaces, beacause the correct way is to annote concrete classes (and methods of concrete classes) with the #Transactional annotation.
From spring references 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.
Then can you share your code with us?