Reputation: 43
I have the following code:
@Transactional
public void handle() {
for (Item item : getItems()) {
handle(item);
}
}
@Transactional(propagation = Propagation.NESTED)
public void handle(Item item) {
/* logic here */
}
Say the loop in handle()
processes 10 items. Also assume that for 3 items handle(Item)
will throw an exception.
My questions:
[1] Will the outer transaction be committed after the 10th item? This means that the necessary changes for 7 items will be committed and that any intermediate changes for the 3 other items will be rolled back to the savepoint as created?
[2] Will the exception in handle(Item)
be caught and not be forwarded to handle()
then? Is this done by @Transactional
?
[3] In addition, I would like to understand what the difference in behavior with the following flow is:
@Transactional
public void handle() {
for (Item item : getItems()) {
handle(item);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handle(Item item) {
/* logic here */
}
From my understanding from the docs, here a new transaction will be started and the active transaction will be suspended. This differs from NESTED
, where a savepoint is created and the current transaction is still being used. But I believe that also here with REQUIRES_NEW
the changes for 7 items will be committed and that any intermediate changes for the other 3 items will be forgotten.
So what is the real difference then (if any)?
Upvotes: 4
Views: 1238
Reputation: 5407
From my understanding from the docs, here a new transaction will be started and the active transaction will be suspended.
A Spring hard fact about transaction management and WHAT IS A TRANSACTION?.
to apply transaction for handle(Item item) inside handle() you should
@Transactional
public void handle() {
for (Item item : getItems()) {
applicationContext.getBean(your service).handle(item);
}
}
or use spring aspectj load time weaving for self-invocation (inside one service call one method from another)
Call method with Propagation.REQUIRES_NEW inside loop it's bad for performance , you create new transaction for each call. You can wrap inside one method but with handling exception - only for exception related to businesses logic! not all types of exceptions
@Transactional
public void handle() {
..some logic
//if you really need it
applicationContext.getBean(your service).handleBatch(getItems());
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleBatch(Collection items) {
for (Item item : items) {
applicationContext.getBean(your service).handle(item)
}
}
public void handle(Item item) {
try{
//do operation
}catch(.appropriate type..Exception ex){
//log exception!!!
}
}
@Transactional(rollbackFor=CheckedException.class)
Upvotes: 1
Reputation: 57381
NESTED starts subtransaction of a main one. REQUIRES_NEW starts separate transaction.
If you mark a method with the REQUIRES_NEW after method exit data remains in DB (committed by the separate transaction) no matter what happens with outer transaction.
In case of NESTED the changes are rollbacked if outer transaction is rolled back.
See answers here
Upvotes: 2