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 Concepts:

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();
    
 }
 
 @Transaction(Propagation=XXX)
 public void methodB(){
    
 }
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

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

Code verification:

The code in this article is presented in two of the traditional three layers, namely the Service and Dao layers. Spring is responsible for dependency injection and annotated transaction management, and the Dao layer is implemented by Mybatis. You can use it any way you 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.

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 Scenario 1 The peripheral method in this scenario does not enable transactions.

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:

Conclusion: Through these two methods, we proved that the internal methods propagating.REQUIRED will open their own transactions when the external methods do not open transactions, and the opened transactions are independent 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:

Conclusion: Propagation.REQUIRED and peripheral methods belong to the same transaction. As long as one method is rolled back, the Propagation. 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. Elegantly-named setName (" zhang "); user1Service.addRequiresNew(user1); User2 user2=new User2(); User2. Elegantly-named setName (" li si "); user2Service.addRequiresNew(user2); throw new RuntimeException(); }Copy the code

Verification Method 2:

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

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

Conclusion: By using these two methods, we proved that the internal method propagating.REQUIRES_NEW will open its own transaction when the external method does not open the transaction, and the opened transaction is independent 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:

Conclusion: The internal methods of Propagation.REQUIRES_NEW still open independent transactions and are independent of external method transactions. Internal methods and external method transactions are independent and do not interfere with 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. 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:

Results showed that both methods performed the same function when peripheral methods failed to open transactions. Both 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:

Conclusion: To illustrate the Propagation of NESTED internal methods in the case of a peripheral method enabled transaction, a NESTED internal method belongs to the subtransaction of an external transaction. The NESTED internal method can be rolled back independently without affecting the peripheral main transaction and other subtransactions

4. The REQUIRED the REQUIRES_NEW, NESTED similarities and differences

By comparing “1.2 Scenario 2” and “3.2 Scenario 2”, we can see that:

NESTED and REQUIRED modified internal methods are peripheral method transactions, and both methods’ transactions are rolled back if the peripheral method throws an exception. 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 see that:

Both NESTED and REQUIRES_NEW can roll back internal method transactions 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.

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.

Write in the last

Welcome to pay attention to my public number [calm as code], massive Java related articles, learning materials will be updated in it, sorting out the data will be placed in it.

If you think it’s written well, click a “like” and add a follow! Point attention, do not get lost, continue to update!!