PROPAGATION_REQUIRED transaction attribute in spring?

13,981

"Propagation required" is defined as

Support a current transaction, create a new one if none exists.

In your case above the deleteAllCountries method executes in a transaction and commits. There is no current transaction when initializeCountries is called, so it executes in a second transaction, and rolling it back has no effect on the changes made by the first method.

Propagation applies to nested method calls, not to successive ones. If you look at the documentation:

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 (as you would expect it to).

However, in the case where an inner transaction scope sets the rollback-only marker, the outer transaction has not decided on the rollback itself, and so the rollback (silently triggered by the inner transaction scope) is unexpected. A corresponding UnexpectedRollbackException is thrown at that point. This is expected behavior so that the caller of a transaction can never be misled to assume that a commit was performed when it really was not. So if an inner transaction (of which the outer caller is not aware) silently marks a transaction as rollback-only, the outer caller still calls commit. The outer caller needs to receive an UnexpectedRollbackException to indicate clearly that a rollback was performed instead.

then you can see all of this is about inner- and outer-, none of it mentions successive calls. In your case the call to deleteAllCountries is the outermost transactional method, so when it finishes successfully then Spring commits the transaction immediately. Then your call to initializeCountries has to be executed within a separate transaction, where it is the outermost method.

Your assumption seems to be that Spring will hold the transaction open after the first method finishes, but that's not how it works. In order to get the effect you want, you could create another method on testModel that wraps the calls to deleteAllCountries and initializeCountries, make that method transactional and give it the attribute PROPAGATION_REQUIRED. That way a rollback of the second method will cause the first method's changes to be rolled back too, because the wrapping method is grouping them together. Otherwise nothing is telling Spring these things should be part of the same transaction.

Share:
13,981
M Sach
Author by

M Sach

Updated on June 27, 2022

Comments

  • M Sach
    M Sach almost 2 years

    In first case study given at http://www.vermatech.com/code/SpringTransactionExamples.html, program is calling two methods i.e.

    testModel.deleteAllCountries();
    testModel.initializeCountries();
    

    where initializeCountries throws runtime exception. For both methods' transaction definition attribute is PROPAGATION_REQUIRED. Still transaction under deleteAllCountries method gets committed but transaction under initializeCountries are rolled back(as per the logs given in the same case study).

    As per PROPAGATION_REQUIRED definition is that it Support a current transaction; create a new one if none exists. So my question here is transaction under initializeCountries method should support the transaction under deleteAllCountries method. I mean both method should be treated as single transaction. As per my understanding either complete transaction should be committed or rolled back? Not sure how come logs are treating them separately.