Reputation: 3542
I have a simple code spring+jpa which looks correct at first glance:
@Transactional(propagation=REQUIRED)
public void outer(long id) {
MyEntity entity = myRepo.findById(id);
try {
// doing something that changes entity
// and may throw exception
doInOuter(entity);
} catch (Exception ex) {
anotherSpringService.inner(entity);
throw ex; // this rollbacks outer transaction for sure
}
}
@Transactional(propagation=REQUIRES_NEW)
public void inner(MyEntity entity) {
// doing something that changes entity
}
Let's consider the case when exception happens during doInOuter(). I would expect that in a catch block in the inner()
Unfortunately that is not what happens. All the changes in both inner/outer are completely "rolled back". I put the quotes because I don't think it happens because of the rollback itself.
If I change the inner to retrieve the entity one more time:
@Transactional(propagation=REQUIRES_NEW)
public void inner(MyEntity entity) {
entity = myRepo.findById(entity.getId());
// doing something which changes entity
}
The code starts to work as expected and I do see the changes done by inner() in the database.
Can anyone explain me why that works and why the first approach doesn't work as expected?
Upvotes: 2
Views: 1011
Reputation: 691635
The Hibernate session is bound to the transaction, and the entity is bound to the session. So, when you pass the entity to the inner method, since it has been loaded from the outer transaction, it stays bound to the outer session.
You shouldn't share the entity between transactions, as you discovered.
Upvotes: 4