When using a @Transactional
annotated method in Spring, is the returning of the annotated method synchronized to the successful commit of the underlying database transaction?
(This is assuming that the transaction will in fact commit after returning, which might not be the case depending on the selected propagation method.)
If waiting for a commit is not the default behavior, is there any way to enable it, either via configuration or otherwise?
The motivation for my question is that I would like to implement a reliable "store and forward"-type endpoint which only returns a response to the client after the effects of the call have been committed to stable storage. Is this possible with @Transactional
at all?
Short answer is no: the method does not wait for a commit.
What actually happens depends on two factors:
If no transaction pre-exists, a new transaction is created before calling the method, and if it has not been marked as rollback only, Spring tries to commit it immediately after the method returns - in that case, even if the method does not wait for a commit, the commit is executed immediately.
Things go harder when, because of nested calls, a transactional annoted method is called while a physical transaction already exists
Extract from Spring Reference Manual:
When the propagation setting is PROPAGATION_REQUIRED, a logical transaction scope is created for each method upon which the setting is applied. Each such logical transaction scope can determine rollback-only status individually, with an outer transaction scope being logically independent from the inner transaction scope. Of course, in case of standard PROPAGATION_REQUIRED behavior, all these scopes will be mapped to the same physical transaction. So a rollback-only marker set in the inner transaction scope does affect the outer transaction's chance to actually commit
PROPAGATION_REQUIRES_NEW, in contrast to PROPAGATION_REQUIRED, uses a completely independent transaction for each affected transaction scope. In that case, the underlying physical transactions are different and hence can commit or roll back independently, with an outer transaction not affected by an inner transaction's rollback status.
PROPAGATION_NESTED uses a single physical transaction with multiple savepoints that it can roll back to. Such partial rollbacks allow an inner transaction scope to trigger a rollback for its scope, with the outer transaction being able to continue the physical transaction despite some operations having been rolled back.
That means that except with PROPAGATION_REQUIRES_NEW, the commit will not be attempted before the end of the containing transaction.
Said differently, if you want that a commit occurs immediately after the end of a transaction annoted method, you must set the propagation to PROPAGATION_REQUIRES_NEW : @Transactional(propagation=Propagation.REQUIRES_NEW)
IF you didn't use @Transactional
, then how would you handle it? it will be similar code block like following
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
businessLogic();
utx.commit();
} catch(Exception ex) {
utx.rollback();
throw ex;
}
@Transactional
actually does the same thing for you. so you can see it is handled after your business logic.
I copied the code from this wonderful blog. You can check this to know more details.
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