preface

Spring specifies seven types of transaction propagation behavior in the TransactionDefinition interface. Transaction propagation behavior is a transaction-enhancement feature unique to the Spring framework that does not belong to the actual transaction provider database behavior. This is a powerful toolkit that Spring provides for us, and using transactional propagation rows can provide a lot of convenience for our development efforts. But there are a lot of misconceptions about it, and you’ve heard the myth that it’s best not to nest a service method transaction. To use tools properly, you need to understand them first. This article introduces seven transaction propagation behaviors in detail and presents them as code examples.

Basic concept

1. What is transaction propagation behavior?

Transaction propagation behavior is used to describe how a transaction propagates when a method modified by one transaction propagation behavior is nested into another method.

In pseudocode:

 public void methodA(){
    methodB();
    //doSomething
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    //doSomething
 }Copy the code

Propagation of methodB() is determined by @Transaction(Propagation=XXX). The Propagation behavior of methodB() is determined by @Transaction(Propagation=XXX). It is important to note that methodA() does not start the transaction, and a method that propagates the behavior modification does not have to be called in the outer method that starts the transaction.

2. Seven transaction propagation behaviors in Spring

Type of transaction propagation behavior instructions
PROPAGATION_REQUIRED If there is no transaction, create a new one. If there is already one, join it. This is the most common choice.
PROPAGATION_SUPPORTS Current transactions are supported, and non-transactionally executed if there are none.
PROPAGATION_MANDATORY Use the current transaction and throw an exception if there is no transaction currently.
PROPAGATION_REQUIRES_NEW Create a new transaction and suspend the current transaction if one exists.
PROPAGATION_NOT_SUPPORTED Performs the operation nontransactionally, suspending the current transaction if one exists.
PROPAGATION_NEVER Executes nontransactionally, throwing an exception if a transaction currently exists.
PROPAGATION_NESTED If a transaction currently exists, it is executed within a nested transaction. If there are no transactions currently, an operation similar to PROPAGATION_REQUIRED is performed.

The definition is very simple and easy to understand, so let’s move on to the code test section to verify our understanding.

Code validation

The code in the traditional three layers structure of two layers, namely the Service and the Dao layer, is responsible by the Spring dependency injection and annotation type transaction management, Dao layer by Mybatis implementation, you can also use any way like, for example, Hibernate, JPA, JDBCTemplate, etc. The database is a MySQL database, but you can use any database that supports transactions without affecting the validation results.

First we create two tables in the database:

user1

CREATE TABLE `user1` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL DEFAULT '',
  PRIMARY KEY(`id`)
)
ENGINE = InnoDB;Copy the code

user2

CREATE TABLE `test`.`user2` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL DEFAULT '',
  PRIMARY KEY(`id`)
)
ENGINE = InnoDB;Copy the code

Then write the corresponding Bean and DAO layer code:

User1

public class User1 { private Integer id; private String name; // The get and set methods omit... }Copy the code

User2

public class User2 { private Integer id; private String name; // The get and set methods omit... }Copy the code

User1Mapper

public interface User1Mapper { int insert(User1 record); User1 selectByPrimaryKey(Integer id); // Other ways to omit... }Copy the code

User2Mapper

public interface User2Mapper { int insert(User2 record); User2 selectByPrimaryKey(Integer id); // Other ways to omit... }Copy the code

Finally, the verification code is implemented by the Service layer, which is listed in the following cases.

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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequired(user2); throw new RuntimeException(); }Copy the code

Verification Method 2:

@Override public void notransaction_required_required_exception(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequiredException(user2); }Copy the code

Verification Method 3:

@Transactional @Override public void transaction_required_required_exception_try(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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.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

2.1 a scene

The peripheral method did not start the transaction.

Verification Method 1:

@Override public void notransaction_exception_nested_nested(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addNested(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addNested(user2); throw new RuntimeException(); }Copy the code

Verification Method 2:

@Override public void notransaction_nested_nested_exception(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addNested(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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 “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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequiresNew(user2); User2 user3=new User2(); User3. Elegantly-named setName (" detective "); 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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequiresNew(user2); User2 user3=new User2(); User3. Elegantly-named setName (" detective "); 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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequiresNew(user2); User2 user3=new User2(); User3. Elegantly-named setName (" detective "); 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

3.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. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequired(user2); throw new RuntimeException(); }Copy the code

Verification Method 2:

@Override public void notransaction_required_required_exception(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addRequired(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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.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. Elegantly-named setName (" zhang "); user1Service.addNested(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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. Elegantly-named setName (" zhang "); user1Service.addNested(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addNestedException(user2); }Copy the code

Verification Method 3:

@Transactional @Override public void transaction_nested_nested_exception_try(){ User1 user1=new User1(); User1. Elegantly-named setName (" zhang "); user1Service.addNested(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); 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….

conclusion

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