Spring transaction propagation behavior:

Spring supports seven kinds of transaction propagation behaviors, which determine the transaction boundary between the client and the called side (in general terms, the complex transaction boundary control is generated when multiple services with transaction control are called each other)

o

Propagation behavior

meaning

PROPAGATION_REQUIRED (REQUIRED in the XML file)

Indicates that the current method must run in a transaction context. If a client has a transaction in progress, the called end will run in that transaction, otherwise a transaction will be restarted. (If an exception occurs on the called side, both the calling side and the called side transaction are rolled back)

PROPAGATION_SUPPORTS(SUPPORTS in XML file)

Indicates that the current method does not need to have a transaction context, but it can run in a transaction if one exists

PROPAGATION_MANDATORY(MANDATORY in the XML file)

Indicates that the current method must run in a transaction and throws an exception if there is no transaction

NESTED(in XML files)

Indicates that if the current method has a transaction running, the method should run in a nested transaction that can be committed or rolled back independently of the encapsulated transaction. If an encapsulated transaction exists and the outer transaction throws an exception to roll back, then the inner transaction must be rolled back, whereas the inner transaction does not affect the outer transaction. If the encapsulated transaction does not exist, it is the same as PROPAGATION_REQUIRED

PROPAGATION_NEVER (NEVER in the XML file)

Represents when a method should not run in a transaction, and throws an exception if one exists

PROPAGATION_REQUIRES_NEW(REQUIRES_NEW in XML file)

Indicates that the current method must run in its own transaction. A new transaction will be started, and if an existing transaction is running, the method will be suspended at runtime until the new transaction is committed or rolled back.

PROPAGATION_NOT_SUPPORTED (NOT_SUPPORTED in the XML file)

Indicates that the method should not run in a transaction. If a transaction is running, it will be suspended at runtime until the transaction is committed or rolled back

Spring configuredeclarative transactions:

* Configure DataSource * Configure transaction manager * propagation properties of transactions * which classes and methods use transactions

The Spring configuration file always consists of three parts for the transaction configuration: DataSource, TransactionManager, and proxy mechanism. Regardless of the configuration, only the proxy mechanism changes.

The DataSource and TransactionManager parts only change according to the data access mode. For example, when using Hibernate for data access, the DataSource is SessionFactory. The realization of the TransactionManager for HibernateTransactionManager.

Depending on the proxy mechanism, Spring transactions can be configured in several different ways:

The first way: each Bean has a proxy

The second way: all beans share a proxy base class

Third way: use interceptors

Fourth way: interceptors configured with tx tags

Fifth way: full notes

Note:

1. Spring’s transaction boundaries start before the business methods are called. After the business methods are executed, commit or rollback is performed, depending on whether runtime exceptions are thrown.

1.PROPAGATION_REQUIRED

We add Propagation.REQUIRED to User1Service and User2Service methods.

User1Service method:

@service public class User1ServiceImpl implements User1Service {// @Override @Transactional(propagation = Propagation.REQUIRED) public void addRequired(User1 user){ user1Mapper.insert(user); }}Copy the code

User2Service method:

@service public class User2ServiceImpl implements User2Service {// @Override @Transactional(propagation = Propagation.REQUIRED) public void addRequired(User2 user){ user2Mapper.insert(user); } @Override @Transactional(propagation = Propagation.REQUIRED) public void addRequiredException(User2 user){ user2Mapper.insert(user); throw new RuntimeException(); }}Copy the code

1.1 a scene

The scenario peripheral method does not open a transaction.

Verification Method 1:

    @Override
    public void notransaction_exception_required_required(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequired(user2);
        
        throw new RuntimeException();
    }Copy the code

Verification Method 2:

    @Override
    public void notransaction_required_required_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiredException(user2);
    }Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Zhang SAN”, “Li Si” are inserted. The external method does not open the transaction, and the insert “Zhang SAN” and “Li Si” methods run independently in their own transaction. The exception of the external method does not affect the internal insert “Zhang SAN” and “Li Si” method independent transaction.
2 “Joe” is inserted, “Joe” is not inserted. The peripheral method has no transaction, and the insert “Tom” and “Tom” methods run independently in their own transactions, so the insert “Tom” method throws an exception and only rolls back the insert “Tom” method, the insert “Tom” method is not affected.

Conclusion: Using these two methods we prove that the peripheral method does not open the transactionPropagation.REQUIREDDecorated internal methods open their own transactions independently of each other.

1.2 scenario 2

Peripheral methods open transactions, which is a highly used scenario.

Verification Method 1:

