Spring Series (9) --- Talk about the isolation level and propagation mechanism of transactions in Spring

A transaction is to encapsulate a group of operations into an execution unit, that is, to encapsulate them together, either all succeed or all fail; then why use a transaction??? For example, we go to the supermarket to shop and spend 500 yuan, and then use Alipay to make a payment. After my payment is successful, the account will be - 500, which should be + 500 in the account of the supermarket; but if there is no concept of transaction, it is possible that after my payment is successful, the supermarket does not have + 500, which means that my 500 yuan is for no reason disappeared. Therefore, transactions are born to solve such problems, this group of operations either succeed together or fail together
.

1 transaction

1.1 Manual transaction


  The implementation of transactions in Spring is mainly divided into two categories: manual operation transactions/declarative automatic commit transactions; among them, manual operation transactions are similar to operation transactions in MySQL, and there are three important operation steps: start transaction (start transaction) --> commit transaction (commit) --> rollback transaction (rollback)
.

Two objects are built into SpringBoot to operate transactions:

  • DataSourceTransactionManager: used to obtain transactions, that is, open transactions, commit transactions and rollback transactions;
  • TransactionDefinition: The attribute of the transaction. When obtaining the transaction, the TransactionDefinition needs to be passed in to obtain a transaction TransactionStatus.
@RestController
@RequestMapping("/user")
public class UserController {
    
    

    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;


    @RequestMapping("/add")
    public int add(UserInfo userInfo) {
    
    
        // 非空校验
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
    
    
            return 0;
        }
        // 开启事务 (获取事务) (将 TransactionDefinition 传递进去从而获得一个事务 TransactionStatus)
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        System.out.println("add 受影响的行数: " + result);
        // 提交事务 / 回滚事务
        //transactionManager.rollback(transactionStatus); // 回滚事务
        transactionManager.commit(transactionStatus);
        return result;
    }
}

It is cumbersome to manually operate transactions through the above code, although it can also be achieved, but there is a simpler way, so it is recommended to use declarative transactions in future code.


1.2 Declarative transaction


The implementation of declarative transactions is relatively simple. You only need to add the annotation @Transactional on the method that requires transactions. There is no need to open and submit transactions. When entering the method, the transaction will be automatically started, and the transaction will be automatically committed after the method is executed. Even if an exception occurs in the middle, the transaction will be automatically rolled back.

@Transactional // 在进入方法之前, 自动开启事务;
// 在方法执行完之后, 自动提交事务; 如果出现异常, 则自动回滚事务
@RequestMapping("/add")
public int add(UserInfo userInfo) {
    
    
    // 非空校验
    if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
            || !StringUtils.hasLength(userInfo.getPassword())) {
    
    
        return 0;
    }
    int result = userService.add(userInfo);
    System.out.println("add 受影响的行数: " + result);
    // 下面这句代码就会出现异常 (0 不能做除数), 但是会回滚操作, 并不会污染数据
    //int num = 10 / 0;
    return result;
}

1.3 Scope of transaction and parameter description


@Transactional Scope:

  • When modifying methods: Note that it can only be applied to public methods, otherwise it will not take effect;
  • Modified class: Indicates that the annotation is valid for all public methods in the class; it is actually recommended to use it when modifying methods.

@Transactional parameter description:

insert image description here

1.4 The working principle and implementation ideas of transactions


How @Transactional works

  • @Transactional is implemented based on AOP, and AOP is implemented using dynamic proxy. If the target object implements the interface, the dynamic proxy of JDK will be used by default. If the target object does not implement the interface, CGLIB dynamic proxy will be used.
  • @Transactional Before starting to execute the business, start the transaction through the proxy, submit the transaction after the execution is successful, and roll back the transaction if there is an exception in the middle.

@Transactional implementation ideas:

insert image description here

@Transactional specific execution details:

insert image description here

2 transaction isolation level


Transaction isolation level: Setting the isolation level is used to ensure that the execution of multiple concurrent transactions is more controllable and more in line with operator expectations.

Transactions mainly have four characteristics : Atomicity (either complete at one time or not at all) / Consistency (the integrity of the database from the beginning to the end of the transaction is not destroyed) / Persistence (after the transaction processing is completed, the modification of the data is permanent, even if the system fails, it will not be destroyed) / Isolation.

  Among the four characteristics of transactions, only isolation is allowed to be set, and transaction isolation refers to the ability of the database to allow multiple concurrent transactions to read, write and modify data at the same time. The function of isolation is to prevent data inconsistency caused by cross-execution when multiple transactions are executed concurrently. It is mainly divided into five levels:

  • DEFAULT: use the transaction isolation level of the connected database;
  • READ_UNCOMMITTED: Read uncommitted; transactions at this isolation level can see uncommitted data in other transactions, because uncommitted data in other transactions can be read, and uncommitted data may be rolled back, so the data read at this level is called dirty data, and this problem is dirty reading;
  • READ_COMMITTED: Read committed; this isolation level can read the data of the committed transaction, and of course there will be no dirty reading; but because the results of other transaction submissions can be read during the execution of the transaction, different results may be obtained in the same SQL query at different times, which is non-repeatable read;
  • REPEATABLE_READ: Repeatable read; MySQL's default transaction isolation level; it can guarantee the same transaction results of multiple queries are consistent; however, when a transaction of this level is executing, another transaction inserts a certain piece of data, but because the results of each query are the same, this data will not be queried, that is to say, the data has been inserted but cannot be read.
  • SERIALIZABLE: Serialization; the highest level of transaction isolation, which will enforce transaction ordering so that there will be no conflicts, thereby solving the problems of dirty reads/non-repeatable reads and phantom reads; but the execution efficiency is relatively low, and there are not many usage scenarios.

insert image description here
When there is a conflict between the transaction isolation level set in Spring and the transaction isolation level of the connected database, Spring will prevail; after all, the implementation of the transaction isolation level mechanism in Spring is based on the transaction isolation level supported by the connection database. In short, the transaction isolation level is a strategy to prevent other transactions from affecting the current transaction execution.


3 Transaction propagation mechanism


The transaction isolation level solves the problem of multiple transactions calling the database at the same time; and the transaction propagation mechanism solves the problem of a transaction passing in multiple nodes or methods, as shown below:
insert image description here

insert image description here

The difference between nested transaction NESTED and joining transaction REQUIRED:

  • If the entire transaction is executed successfully, the results of the two are the same;
  • If half of the transaction execution fails, the entire REQUIRED transaction will be rolled back; while the nested object will be partially rolled back, without affecting the execution result of the previous method (because there is a concept of savepoint in the transaction, after the nested transaction enters, it is equivalent to creating a new savepoint, and the rollback only rolls back to the current savepoint, so the previous transaction is not affected).

Guess you like

Origin blog.csdn.net/Onion_521257/article/details/129967378