Search code examples
javaspringspring-transactions

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?


Solution

  • 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)