Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do @Transactional annotated methods wait for a successful commit in Spring?

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?

like image 735
lxgr Avatar asked Jun 08 '16 20:06

lxgr


2 Answers

Short answer is no: the method does not wait for a commit.

What actually happens depends on two factors:

  • does a transaction exist prior to calling the method?
  • what is the transaction propagation used?

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)

like image 192
Serge Ballesta Avatar answered Sep 25 '22 23:09

Serge Ballesta


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.

like image 29
stinepike Avatar answered Sep 21 '22 23:09

stinepike