   @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_required(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequired(user2);
        
        throw new RuntimeException();
    }Copy the code

Verification Method 2:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_required_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiredException(user2);
    }Copy the code

Verification Method 3:

    @Transactional
    @Override
    public void transaction_required_required_exception_try(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        try {
            user2Service.addRequiredException(user2);
        } catch (Exception e) {
            System.out.println("Method rollback"); }}Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Zhang SAN”, “Li Si” are not inserted. The outer method starts the transaction, the inner method joins the outer method transaction, the outer method rolls back, and the inner method rolls back.
2 “Zhang SAN”, “Li Si” are not inserted. The outer method starts the transaction, the inner method joins the outer method transaction, the inner method throws an exception and rolls back, and the outer method senses the exception and rolls back the whole transaction.
3 “Zhang SAN”, “Li Si” are not inserted. The outer method opens the transaction, the inner method joins the outer method transaction, the inner method throws an exception and rolls back, even if the method is caught and not sensed by the outer method, the whole transaction rolls back.

Conclusion: The above experimental results show that in the case of the peripheral method to open transactionsPropagation.REQUIREDThe decorated inner method is added to the outer method’s transaction, allPropagation.REQUIREDThe modified inner and outer methods are part of the same transaction, and if one method is rolled back, the entire transaction is rolled back.

2.PROPAGATION_REQUIRES_NEW

We add the Propagation.REQUIRES_NEW attribute to the corresponding methods of User1Service and User2Service. User1Service method:

@service public class User1ServiceImpl implements User1Service {// @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void addRequiresNew(User1 user){ user1Mapper.insert(user); } @Override @Transactional(propagation = Propagation.REQUIRED) public void addRequired(User1 user){ user1Mapper.insert(user); }}Copy the code

User2Service method:

@service public class User2ServiceImpl implements User2Service {// @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void addRequiresNew(User2 user){ user2Mapper.insert(user); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void addRequiresNewException(User2 user){ user2Mapper.insert(user); throw new RuntimeException(); }}Copy the code

2.1 a scene

The peripheral method did not start the transaction.

Verification Method 1:

