Reputation: 37034
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
Upvotes: 8
Views: 2978
Reputation: 1600
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.
Upvotes: 1
Reputation: 10931
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()
.
Upvotes: 6
Reputation: 3593
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.
Upvotes: 4