Spring transaction nesting

Source: Internet
Author: User

Spring transaction nesting

Reference
PROPAGATION_REQUIRED -- supports the current transaction. If no transaction exists, a new transaction is created. This is the most common choice.
PROPAGATION_SUPPORTS -- supports the current transaction. If no transaction exists, it is executed in non-transaction mode.
PROPAGATION_MANDATORY -- supports the current transaction. If no transaction exists, an exception is thrown.
PROPAGATION_REQUIRES_NEW -- creates a transaction. If a transaction exists, it is suspended.
PROPAGATION_NOT_SUPPORTED -- executes operations in non-transaction mode. If a transaction exists, the current transaction is suspended.
PROPAGATION_NEVER -- runs in non-transaction mode. If a transaction exists, an exception is thrown.
PROPAGATION_NESTED -- if a transaction exists, it is executed within the nested transaction. If no transaction exists, perform a similar operation as PROPAGATION_REQUIRED.
The first six policies are similar to ejb cmt, and the seventh (PROPAGATION_NESTED) is a special variable provided by Spring.
It requires the Transaction Manager or the JDBC 3.0 Savepoint API to provide nested transaction behaviors (such as DataSourceTransactionManager of Spring)

The most common misunderstanding I have ever seen is the following:

Reference
Assume that there are two service interfaces ServiceA and ServiceB, and one of the ServiceA methods is implemented as follows:

/**
* Set the transaction property to PROPAGATION_REQUIRED.
*/
Void methodA (){
// Call the ServiceB Method
ServiceB. methodB ();
}

If ServiceB's methodB is configured with a transaction, it must be configured as PROPAGATION_NESTED.
 

This idea may have harmed many people and thought that services should be avoided from calling each other. In fact, there is no need to worry about this. PROPAGATION_REQUIRED has already been quite clear,
If a transaction already exists in the current thread, the method call will join this transaction. If no transaction exists, a new transaction will be created. Therefore, ServiceB # methodB () as long as the most common rule is configured as PROPAGATION_REQUIRED, if ServiceB # methodB (we call it an internal transaction, which lays the foundation for the following) throws an exception, so ServiceA # methodA (we call it an external transaction) If this exception is not configured for current affairs commit (that is, the usage of + MyCheckedException), the entire transaction must be rollback, what Service can only call Dao and other statements is nonsense. spring is only responsible for configuring interception of the transaction attribute method. How does it know whether your method is in Service or Dao?

After talking about this for a long time, what is real transaction nesting? Let's take a look at the original words of Juergen Hoeller.

Juergen Hoeller wrote:
PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. this transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. the outer transaction will get susponded at the beginning of the inner one, and resumed once the inner one has completed.

Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table shocould happen in its own transactions, to keep the lock there as short as possible. the goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.

PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. what will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. the nested transaction is part of the outer transaction, so it will only be committed at the end of the outer transaction.

Nested transactions essential tially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there-all within one isolated transaction, and not losing any previous work done within the outer transaction.

For example, consider parsing a very large input file consisting of account transfer blocks: The entire file shocould essential be parsed within one transaction, with one single commit at the end. but if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. you cocould either start over the entire transaction every time a block fails, remembering which blocks to skip-or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. the latter is of course much more efficient, in particle when a block at the end of the file fails.
 

Juergen Hoeller wrote:
Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.

So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules ), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. the immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.

If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. at that point, the rollback rules of the outer transaction decide whether to trigger a rollback. that wocould be a rollback of the entire outer transaction then.

So essentially, it depends on your exception handling. if you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. if you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.
 

That is to say, PROPAGATION_REQUIRES_NEW and PROPAGATION_NESTED are the most confusing methods. What are the differences between the two methods? Let me briefly translate Juergen Hoeller's words:

PROPAGATION_REQUIRES_NEW starts a new "internal" transaction that does not depend on the environment. this transaction will be fully committed or rolled back without relying on external transactions, it has its own isolation scope, its own lock, and so on. when the internal transaction starts to execute, the external transaction will be suspended. When the internal transaction ends, the external transaction will continue to execute.


On the other hand, PROPAGATION_NESTED starts a "nested" transaction, which is a real sub-transaction that already has transactions. when a latent transaction starts execution, it obtains a savepoint. if this nested transaction fails, we will roll back to this savepoint. A latent transaction is a part of an external transaction. It is committed only after the external transaction ends.

It can be seen that the biggest difference between PROPAGATION_REQUIRES_NEW and PROPAGATION_NESTED is that PROPAGATION_REQUIRES_NEW is a new transaction, and PROPAGATION_NESTED is a sub-transaction of the external transaction. If the external transaction commit, A latent transaction will also be committed. This rule applies to roll back.


So how do external transactions use the savepoint feature of nested transactions? Let's talk with code.