    @Override
    public void notransaction_exception_requiresNew_requiresNew(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequiresNew(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiresNew(user2);
        throw new RuntimeException();
        
    }Copy the code

Verification Method 2:

    @Override
    public void notransaction_requiresNew_requiresNew_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequiresNew(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiresNewException(user2);
    }Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Joe” in, “Joe” in. The outer method does not have a transaction, and the insert “Zhang SAN” and “Li Si” methods run independently in their own transaction. The outer method throws an exception and the rollback does not affect the inner method.
2 “Joe” is inserted, “Joe” is not inserted If the peripheral method does not start the transaction, insert the “Three” method and insert the “four” method respectively start their own transaction, insert the “four” method throws an exception rollback, other transactions are not affected.

Conclusion: Using these two methods we prove that the peripheral method does not open the transactionPropagation.REQUIRES_NEWDecorated internal methods open their own transactions independently of each other.

2.2 scenario 2

The peripheral method starts the transaction.

Verification Method 1:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_exception_required_requiresNew_requiresNew(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiresNew(user2);
        
        User2 user3=new User2();
        user3.setName("Fifty");
        user2Service.addRequiresNew(user3);
        throw new RuntimeException();
    }Copy the code

Verification Method 2:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiresNew(user2);
        
        User2 user3=new User2();
        user3.setName("Fifty");
        user2Service.addRequiresNewException(user3);
    }Copy the code

Verification Method 3:

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void transaction_required_requiresNew_requiresNew_exception_try(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addRequired(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addRequiresNew(user2);
        User2 user3=new User2();
        user3.setName("Fifty");
        try {
            user2Service.addRequiresNewException(user3);
        } catch (Exception e) {
            System.out.println("Rollback"); }}Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Zhang SAN” is not inserted, “Li Si” is inserted, “Wang Wu” is inserted. The external method opens the transaction, inserts “Zhang SAN” method and the external method a transaction, inserts “Li Si” method, inserts “Wang Wu” method respectively in the independent new transaction, the external method throws an exception only rollback and the external method of the same transaction method, so inserts “Zhang SAN” method rollback.
2 “Zhang SAN” is not inserted, “Li Si” is inserted, “Wang Wu” is not inserted. The peripheral method starts the transaction, inserts the “Zhang SAN” method and the peripheral method a transaction, inserts the “Li Si” method, inserts the “Wang wu” method respectively in the independent new transaction. Insert “king” method to throw an exception, first insert “king” method transaction is rolled back, the exception continues to throw is perceived by the peripheral method, the peripheral method transaction is also rolled back, so insert “three” method is also rolled back.
3 “Zhang SAN” is inserted, “Li Si” is inserted, “Wang Wu” is not inserted. The peripheral method starts the transaction, inserts the “Zhang SAN” method and the peripheral method a transaction, inserts the “Li Si” method, inserts the “Wang wu” method respectively in the independent new transaction. Insert the “king five” method to throw an exception, first of all, the transaction inserted into the “king five” method is rolled back, the exception caught will not be perceived by the peripheral method, the peripheral method transaction does not roll back, so insert the “king three” method successfully inserted.

Conclusion: In the case of a peripheral method opening a transactionPropagation.REQUIRES_NEWDecorated internal methods still open independent transactions independently and independently of external method transactions, with internal methods and external method transactions independent of each other.

3.PROPAGATION_NESTED

We add the Propagation.NESTED attribute to the corresponding methods of User1Service and User2Service. User1Service method:

@service public class User1ServiceImpl implements User1Service {// @Override @Transactional(propagation = Propagation.NESTED) public void addNested(User1 user){ user1Mapper.insert(user); }}Copy the code

User2Service method:

@service public class User2ServiceImpl implements User2Service {// @Override @Transactional(propagation = Propagation.NESTED) public void addNested(User2 user){ user2Mapper.insert(user); } @Override @Transactional(propagation = Propagation.NESTED) public void addNestedException(User2 user){ user2Mapper.insert(user); throw new RuntimeException(); }}Copy the code

3.1 a scene

The scenario peripheral method does not open a transaction.

Verification Method 1:

    @Override
    public void notransaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addNested(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }Copy the code

Verification Method 2:

    @Override
    public void notransaction_nested_nested_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addNested(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addNestedException(user2);
    }Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Zhang SAN”, “Li Si” are inserted. The external method does not open the transaction, and the insert “Zhang SAN” and “Li Si” methods run independently in their own transaction. The exception of the external method does not affect the internal insert “Zhang SAN” and “Li Si” method independent transaction.
2 “Joe” is inserted, “Joe” is not inserted. The peripheral method has no transaction, and the insert “Tom” and “Tom” methods run independently in their own transactions, so the insert “Tom” method throws an exception and only rolls back the insert “Tom” method, the insert “Tom” method is not affected.

Conclusion: Using these two methods we prove that the peripheral method does not open the transactionPropagation.NESTEDandPropagation.REQUIREDIn the same way, decorated internal methods open their own transactions independently of each other.

3.2 scenario 2

The peripheral method starts the transaction.

Verification Method 1:

    @Transactional
    @Override
    public void transaction_exception_nested_nested(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addNested(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addNested(user2);
        throw new RuntimeException();
    }Copy the code

Verification Method 2:

    @Transactional
    @Override
    public void transaction_nested_nested_exception(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addNested(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        user2Service.addNestedException(user2);
    }Copy the code

Verification Method 3:

    @Transactional
    @Override
    public void transaction_nested_nested_exception_try(){
        User1 user1=new User1();
        user1.setName("Zhang");
        user1Service.addNested(user1);
        
        User2 user2=new User2();
        user2.setName("Bill");
        try {
            user2Service.addNestedException(user2);
        } catch (Exception e) {
            System.out.println("Method rollback"); }}Copy the code

The verification methods are executed respectively, and the results are as follows:

Verification Method Number Database results Results analysis
1 “Zhang SAN”, “Li Si” are not inserted. The outer method starts the transaction, the inner transaction is a child of the outer transaction, the outer method rolls back, and the inner method rolls back.
2 “Zhang SAN”, “Li Si” are not inserted. The outer method starts the transaction, the inner transaction is a child of the outer transaction, the inner method throws an exception and rolls back, and the outer method senses the exception and causes the whole transaction to roll back.
3 “Zhang SAN” inserted, “Li Si” not inserted. The outer method opens the transaction, and the inner transaction is a sub-transaction of the outer transaction. Insert the “zhang SAN” inner method throws an exception, which can be rolled back to the sub-transaction separately.

Conclusion: The above experimental results show that in the case of the peripheral method to open transactionsPropagation.NESTEDThe modified inner method is a subtransaction of an external transaction. The outer main transaction is rolled back, and the subtransaction must be rolled back, while the inner subtransaction can be rolled back independently without affecting the outer main transaction and other subtransactions

4. The REQUIRED the REQUIRES_NEW, NESTED similarities and differences

By comparing “scenario 2” and “Scenario 2”, we know that the inner methods modified by NESTED and REQUIRED are peripheral method transactions. If the peripheral method throws an exception, the transactions of both methods will be rolled back. But REQUIRED joins the enclosing method transaction, so it belongs to the same transaction as the enclosing method transaction, which will be rolled back once the REQUIRED transaction throws an exception. NESTED is a subtransaction of a peripheral method and has a separate savepoint. Therefore, an exception thrown by a NESTED method is rolled back and does not affect the transactions of the peripheral method.

By comparing “2.2 Scenario 2” and “3.2 Scenario 2”, we can know that both REQUIRES_NEW and internal method transactions can be rolled back without affecting peripheral method transactions. However, because NESTED transactions are NESTED, after the rollback of the peripheral method, the child transactions of the peripheral method transaction are also rolled back. REQUIRES_NEW is implemented by starting a new transaction. The internal and peripheral transactions are two transactions, and the rollback of the peripheral transaction does not affect the internal transaction.

5. Other transaction communication behaviors

Due to the length of this article, other tests of transaction propagation behavior are not described here. Interested readers can go to the source code to find the corresponding test code and result interpretation. Portal: github.com/TmTse/tran…

Simulation case

With all this talk about transaction propagation behavior, how do we apply it in practice? Here’s an example:

Suppose we have a registered method that calls the method that adds credits. If we want to add credits without affecting the registration process (i.e., adding credits fails to roll back the registration method), we would write:

@Service public class UserServiceImpl implements UserService { @Transactional public void register(User user){ try { membershipPointService.addPoint(Point point); } catch (Exception e) {// omit... } // omit... } // omit... }Copy the code

We also specify that if a registration failure affects the addPoint() method (which is rolled back as well as the addPoint() method), then the addPoint() method needs to be implemented like this:

@Service public class MembershipPointServiceImpl implements MembershipPointService{ @Transactional(propagation = Propagation.NESTED) public void addPoint(Point point){ try { recordService.addRecord(Record record); } catch (Exception e) {// omit... } // omit... } // omit... }Copy the code

We notice that the addRecord() method is also called in addPoint(), which is used for logging. His implementation is as follows:

@Service public class RecordServiceImpl implements RecordService{ @Transactional(propagation = Propagation.NOT_SUPPORTED) public void addRecord(Record Record){ } // omit... }Copy the code

In the addRecord() method “propagation = propagation.not_supported”, because there is no precision in logging, you can have one more or one less. So neither the addRecord() method itself nor the peripheral addPoint() method will cause the addRecord() method to roll back, and neither the addRecord() method will affect the execution of the peripheral addPoint() method.

Through this example I believe that you have a more intuitive understanding of the use of transaction propagation behavior, through the combination of various attributes can really make our business implementation more flexible and diverse.

conclusion

Through the above introduction, I believe you have a deeper understanding of Spring transaction propagation behavior, I hope you can help with daily development work.