Preface
First of all:
It is not duplicate of Differences between requires_new and nested propagation in Spring transactions - I read it but I didn't found answer to my question
Question:
After reading topic I mentioned I understood that main difference between propagation levels in count of physical transactions:
2 db transactions- for REQUIRES_NEW
for outer and for inner method
1 db transaction - for NESTED
for outer and for inner method. It will not work if underlying database doesn't support savepoints
But looks like logic will be the same from my point if view.
How to understand which level to use in practise? Any use cases to understand it? Handy examples of behavioural differences?
P.S.
I suppose there are some visibility for other transactions differences because different transaction commit time.
P.S.2
Also I suppose there are performance difference:
@Transactional
public void outer(){
for(int i=0;i<100500;i++){
inner();
}
}
@Transactional
public void inner(){
//some logic
}
For that case NESTED will be better because of 1 long physical transaction instead of 100500+1
Transaction Propagation - MANDATORYIf none exists then gets executed with out transaction. Always executes in a transaction. If there is any existing transaction it is used. If there is no existing transaction it will throw an exception.
REQUIRED, spring checks for any existing transaction. If yes, it uses that old transaction otherwise it creates a new one. But, the disadvantage of Propagation. REQUIRES_NEW is that even if the inner method fails to execute (because of some exception), the outer method commits the transaction.
What is the transaction behavior of the PROPAGATION_REQUIRES_NEW mode? Select a unique answer. If a transaction exists, the current method should run within this transaction. Otherwise, it should start a new transaction and run within its own transaction.
As it stands in your example, if inner()
had:
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void inner(){
//some logic
}
Then if it threw an exception from the second call in the outer()
loop, the changes from the first call would have already been committed - by its REQUIRES_NEW
.
If inner()
had:
@Transactional(propagation=Propagation.NESTED)
public void inner(){
//some logic
}
Then the changes from the first call would be rolled back - because there is no catch block in outer()
.
The point where the propagation level on inner()
really starts to matter is if the outer()
loop is going to handle exceptions in inner()
:
@Transactional
public void outer() {
for (int i = 0; i < 100500; i++) {
try {
inner();
} catch (Exception ex) {
// Report and continue
}
}
// Something else that could fail
}
Clearly both REQUIRES_NEW
and NESTED
would only keep changes from the successful inner()
calls. The key difference though is that with NESTED
, there is still the option to throw it all away if there is a subsequent failure in outer()
.
As you say, the other factor is scalability - some databases may not appreciate the size of the parent transaction with NESTED
propagation.
Also, it might be worth saying - though I suspect it was just aiming for clarity in the example. Calling this.inner()
directly is bypassing the Spring transaction advisor. It needs to be allowed to inject an 'advised bean' to allow the @Transactional
annotation to do its magic before and after the call - e.g. nextAutowiredBean.inner()
.
The big differences I see:
in case of nested:
In case of requires_new:
In case of performance, if the other factors are not important, you can find a break even between transaction size and transaction number. i.m.O there is no general answer to the question, if nested is faster than requires_new.
If your inner logic is independent of the outer logic then use Requires_new, if not use nested.
For example, your outer method may be processing a job containing a large number of records and calling an inner method that persists job status (progress, warnings and validation errors). You would want the inner methods transaction to be independent, and its db changes to be saved immediately so some other part of the system could display the progress. If the outer method encounters an exception, it's transaction would rollback, but the inner method's transactions would not.
You would want to uses nested or dependent transactions when you need the outer and inner changes to either both be persisted or both be rolled back together. For example, you need to create a new user (using the "outer" service) and save their address (using the "inner" service). If your requirements are a user must have an address, then if the saving the user or address fails you want both changes to be rolled back.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With