Java code
  1. ServiceA {
  2.  
  3. /**
  4. * Set the transaction property to PROPAGATION_REQUIRED.
  5. */
  6. VoidmethodA (){
  7. ServiceB. methodB ();
  8. }
  9.  
  10. }
  11.  
  12. ServiceB {
  13.  
  14. /**
  15. * Set the transaction property to PROPAGATION_REQUIRES_NEW.
  16. */
  17. VoidmethodB (){
  18. }
  19.  
  20. }

    In this case, because the transaction attribute of ServiceB # methodB is PROPAGATION_REQUIRES_NEW, there is no relationship between the two. ServiceA # methodA and ServiceB # methodB will not affect the transaction result because of the execution of the other party, because they are basically two transactions, the ServiceA # methodA transaction has been suspended when ServiceB # methodB is executed (the transaction suspension content is beyond the scope of this article, I will write some pending articles if I have time ).

    So what is PROPAGATION_NESTED? Continue viewing code

    Java code
    1. ServiceA {
    2.  
    3. /**
    4. * Set the transaction property to PROPAGATION_REQUIRED.
    5. */
    6. VoidmethodA (){
    7. ServiceB. methodB ();
    8. }
    9.  
    10. }
    11.  
    12. ServiceB {
    13.  
    14. /**
    15. * Set the transaction property to PROPAGATION_NESTED.
    16. */
    17. VoidmethodB (){
    18. }
    19.  
    20. }

      The current situation becomes more complex. ServiceB # The transaction attribute of methodB is configured as PROPAGATION_NESTED. How can we collaborate between the two? From the original words of Juergen Hoeller, we can find the answer. ServiceB # If methodB is rollback, internal transactions (that is, ServiceB # methodB) will be rolled back to the SavePoint before execution (note, this is the first time mentioned in this article. The core concept of a latent transaction), while external transactions (ServiceA # methodA) can be processed in the following two ways:

      1. Rewrite ServiceA as follows
      Java code
      1. ServiceA {
      2.  
      3. /**
      4. * Set the transaction property to PROPAGATION_REQUIRED.
      5. */
      6. VoidmethodA (){
      7. Try {
      8. ServiceB. methodB ();
      9. } Catch (SomeException ){
      10. // Execute other services, such as ServiceC. methodC ();
      11. }
      12. }
      13.  
      14. }

        This method is also the most valuable part of the latent transaction. It plays a role in branch execution, if ServiceB. methodB failed, then ServiceC. methodC (), while ServiceB. methodB has rolled back to its SavePoint before execution, so no dirty data is generated (equivalent to this method has never been executed). This feature can be used in some special services, neither PROPAGATION_REQUIRED nor PROPAGATION_REQUIRES_NEW can achieve this. (digression: Seeing this code seems like a stranger, remembering prototype. try function in js)

        2. The code is not modified, so if the internal transaction (that is, ServiceB # methodB) rollback, then ServiceB. methodB first rolls back to the SavePoint before it is executed (this will happen under any circumstances ),
        The external transaction (ServiceA # methodA) determines whether it is commit or rollback (+ MyCheckedException) based on the specific configuration ).


        The above roughly describes the use cases of latent transaction sets. Next we will look at how to use PROPAGATION_NESTED in spring. First, let's look at AbstractPlatformTransactionManager.

        Java code
        1. /**
        2. * CreateaTransactionStatusforanexistingtransaction.
        3. */
        4. PrivateTransactionStatushandleExistingTransaction (
        5. TransactionDefinitiondefinition, Objecttransaction, booleandebugEnabled)
        6. ThrowsTransactionException {
        7.  
        8. ... Omitted
        9.  
        10. If (definition. getPropagationBehavior () = TransactionDefinition. PROPAGATION_NESTED ){
        11. If (! IsNestedTransactionAllowed ()){
        12. ThrownewNestedTransactionNotSupportedException (
        13. "Transactionmanagerdoesnotallownestedtransactionsbydefault-" +
        14. "Specify 'nestedtransactionallowed 'propertywithvalue 'true '");
        15. }
        16. If (debugEnabled ){
        17. Logger. debug ("Creatingnestedtransactionwithname [" + definition. getName () + "]");
        18. }
        19. If (useSavepointForNestedTransaction ()){
        20. // CreatesavepointwithinexistingSpring-managedtransaction,
        21. // ThroughtheSavepointManagerAPIimplementedbyTransactionStatus.
        22. // UsuallyusesJDBC3.0savepoints. NeveractivatesSpringsynchronization.
        23. DefaultTransactionStatusstatus =
        24. NewTransactionStatus (definition, transaction, false, false, debugEnabled, null );
        25. Status. createAndHoldSavepoint ();
        26. Returnstatus;
        27. }
        28. Else {
        29. // Nestedtransactionthroughnestedbeginandcommit/rollbackcils.
        30. // UsuallyonlyforJTA: Springsynchronizationmightgetactivatedhere
        31. // Incaseofapre-existingJTAtransaction.
        32. DoBegin (transaction, definition );
        33. BooleannewSynchronization = (this. transactionSynchronization! = SYNCHRONIZATION_NEVER );
        34. ReturnnewTransactionStatus (definition, transaction, true, newSynchronization, debugEnabled, null );
        35. }
        36. }
        37. }
          1. Set the nestedTransactionAllowed attribute of transactionManager to true. Note that the default attribute is false !!!

          Let's look at the AbstractTransactionStatus # createAndHoldSavepoint () method.

          Java code
          1. /**
          2. * Createasavepointandholditforthetransaction.
          3. * @ Throwsorg. springframework. transaction. NestedTransactionNotSupportedException
          4. * Iftheunderlyingtransactiondoesnotsupportsavepoints
          5. */
          6. PublicvoidcreateAndHoldSavepoint () throwsTransactionException {
          7. SetSavepoint (getSavepointManager (). createSavepoint ());
          8. }

            We can see that the Savepoint is implemented by SavepointManager. createSavepoint. Then we can see the level of the SavepointManager and find
            Its Template implementation is JdbcTransactionObjectSupport, commonly used cetcetransactionmanager, HibernateTransactionManager
            TransactonObject in is its subclass.
            JdbcTransactionObjectSupport tells us that two conditions must be met before createSavepoint can be created:

            2. java. SQL. Savepoint must exist, that is, jdk version 1.4 +
            3. Connection. getMetaData (). supportsSavepoints () must be true, that is, jdbc drive must support JDBC 3.0

            After all the preceding conditions are met, you can try PROPAGATION_NESTED.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.