@Transactional (noRollbackFor=RuntimeException.class) does not prevent rollback on RuntimeException
Once an exception is caught, the Hibernate Session should be discarded and the transaction should be rolled back:
If the Session throws an exception, the transaction must be rolled back and the session discarded. The internal state of the Session might not be consistent with the database after the exception occurs.
So, noRollbackFor
applies to your Service and DAO layer that might throw an exception. Let's say you have a gatewayService that write to a Database through a Hibernate DAO and also sends an email through an emailService. If the emailService throws a SendMailFailureException
you can instruct the gatewayService not to roll back when it will catch this exception:
@Transactional(noRollbackFor=SendMailFailureException.class)
public void saveAndSend(Entity e){
dao.save(e);
emailService.send(new Email(e));
}
ThermalEagle
Updated on September 26, 2020Comments
-
ThermalEagle over 3 years
@Transactional (noRollbackFor=RuntimeException.class) public void methodA (Entity e){ service.methodB(e); }
---service method below---
@Transactional (propagation=Propagation.REQUIRES_NEW, noRollbackFor=RuntimeException.class) public void methodB (Entity e){ dao.insert(e); }
When
dao.insert(e)
inmethodB()
causes a primary key violation and throws aConstraintViolationException
, which is a subclass ofRuntimeException
, I would expect the transaction to still commit because of thenoRollbackFor
property I used. But I observed that the outer transaction (onmethodA
) is still being rolled back by theHibernateTransactionManager
with the messageorg.springframework.transaction.UnexpectedRollback Exception: Transaction rolled back because it has been marked as rollback-only
I've found similar questions reported but not exactly this one.
-
sol4me over 9 yearsDid you set globalRollbackOnParticipationFailure to false E.g.
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="globalRollbackOnParticipationFailure" value="false" /> <property name="sessionFactory" ref="sessionFactory" /> </bean>
-
gknicker over 9 yearspretty sure sol4me is on the right track - see stackoverflow.com/a/11205537/1594449
-
ThermalEagle over 9 yearsSounds like a great idea but I am not keen to change the setting of
globalRollbackOnParticipationFailure
because I am enhancing an existing monolithic piece of code that already uses the same transaction manager, and I would want to keep the existing code unaffected by my change - this does not appear feasible by changing the tx manager config. Can I do something that will be entirely specific to my change? -
ThermalEagle over 9 yearsjust for your information, I also tried catching the
RuntimeException
thrown indao.insert(e);
withinmethodB
and rethrowing it out after wrapping it in a checked exception. I changed thenoRollbackFor
to match the checked exception used. However, this made no difference - the outer transaction inmethodA
was still rolled back! -
M. Deinum over 9 yearsare
methodA
andMethodB
on the same object/service? -
Ankur Singhal over 9 years@ThermalEagle how is the transaction started/propagated for
methodA
, Also have a look at the queries being fired at the back, doesmethod A
is trying to insert something as well during flushing..?? -
Andy Dufresne over 9 years@ankur-singhal - would it matter who starts the transaction for methodA since methodB is starting a new transaction?
-
Ankur Singhal over 9 years@AndyDufresne yes, it does not matter since
methodB
starts its own transaction, -
Cloudanger over 4 yearsIs the
dao.insert(e)
annotated with@Transactional
? If so, it should also havenoRollbackFor
to not mark whole transaction to rollback.
-
-
Andy Dufresne over 9 yearsWell explained with an example
-
Ankur Singhal over 9 years@AndyDufresne what my understanding is -
Method B
has its own transaction, method A transaction will be suspended , so as proxy, proxy called ittarget method method B
, and if any exception occurs for whichno rollback
is configured, only this transaction will be take care of, then it will resume the transaction ofmethod A
-
Vlad Mihalcea over 9 yearsEven if there's a new transaction because of Propagation.REQUIRES_NEW, the same principle applies to the Hibernate Session.
-
Oleg Kuts over 3 years@VladMihalcea so
noRollBackFor
works only for exceptions that are not database\hibernate specific ones? So sayDataIntegrityViolationException
will rollback in any case? Also as I understand even havingPropagation.REQUIRES_NEW
will not help as hibernate will mark all suspended transaction in the chain as rollback-only, because hibernate marks whole Session as rollback? -
Vlad Mihalcea over 3 yearsThat's right. You can use it for when the cache is not available, but you could still serve the request from the DB.
-
Oleg Kuts over 3 years@VladMihalcea looks like
propagation.not_supported
did the trick. Original transaction gets suspended, and is not going to roll back even if method marked withnot_supported
throwsDataIntegrityViolationException
. -
Vlad Mihalcea over 3 yearsVery interesting use case. You should write an article about it. I'm interested in reading more about it.
-
Olgun Kaya about 2 yearsThis helped me a lot. I first resist to use it like feeling a bit cheating. Then, I read the example and convinced. My case was, reset password token expired (no scheduler used for the sake of performance) but status still seems to be legit. I first check if token, if status legit but date expired; then update token to status expired too and throw exception (AlreadyUsedException) for client to take appropriate action. Without this property it was failing.