Detailed explanation of Spring @Transactional transaction propagation mechanism

We rarely use the transaction propagation level in our daily work. We simply use transactions and rollbackfor to throw exceptions to solve transaction problems. But in fact, what we use many times is incorrect, or it will cause the transaction granularity to be too large. This article explains the transaction in detail Communication level also allows you to better deal with transactional issues.

1. Spring transaction propagation mechanism

1. What is the transaction propagation mechanism?

For example, method A is a transaction method. Method B is called during the execution of method A. Whether method B has a transaction and method B's different requirements for transactions will affect the specific execution of method A's transaction. At the same time, method A The transaction also has an impact on the transaction execution of method B. The specific impact is determined by the transaction propagation type defined by the two methods.

To put it simply, our method calls usually involve one method calling another, and different methods can have different transactions, so the propagation mechanism refers to how transactions are propagated in multiple methods.

2. Introduction to Spring transaction propagation type Propagation

There are seven communication types in total

  1. Propagation.REQUIRED
  2. Propagation.SUPPORTS
  3. Propagation.MANDATORY
  4. Propagation.REQUIRED_NEW
  5. Propagation.NOT_SUPPORTED
  6. Propagation.NESTED
  7. Propagation.NEVER

This article explains what will happen to multiple @Transactional methods under different communication types from a combination of cases? What impact will different propagation mechanisms have when encountering abnormal situations.

1. Propagation.REQUIRED

This is the default propagation mechanism, the most commonly used one and the default one for @Transactional

If there is no current transaction, create a new transaction yourself. If there is currently a transaction, join this transaction.

// 示例1:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

To put it simply, start a transaction. In the above case, if the main method does not open the transaction, then the sub method will open it. If the main method has @Transactional opened the transaction, the sub method will join the transaction of the outer method, so The execution of the above method will all be rolled back when an exception is encountered.

Result:
All A, B, and C cannot be inserted.

// 示例2:
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

Result:
A is inserted successfully, BC starts a new transaction, and rolls back when an exception occurs, and B and C cannot be inserted.

2. Propagation.SUPPORTS

If there is currently a transaction, join the current transaction. If there is no transaction currently, execute it in a non-transactional method.

// 示例3:
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.SUPPORTS)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

This is very similar to REQUIRED, but the inner sub method transaction depends on the main method. If the main method is enabled, then the inner transaction will be together with the outer transaction. If an exception occurs, all will be rolled back.
Result:
A and B were inserted successfully, but C could not be inserted because an exception occurred.

3. Propagation.MANDATORY

If a transaction currently exists, join the current transaction. If the current transaction does not exist, an exception will be thrown.

// 示例4:
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

The execution result of this situation is that insertA is stored successfully, but insertB and insertC are not stored. The fact that b and c are not stored is not the reason for transaction rollback, but because the main method does not declare a transaction and directly throws the exception required by the transaction when executing the sub method (if the current transaction does not exist, an exception is thrown). So the content in the sub method is not executed at all.

Result:
A is inserted successfully, B and C cannot be inserted, and the method throws an exception.

Then when the main method has a transaction

// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

Result:
All A, B, and C cannot be inserted, and A and B are rolled back.

4. Propagation.REQUIRED_NEW

Creates a new transaction and suspends the current transaction if one exists.

// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
    throw RuntimeException;     //发生异常抛出
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sub(){
    
    
    insertB();  //插入B
    insertC();  //调用C

Because the sub method will start a new transaction, the exception thrown by the main method will not affect the submission of the sub method.
Result:
A failed to insert, B and C can be inserted successfully.

5. Propagation.NOT_SUPPORTED

Always execute in a non-transactional manner. If a transaction currently exists, the current transaction is suspended.

// 示例6:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

Example 6 Because when the main method has a transaction, the current transaction will be suspended.
That is, main runs as a transaction and sub does not run as a transaction .
So the final result:
A because sub throws an exception and the transaction is rolled back and the insertion fails. B because it does not run as a transaction. The insertion was successful, but C encountered an exception and would not be executed subsequently, so the insertion failed.

// 示例7:
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

Example 7: In this case, all methods will not run as transactions.
A and B can be inserted successfully, but C cannot be inserted.

6. Propagation.NEVER

Do not use transactions, throw an exception if the current transaction exists

// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.NEVER)
public void sub(){
    
    
    insertB();  //插入B
    insertC();  //调用C

Because sub is Never, it will not be executed and an error will be thrown directly. Therefore, main's transaction will be rolled back immediately when it encounters an exception.
Therefore, A cannot be inserted after rolling back, and B and C will not be inserted.

7. Propagation.NESTED

If the current transaction exists, it will be executed in a nested (parent-child) transaction, otherwise the operation of REQUIRED is the same (start a transaction)

// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    service.sub();   // 调用其他方法
    throw RuntimeException;     //发生异常抛出
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
    
    
    insertB();  //插入B
    insertC();  //调用C

This is the propagation mechanism that needs to be understood most. It is necessary to understand the nested (father-child) transactions. Main is the parent transaction and sub is the child transaction. If there is an exception in main, all transactions will be rolled back.
Result:
A, B, C are all rolled back

// 示例8:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    
    
    insertA();  // 插入A 
    try {
    
    
   		 service.sub();   // 调用其他方法
	} catch (Exception e) {
    
    
		
	}
	insertD();
}
// 两个Service中调用,如果同一个要注意不能用this调用,事务不会起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
    
    
    insertB();  //插入B
    throw RuntimeException;     //发生异常抛出
    insertC();  //调用C

Example 8: An exception occurs in the sub-transaction and an exception is thrown, but the parent transaction catch is lost. At this time, the main method is equivalent to normal execution without exception, and only the sub-transaction is rolled back.
Result:
A and D were inserted successfully, but B and C failed to be inserted.

  • In the same transaction inside and outside REQUIRED
    , if an exception is thrown anywhere, all will be rolled back together.

  • REQUIRED_NEW
    starts a new transaction internally. The rollback of the external transaction will not affect the internal transaction, and if the internal transaction is thrown and caught, it will not affect the external transaction.

How to memorize quickly? Divide seven into four groups. Remember 221 like this. Two pairs are similar to each other.

Group Propagation type meaning
group1 Propagation.REQUIRED If there is currently a transaction, join the current transaction, otherwise start a new transaction
group1 Propagation.REQUIRED_NEW Start a new transaction regardless of whether there is currently a transaction
group2 Propagation.SUPPORTED If the current transaction exists, join the transaction, otherwise run as a non-transaction
group2 Propagation.NOT_SUPPORTED Always execute in a non-transactional manner. If a transaction currently exists, the current transaction is suspended.
group3 Propagation.NEVER Do not use transactions, throw an exception if the current transaction exists
group3 Propagation.MANDATORY If a transaction currently exists, join the current transaction. If the current transaction does not exist, an exception will be thrown.
group4 Propagation.NESTED Parent-child (nested) transactions, parent rollback is fully rolled back, child rollback does not affect the parent transaction

2. Specific cases

Simply talking about cases is boring, and you may wonder what situations it would be used in at work. Here is an example to explain.

When placing an order, we mainly write the order, then add points, and finally record the log

 @Service
   public class OrderServiceImpl implements OrderService{
    
    
        
        @Transactional
        public void placeOrder(OrderDTO orderDTO){
    
    
                   
            try {
    
    
                pointService.addPoint(Point point);
            } catch (Exception e) {
    
    
               // 记录错误信息
            }
            //省略...
        }
        //省略...
   }
   @Service
   public class PointServiceImpl implements PointService{
    
    
        
        @Transactional(propagation = Propagation.NESTED)
        public void addPoint(Point point){
    
    
                   
            try {
    
    
                recordService.addRecord(Record record);
            } catch (Exception e) {
    
    
               //省略...
            }
            //省略...
        }
        //省略...
   }
   @Service
   public class RecordServiceImpl implements RecordService{
    
    
        
        @Transactional(propagation = Propagation.NOT_SUPPORTED)
        public void addRecord(Record record){
    
    
                   
           
            //省略...
        }
        //省略...
   }

The operation of placing an order will not affect the operation of adding points, so we use NESTED. As long as the order is placed successfully, adding points can succeed or fail. If it fails, an error message will be sent for subsequent compensation.
We can have or without logging. We can set it to NOT_SUPPORTED to disable transactions, so that the transaction method can be as streamlined as possible and avoid a large transaction method.

Summarize

This article explains the seven propagation mechanisms of Spring transactions. We can avoid too long transaction methods based on specific types and specific settings. The more library tables called in a transaction, the more likely it is to cause deadlock, so we need to use Specific needs to be used separately.

Guess you like

Origin blog.csdn.net/qq_40922616/article/details/129919921