Detailed transaction: Mysql transaction, Spring transaction, distributed transaction

Detailed transaction: Mysql transaction, Spring transaction, distributed transaction

[1] Mysql transaction

[1] 4 isolation levels

(1) Serialization
Only one transaction can be executed at the same time, and all transactions are executed serially
(2) Repeatable read
The default isolation level of the InnoDB engine, the changes of the current transaction will not be seen by the outside world (3) The current transaction can
be submitted for reading
See the modification of data by other transactions
(4) uncommitted read

【2】Test case

(1) View the global isolation level and view the isolation level of the current session (command of mysql5.7 version)
insert image description here(2) Modify the isolation level of the current session
insert image description here(3) Test serialization
first execute the first two lines of code of session 1, but If you do not execute the commit transaction
insert image description hereand then execute the first two lines of code of session 2, and do not execute the commit transaction
insert image description here, you will find that the transaction of transaction 2 cannot be opened. You must wait for the previous transaction to be committed and
insert image description hereexecute the commit of session 1 before session 2 can be opened. affairs

The highest security, but the efficiency is not good

(4) Test repeatable read
Modify the isolation level of the current session
insert image description hereand then session 1 starts the transaction, and queries the value, but does not commit the transaction. At this time, the money result of the query is 99
insert image description here, then session 2 starts the transaction, and modifies the value (at this time, two transactions can be carried out at the same time), and modifies the money value to 100. But do not submit the transaction
insert image description here
and go back to session 1, execute the query statement, the money result is still 99.
insert image description hereAfter the transaction of session 1 is submitted, re-execute the query, and the money result is 100 (at this time, the transaction of session 2 has not been submitted yet)

That is to say, the result of the query in the current transaction remains unchanged, even if the data has been modified by other transactions, the modification of the current transaction to other transactions is invisible

(5) Phantom reading caused by repeatable reading
The transaction of session 2 is committed successfully, but because the transaction is not visible before it is committed, the value read by session 1 may still be different from the final actual value.

(6) Test commit read (non-repeatable read)
After the transaction of session 1 is submitted, the value modified by session 2 must be found after the transaction of session 2 is also submitted. Even if session 2 changes money to 100, as long as session 2 has not committed the transaction, session 1 will always read 99.

Phantom reads may also occur at this isolation level.

The reason for phantom reading caused by repeatable reading is that the transaction of session 2 is committed successfully, which causes the value read by session 1 to be different from the final actual value. The reason for the phantom reading caused by non-repeatable reading is that session 2 has not committed the transaction after modifying the value, and session 1 can read the modified value of 100, but if session 2 rolls back, the final real result should still be 99 . But session 1 has fetched 100 and used

For example, after the two sessions start the transaction, they both execute the select statement first, and both find that there is no such data, and then execute an insert method to insert the data. After executing the insert, it will be locked, and the second transaction will be locked when it comes in.

(7) Uncommitted Read

[2] The principle of Mysql transaction implementation

[1] What effect does the transaction want to achieve?

The effect that the transaction wants to achieve is mainly reliability and concurrent processing
(1) Reliability
The database must ensure that when an insert or update operation is performed, if an exception is thrown or data crashes, the operation of the data needs to be consistent. To achieve this effect, You need to know the state before and after modification, so there are undo log and redo log

(2) Concurrent processing
means that when multiple concurrent requests come, and one of them is for data modification operations, it will have an impact. In order to avoid reading dirty data, it is necessary to isolate the read and write between transactions. As for The extent of isolation depends on the scenario of the business system. To achieve this, the isolation level of Mysql must be used.

【2】Introduction to redo log and undo log

(1) What is redo log
? Redo log is called redo log, which is used to achieve transaction persistence. The log file consists of two parts: redo log buffer (redo log buffer) and redo
insert image description here

start transaction;
select balance from bank where name="zhangsan";
// 生成 重做日志 balance=600
update bank set balance = balance - 400; 
// 生成 重做日志 amount=400
update finance set amount = amount + 400;
commit;

insert image description here
(2) What is the function of redo log?
In order to improve performance, mysql will not synchronize every modification to the disk in real time, but will first save it in the Buffer Pool (buffer pool), use this as a cache, and then use a background thread to communicate between the buffer pool and the disk. synchronization of

So the question is, what if there is a downtime or power outage before synchronization? It has not had time to execute the red operation in the above figure, which will result in the loss of some modification information of the committed transaction.

Therefore, the redo log is introduced to record the modification information of the transaction that has been successfully submitted, and the redo log will be persisted to the disk. After the system is restarted, the redo log can be read to restore the latest data

(3) Summarize that
redo log is used to restore data and guarantee the persistence of committed transactions

(1) What is undo log?
The undo log is called the rollback log, which is used to record the information before the data is modified. It is just the opposite of the redo log lock record mentioned earlier, and the redo log records the information after the data has been modified. The undo log mainly records the logical changes of the data. In order to roll back the previous operation when errors occur, it is necessary to record all the previous operations, and then roll back when an error occurs.

insert image description hereBefore writing data or modifying data, the information before modification will be recorded in the undo log

(2) What is the function of undo log?
The undo log records the data information of the previous version of the transaction modification, so if you add a rollback due to a system error or rollback operation, you can roll back to the state before it was not modified according to the information in the undo log.

(3) Summarize that
the undo log is used to roll back data to ensure the atomicity of uncommitted transactions

[3] mysql lock technology and MVCC foundation

(1) mysql lock technology

When there are multiple requests to read the data in the table, no action can be taken, but when there are read requests and write requests in multiple requests, there must be a measure to control concurrency, otherwise it is likely to cause The data is inconsistent.

(1) Read-write locks
It is very simple to solve the above problems. You only need to use a combination of two locks to control read and write requests. These two locks are called:
1-shared lock (shared lock), also known as "Read lock"
read lock can be shared, or multiple read requests can share a lock to read data without causing blocking

2- Exclusive lock (exclusive lock), also known as "write lock"
write lock will exclude all other requests to acquire locks, and block consistently until the write is completed to release the lock
insert image description here
(2) Summary
Through the read-write lock, you can read and read Parallel, but it cannot write and read, and write and write in parallel. Transaction isolation is achieved based on read-write locks.

(2) MVCC foundation

MVCC (MultiVersion Concurrency Control) is called multi-version concurrency control.

The implementation of MVCC in mysql relies on undo log and read view
(1) undo log: multiple versions of a row of data are recorded in undo log
(2) read view: used to judge the visibility of the current version of data
insert image description here

[4] Implementation principle of transaction

(1 Introduction

The redo log, rollback log and lock technology mentioned above are the basis for implementing transactions.
(1) The atomicity of the transaction is realized through the undo log
(2) The persistence of the transaction is realized through the redo log
(3) The isolation of the transaction is realized through (read-write lock + MVCC)
(4) The big boss consistency of the transaction is achieved through atomicity + persistence + isolation

Therefore, the purpose of atomicity, persistence, and isolation is to ensure data consistency. In short, ACID is just a concept, and the ultimate goal of transactions is to ensure the reliability and consistency of data.

(2) Realization of atomicity

(1) What is atomicity?
A transaction must be regarded as an indivisible minimum unit of work. All operations in a transaction are either submitted successfully or rolled back in failure. It is impossible for a transaction to perform only some of the operations , which is the atomicity of transactions.

The above passage is taken from the definition of atomicity in the book "High Performance MySQL". Atomicity can be summarized as achieving either all failures or all successes.

I believe everyone understands the above concepts, so how is the database realized? It is through the rollback operation.
The so-called rollback operation means that when an error occurs or the rollback statement is explicitly executed, the data needs to be restored to its original appearance, so at this time, the undo log needs to be used for rollback. Next, let’s take a look at how the undo log implements transaction atomicity. how sex works

(2) Generation of undo log
Assume that there are two tables bank and finance. The original data in the table is shown in the figure. The undo log generated when inserting, deleting and updating operations is shown in the figure below: From the figure above, we can
insert image description hereunderstand Data changes are accompanied by the generation of rollback logs:
1-The rollback log of the data before modification (zhangsan,1000) is generated
2-The rollback log of the data before modification (zhangsan,0) is generated

According to the above process, the following conclusions can be drawn:
1-Each data change (insert/update/delete) operation is accompanied by the generation of an undo log, and the rollback log must be persisted to disk before the data
2-The so-called rollback It is to do the reverse operation according to the rollback log. For example, the reverse operation of delete is insert, the reverse operation of insert is delete, and the reverse operation of update is update.

Thinking: Why write the log first and then the database?

(3) Rollback according to the undo log
In order to achieve success or failure at the same time, when the system encounters an error or performs a rollback operation, it needs to be rolled back according to the undo log. The rollback
insert image description hereoperation is to restore to the original state. The undo log records that the data is For the information before modification and the newly added and deleted data information, a rollback statement is generated according to the undo log, for example:
1- If there is a new data record in the rollback log, a statement to delete the entry is generated
2- If it is in the undo log If there is a delete data record in the rollback log, then generate the statement that generates the entry
3- If there is a modification data record in the rollback log, then generate a statement that modifies the original data

(3) Implementation of persistence

Once a transaction is committed, its modifications will be permanently saved in the database, and the modified data will not be lost even if the system crashes.

Let’s first understand the data storage mechanism of MySQL. MySQL’s table data is stored on the disk, so when you want to access it, you have to go through disk IO. However, even using SSD disk IO is very performance-consuming.
For this reason, in order to improve performance, InnoDB provides a buffer pool (Buffer Pool). The Buffer Pool contains the mapping of disk data pages and can be used as a cache: (1) Read data: it will be read
from the buffer pool first. If the buffer pool If it is not in the pool, read from the disk and put it into the buffer pool;
(2) Write data: it will be written to the buffer pool first, and the data in the buffer pool will be periodically synchronized to the disk;

Although the buffer pool measures above have brought a qualitative leap in performance, they have also brought new problems. When the MySQL system goes down and the power is off, data may be lost! ! !

Because our data has been submitted, but it is in the buffer pool at this time, and has not had time to persist on the disk, so we urgently need a mechanism to store the data of the submitted transaction for use in restoring data.

So redo log comes in handy. Let's see when the redo log was generated

insert image description here
Since the redo log also needs to be stored, and it also involves disk IO, why use it?
(1) The storage of redo log is sequential storage, while cache synchronization is random operation.
(2) Cache synchronization is based on data pages, and the size of the data transferred each time is larger than the redo log.

(4) Realization of isolation

1:

Isolation is the most complex of transactional ACID properties. Four isolation levels are defined in the SQL standard, and each level specifies which modifications in a transaction are visible between transactions and which are not.

The lower the isolation level, the higher the concurrency can be implemented, but at the same time, the implementation complexity and overhead are also greater.

Mysql has the following four isolation levels (levels from low to high):
(1) READ UNCOMMITED (uncommitted read)
(2) READ COMMITED (committed read)
(3) REPEATABLE READ (repeatable read)
(4) SERIALIZABLE (repeatable read) repeat read)

As long as you thoroughly understand the isolation level and its implementation principle, it is equivalent to understanding the isolation type in ACID. As mentioned earlier, the purpose of atomicity, isolation, and persistence is to achieve consistency, but the isolation type is different from the other two. Atomicity and persistence are to achieve data reliability. For example It is necessary to achieve recovery after downtime and rollback after error.

So what is isolation to do? Isolation is about managing the access order of multiple concurrent read and write requests. This sequence includes serial or parallel instructions. Write requests not only refer to insert operations, but also include update operations.

insert image description hereIn short, it can be seen from the implementation of isolation that this is a trade-off between data reliability and performance.
(1) High reliability, low concurrency performance (such as Serializable)
(2) Low reliability, high concurrency performance (such as Read Uncommited)

2:READ UNCOMMITTED

Under the READ UNCOMMITTED isolation level, modifications made in a transaction are visible to other transactions even if they have not been committed. Transactions can read uncommitted data, causing dirty reads.

Because the read does not add any locks, the write operation modifies the data during the read process, which will cause dirty reads. The advantage is that it can improve concurrent processing performance and achieve parallel reading and writing.

In other words, read operations cannot exclude write requests.
insert image description hereAdvantages: read and write in parallel, high performance
Disadvantages: cause dirty reads

3:READ COMMITTED

All modifications made by a transaction before its submission are invisible to other transactions. Other transactions can read the committed changes. This logic is acceptable in many scenarios.

InnoDB uses exclusive locks in READ COMMITTED, and reads data without locking but using the MVCC mechanism. Or in other words, he uses a read-write separation mechanism.
But this level will produce non-rereadable and phantom read problems.

(1) What is non-rereadable?
The results of multiple reads within a transaction are not the same.

(2) Why does non-repeatable read occur?
This has something to do with the MVCC mechanism at the READ COMMITTED level. Under this isolation level, a new version number is generated each time a select is made, so each time a select is read, it is not a copy but a different copy.

Between each select, there are other transactions that update the data we read and commit, then there is a non-repeatable read (3) The result of multiple reads within a transaction of
insert image description here
REPEATABLE READ (Mysql default isolation level) is
the same. At this level, query problems such as dirty reads and non-repeatable reads can be avoided. Mysql has two mechanisms to achieve this isolation level, namely using read-write locks and MVCC.

Implementing read-write locks:
insert image description here
why is it repeatable? As long as the read lock is not released, the data read for the first time can still be read during the second read.
(1) Advantages: Simple to implement
(2) Disadvantages: Cannot achieve parallel reading and writing

Implemented with MVCC:
insert image description here
Why can it be read repeatedly? Because only one version is generated for multiple reads, the reads are naturally the same data.
1-Advantages: read and write in parallel
2-Disadvantages: high complexity of implementation

However, under this isolation level, there will still be the problem of phantom reading. I plan to introduce another article about the solution to phantom reading.

4:SERIALIZABLE

This isolation level is the easiest to understand and the simplest to implement. Under the isolation level, there are no other advantages except that it will not cause data inconsistency.

insert image description here

(5) Implementation of Consistency

A database is always transitioning from one consistent state to another.

Here is an example: zhangsan transfers 400 from bank card to wealth management account

start transaction;
select balance from bank where name="zhangsan";
// 生成 重做日志 balance=600
update bank set balance = balance - 400; 
// 生成 重做日志 amount=400
update finance set amount = amount + 400;
commit;

(1) If an exception occurs after executing update bank set balance = balance - 400;, the money in the bank card cannot be reduced for no reason, but rolled back to the original state.
(2) Or after the transaction is committed, the buffer pool is down before it is synchronized to the disk. This is also unacceptable, and it should be restored and persisted when it is restarted.
(3) If there are concurrent transaction requests, the visibility between transactions should also be done well to avoid dirty reads, non-repeatable reads, phantom reads, etc. When concurrency is involved, there is often a balance between performance and consistency, and a certain trade-off is made, so isolation is also a violation of consistency.

【5】Summary

What technologies and ideas are used to realize the transaction?
(1) Atomicity: Use undo log to achieve rollback
(2) Persistence: Use redo log to achieve recovery after failure
(3) Isolation: Use locks and MVCC, the optimization ideas used include read-write separation, read Read parallelism, read-write parallelism
(4) Consistency: achieve consistency through rollback, recovery, and isolation in a concurrent environment.

[4] Spring declarative transaction @Transactional

[1] Prepare test code

(1) Add dependencies

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.allen</groupId>
    <artifactId>transactional</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>transactional</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.21</version>
        </dependency>

        <!--<dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

(2) configuration file

server.port=8081

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/sharding-jdbc-order?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

logging.level.root=debug

(3) Add code Controller, Service

package com.allen.transactional.Con;

import com.allen.transactional.Service.UserService01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName: TestController
 * @Author: AllenSun
 * @Date: 2022/12/20 下午10:37
 */
@RestController
public class TestController {
    
    

    @Autowired
    UserService01 userService01;

    @GetMapping("/hello")
    public void hello () {
    
    
        userService01.transfer();
    }
}
package com.allen.transactional.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @ClassName: UserService01
 * @Author: AllenSun
 * @Date: 2022/12/20 下午10:38
 */
@Service
public class UserService01 {
    
    

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    UserService02 userService02;

    @Transactional
    public void transfer() {
    
    
        jdbcTemplate.update("update order_1 set count = ? where user_id=?;",1,101);
        int i=1/0;
    }
}
package com.allen.transactional.Service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

/**
 * @ClassName: UserService01
 * @Author: AllenSun
 * @Date: 2022/12/20 下午10:38
 */
@Service
public class UserService02 {
    
    

    @Autowired
    JdbcTemplate jdbcTemplate;

    public void update() {
    
    
        jdbcTemplate.update("update order_1 set count = ? where user_id=?;",100,103);
    }

}

(4) Test path

http://127.0.0.1:8081/hello

(5) View logs

Note that a line of int i=1/0 was added to the transfer method; causing an exception to be thrown and a transaction triggered, you can see the details of the log

【2】Isolation

【3】Communication

(1) What is the dissemination of transactions

The transaction propagation behavior is to solve the transaction problem of calling each other between business layer methods. When one transaction method is called by another transaction method, in what state should the transaction exist? For example, a new method may continue to run in an existing transaction, or it may start a new transaction and run in its own transaction, etc. These rules involve transaction propagation.

(2) Seven types of transmission

The default is Required.

The three most used are REQUIRED, REQUIRES_NEW, and NESTED. In 90% of cases, using REQUIRED can solve the problem.
insert image description here

(3) Required (internal method and external method share the same transaction)

The default attribute of the @Transactional annotation is that the REQUIRED
method and its internal methods share the same transaction
(1) If the transfer method has already started a transaction, the update method will not start a new transaction, but will be added to the transaction of the transfer method Come
(2) If the transfer method does not open a transaction, then update will start its own new transaction
(3) If the transfer method throws an exception, it will also cause the update method to roll back

insert image description here
The data before the test The data
insert image description here
after the test did not change

View the log content of the console

# 创建transfer方法的事务
o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
# 执行tranfer方法的sql
o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows

# 将update方法添加到transfer方法的事务中去
o.s.j.d.DataSourceTransactionManager     : Participating in existing transaction
o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows

# 抛出异常,开始回滚
o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@79ede17d]]]
o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection 

Because the update method is added to the transaction of the transfer method, the two methods share a transaction. If a rollback occurs, both methods must be rolled back

(4) Requires_NEW (external methods and internal methods each have their own independent affairs)

(1) If there is an external transaction, the internal transaction will also start its own transaction, the external transaction will be suspended, and the internal transaction will run independently. There may be two transactions at the same time
1-transfer method will roll back after throwing an exception, but the update method will not follow the rollback, because the update method starts its own transaction
2-if the transfer method is executed normally, but the update method throws Exception rollback depends on whether the exception of the update method will be handled. If the exception of the update is handled, the transfer method will not be rolled back, otherwise it will be rolled back if it is affected by the exception of the update. (2) If there is no external
transaction , the internal will still open its own affairs

Case 1: The transfer method is REQUIRED, the update method is Requires_NEW, both methods are normal

insert image description hereHere is a detail, if there is no database index for the user_id field, the transfer method will lock the table during execution, then the update method will fall into a deadlock when executing sql, and the access will time out. So you can add a unique index to the user_id field, so that the transfer method will only add row locks, and there will be no deadlock problem

View console logs

# 创建transfer方法的事务
2022-12-21 00:01:31.146 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2022-12-21 00:01:31.147 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5f489349]]] for JDBC transaction
2022-12-21 00:01:31.147 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5f489349]]] to manual commit
# 准备执行transfer方法的sql
2022-12-21 00:01:31.147 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:01:31.147 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:01:31.149 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows

# 挂起transfer方法的事务,创建update方法的事务
2022-12-21 00:01:31.149 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.allen.transactional.Service.UserService02.update]
2022-12-21 00:01:31.150 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@201ca654]]] for JDBC transaction
2022-12-21 00:01:31.151 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@201ca654]]] to manual commit
# 准备执行update方法的事务
2022-12-21 00:01:31.151 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:01:31.151 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:01:31.151 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows

# 提交update方法的事务
2022-12-21 00:01:31.152 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2022-12-21 00:01:31.152 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@201ca654]]]
2022-12-21 00:01:31.152 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@201ca654]]] after transaction
2022-12-21 00:01:31.152 DEBUG 65541 --- [nio-8081-exec-2] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

# 唤醒transfer方法的事务,然后提交
2022-12-21 00:01:31.153 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
2022-12-21 00:01:31.153 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2022-12-21 00:01:31.153 DEBUG 65541 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5f489349]]]

View the results of the method execution, the sql of the two methods are executed successfully
insert image description here

Case 2: The transfer method is REQUIRED and there is no exception; the update method is Requires_NEW and there is an exception (depending on whether the exception of the internal method update has been processed)

It is because the internal update method throws an exception and does not process it, resulting in an exception in the external method, causing both methods to roll back.
insert image description hereCheck the console log

# 创建transfer方法的事务
2022-12-21 00:11:17.147 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2022-12-21 00:11:17.153 DEBUG 67348 --- [nio-8081-exec-2] o.a.tomcat.jdbc.pool.PooledConnection    : Instantiating driver using class: com.mysql.jdbc.Driver [url=jdbc:mysql://127.0.0.1:3306/sharding-jdbc-order?characterEncoding=UTF-8]
2022-12-21 00:11:17.414 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@771338ee]]] for JDBC transaction
2022-12-21 00:11:17.416 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@771338ee]]] to manual commit
# 执行transfer方法的sql
2022-12-21 00:11:17.421 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:11:17.421 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:11:17.433 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 挂起transfer方法的事务,并且创建update方法的事务
2022-12-21 00:11:17.435 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.allen.transactional.Service.UserService02.update]
2022-12-21 00:11:17.435 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6e1ad23e]]] for JDBC transaction
2022-12-21 00:11:17.435 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6e1ad23e]]] to manual commit
# 执行update方法的sql
2022-12-21 00:11:17.437 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:11:17.437 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:11:17.438 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 发现异常,准备回滚update方法
2022-12-21 00:11:17.439 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2022-12-21 00:11:17.439 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6e1ad23e]]]
2022-12-21 00:11:17.440 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6e1ad23e]]] after transaction
2022-12-21 00:11:17.440 DEBUG 67348 --- [nio-8081-exec-2] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

# 唤醒transfer方法的事务
2022-12-21 00:11:17.440 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
# transfer方法也开始回滚
2022-12-21 00:11:17.440 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2022-12-21 00:11:17.440 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@771338ee]]]
2022-12-21 00:11:17.442 DEBUG 67348 --- [nio-8081-exec-2] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@771338ee]]] after transaction

Both methods have been rolled back. Next, handle the exception of the internal update method and then check whether the update method has any effect on the transfer method.
insert image description hereThen look at the console log

# 创建transfer方法的事务
2022-12-21 00:22:55.704 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2022-12-21 00:22:55.710 DEBUG 68808 --- [nio-8081-exec-1] o.a.tomcat.jdbc.pool.PooledConnection    : Instantiating driver using class: com.mysql.jdbc.Driver [url=jdbc:mysql://127.0.0.1:3306/sharding-jdbc-order?characterEncoding=UTF-8]
2022-12-21 00:22:55.955 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@559824e7]]] for JDBC transaction
2022-12-21 00:22:55.956 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@559824e7]]] to manual commit
# 执行transfer方法的sql
2022-12-21 00:22:55.959 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:22:55.959 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:22:55.973 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 挂起transfer方法的事务,并且创建update方法的事务
2022-12-21 00:22:55.974 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.allen.transactional.Service.UserService02.update]
2022-12-21 00:22:55.975 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6b1e08a1]]] for JDBC transaction
2022-12-21 00:22:55.975 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6b1e08a1]]] to manual commit
# 执行update方法的sql
2022-12-21 00:22:55.977 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:22:55.977 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:22:55.979 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# update方法出现异常,开始回滚update的sql
2022-12-21 00:22:55.979 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2022-12-21 00:22:55.979 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6b1e08a1]]]
2022-12-21 00:22:55.981 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6b1e08a1]]] after transaction
2022-12-21 00:22:55.982 DEBUG 68808 --- [nio-8081-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
# 唤醒transfer方法的事务
2022-12-21 00:22:55.982 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
2022-12-21 00:22:55.982 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
# 提交transfer方法的事务
2022-12-21 00:22:55.982 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@559824e7]]]
2022-12-21 00:22:55.983 DEBUG 68808 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@559824e7]]] after transaction

Check the results, you can see that the transfer method has been modified successfully, but the update method has not been modified successfully
insert image description here

Case 3: The transfer method is REQUIRED, and there is an exception; the update method is Requires_NEW, and there is no exception

insert image description hereView console log information

# 创建transfer方法的事务
2022-12-21 00:31:25.591 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2022-12-21 00:31:25.833 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@17ce73b0]]] for JDBC transaction
2022-12-21 00:31:25.835 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@17ce73b0]]] to manual commit
# 执行transfer方法的sql
2022-12-21 00:31:25.839 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:31:25.839 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:31:25.852 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 挂起transfer方法的事务,创建update方法的事务
2022-12-21 00:31:25.854 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [com.allen.transactional.Service.UserService02.update]
2022-12-21 00:31:25.854 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@7f5ec022]]] for JDBC transaction
2022-12-21 00:31:25.854 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@7f5ec022]]] to manual commit
# 执行update方法的sql
2022-12-21 00:31:25.856 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:31:25.856 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:31:25.856 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 提交update方法的事务
2022-12-21 00:31:25.857 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2022-12-21 00:31:25.857 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@7f5ec022]]]
2022-12-21 00:31:25.858 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@7f5ec022]]] after transaction
2022-12-21 00:31:25.858 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource
# 唤醒transfer方法的事务
2022-12-21 00:31:25.859 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
# transfer方法出现异常,开始回滚transfer方法的sql
2022-12-21 00:31:25.859 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2022-12-21 00:31:25.859 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@17ce73b0]]]
2022-12-21 00:31:25.861 DEBUG 69835 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@17ce73b0]]] after transaction
2022-12-21 00:31:25.861 DEBUG 69835 --- [nio-8081-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

Check the database results, the transfer method is rolled back, and the modification is not successful, but the update method transaction is successfully submitted, and the modification is successful
insert image description here

(5) NESTED (the external method is the main transaction, the internal method is the sub-transaction, and the transaction is nested)

If the main transaction is rolled back, the sub-transaction will also be rolled back

Case 1: The transfer method has an exception and rolls back, the update method has no exception but rolls back

insert image description here

# 创建transfer方法的事务
2022-12-21 00:43:43.676 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2022-12-21 00:43:43.915 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5e363a1d]]] to manual commit
# 执行transfer方法的sql
2022-12-21 00:43:43.920 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:43:43.921 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:43:43.937 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 创建嵌套子事务update方法的事务
2022-12-21 00:43:43.938 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating nested transaction with name [com.allen.transactional.Service.UserService02.update]
# 执行update方法的sql
2022-12-21 00:43:43.943 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:43:43.943 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:43:43.944 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
2022-12-21 00:43:43.945 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing transaction savepoint
# transfer方法出现异常,开始回滚,主事务和子事务都会回滚,两个事务都没有提交
2022-12-21 00:43:43.945 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
2022-12-21 00:43:43.945 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5e363a1d]]]
2022-12-21 00:43:43.946 DEBUG 71230 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@5e363a1d]]] after transaction
2022-12-21 00:43:43.946 DEBUG 71230 --- [nio-8081-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Returning JDBC Connection to DataSource

Both methods do not modify the database, and the data in the database remains unchanged

Case 2: The transfer method has no exception, the update method has an exception, and the rollback of the sub-transaction does not affect the main transaction

Add exception handling to the update method, otherwise the main transaction will still roll back
insert image description here

# 创建transfer方法的事务
2022-12-21 00:53:26.042 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.allen.transactional.Service.UserService01.transfer]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
# 执行transfer方法的sql
2022-12-21 00:53:26.317 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:53:26.318 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:53:26.332 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# 创建内部方法update的子事务
2022-12-21 00:53:26.334 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Creating nested transaction with name [com.allen.transactional.Service.UserService02.update]
# 执行update方法的sql
2022-12-21 00:53:26.339 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL update
2022-12-21 00:53:26.339 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : Executing prepared SQL statement [update order_1 set count = ? where user_id=?;]
2022-12-21 00:53:26.340 DEBUG 72498 --- [nio-8081-exec-1] o.s.jdbc.core.JdbcTemplate               : SQL update affected 1 rows
# update方法出现异常,开始回滚
2022-12-21 00:53:26.341 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Rolling back transaction to savepoint
# 提交transfer方法的事务
2022-12-21 00:53:26.342 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2022-12-21 00:53:26.342 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@57a99f1]]]
2022-12-21 00:53:26.344 DEBUG 72498 --- [nio-8081-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection 

insert image description here

(6) MANDATORY (mandatory)

The MANDATORY method transaction cannot exist independently and must be added to a transaction
(1) If the transfer method has a transaction, then the update method will be added to the transaction of the transfer method
(2) If the transfer method has no transaction, then the transaction of the update method will report an error
insert image description here

(7)SUPPORTS

(1) If the transfer method has a transaction, then the update method will be added to the transaction of the transfer method.
(2) If the transfer method has no transaction, it is useless to add the transaction of the update method, and continue to run directly in a non-transactional manner

(8) NOT_SUPPORTED (suspend if there is a transaction)

After adding this attribute, if the current method adds a transaction, suspend the transaction and run it in a non-transactional manner

(9) NEVER (no adding transactions)

After adding this attribute, if the current method adds a transaction, an exception will be thrown directly, and it must be run in a non-transactional manner

[4] Rollback rules

Under what circumstances will it be rolled back?
Rollback will only occur when a runtime exception RuntimeException is encountered. If a checked exception is encountered, the rollback will not be triggered

(1) Add an IOException, then the rollback will not be triggered, because it is not a runtime exception
insert image description here
(2) Add the rollbackfor parameter, modify the rollback rules, and specify which exception will be rolled back.
At this time, the rollback can be triggered
insert image description here
(3 ) can also use the noRollbackFor parameter to specify which exception does not roll back

【5】Whether read-only

If a business method has only one query sql, there is no need to add transactions, which will slow down the execution efficiency.

However, if there are multiple query sqls in a business method, by default, each query sql will start an independent transaction. At this time, if there are concurrent tasks that modify the data, multiple query sqls in one method may query With different results, multiple transactions cannot be resolved using the isolation level.

After enabling read-only, you can ensure that the query results of multiple query transactions are the same.
insert image description hereIf there is only one query sql, there is no need to add it. If there are multiple query sql, you can judge whether to add it according to the situation.

【6】Timeout

Set a timeout period. If the transaction has not been executed within this time, the transaction will be automatically rolled back to avoid large transactions.

insert image description here

[7] Scenarios and reasons for transaction failure

(1) Transaction annotations are only valid if they are added to public methods

If it is added to the private method, it will directly explode the red line; if it is added to the protected method, although it will not become popular, the transaction will not take effect
insert image description here

(2) Self-invocation will cause transaction failure

The m1 method without a transaction internally calls the transfer method with a transaction, but when other methods call the m1 method, the transaction of the transfer method will fail. The reason is that the
insert image description here
principle of transaction annotation is AOP proxy, and the proxy mode will only intercept the methods passed in through the proxy.

Annotations are written on the implementation class of the method, not on the interface

(3)

[5] The underlying principle of spring declarative transaction @Transactional implementation

[1] Native transaction management

Transactions were born when there was no Spring. In fact, the framework relies on the capabilities provided by the bottom layer, but it abstracts and reuses this process. The essence of Spring transactions is actually the database's support for transactions. Without database transaction support, spring cannot provide transaction functions.
Here we use the underlying API to understand the process of transaction management (JDBC as an example):

// 获取mysql数据库连接
Connection conn = DriverManager.getConnection("xxxx");
   conn.setAutoCommit(false);
statement = conn.createStatement();
// 执行sql,返回结果集
resultSet = statement.executeQuery("xxxx");
conn.commit(); //提交
//conn.rollback();//回滚

The above is an example of a native operation transaction. These processes are also inescapable from Spring transactions, but you cannot perceive this process to be automated or transparent for programming efficiency.
And what we do afterwards is to gradually restore this automated process.

[2] Transaction API provided by Spring

Spring provides many APIs about transactions. But the most basic ones are PlatformTransactionManager, TransactionDefintion and TransactionStatus.

(1) Transaction Manager - PlatformTransactionManager

PlatformTransactionManager is the top-level interface of the transaction manager. Transaction management is limited to specific data sources (for example, the transaction manager corresponding to JDBC is DatasourceTransactionManager), so PlatformTransactionManager only specifies the basic operations of transactions: creating transactions, committing things and rolling back transactions.

public interface PlatformTransactionManager extends TransactionManager {
    
    
 
    /**
     * 打开事务
     */
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
 
	/**
	 * 提交事务
	 */
	void commit(TransactionStatus status) throws TransactionException;
 
	/**
	 * 回滚事务
	 */
	void rollback(TransactionStatus status) throws TransactionException;
}

At the same time, in order to simplify the implementation of the transaction manager, Spring provides an abstract class AbstractPlatformTransactionManager, which specifies the basic framework of the transaction manager, leaving only the characteristics that depend on the specific platform as abstract methods for subclass implementation.

(2) Transaction status - TransactionStatus

Transaction status is my literal translation of the TransactionStatus class. In fact, I think this class can be viewed directly as a superset of transactions (including transaction objects and storing the status of transactions). It is this object that is created when PlatformTransactionManager.getTransaction().
The methods of this object are all related to the transaction state:

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    
    
 
	/**
	 * 是否有Savepoint Savepoint是当事务回滚时需要恢复的状态
	 */
	boolean hasSavepoint();
 
	/**
	 * flush()操作和底层数据源有关,并非强制所有数据源都要支持
	 */
	@Override
	void flush();
 
}

In addition, TransactionStatus also inherits other methods from the parent interface, which are summarized below:

	/**
	 * 是否是新事务(或是其他事务的一部分)
	 */
	boolean isNewTransaction();
 
	/**
	 * 设置rollback-only 表示之后需要回滚
	 */
	void setRollbackOnly();
 
	/**
	 * 是否rollback-only
	 */
	boolean isRollbackOnly();
 
	/**
	 * 判断该事务已经完成
	 */
	boolean isCompleted();
	
	
	/**
	 * 创建一个Savepoint
	 */
	Object createSavepoint() throws TransactionException;
 
	/**
	 * 回滚到指定Savepoint
	 */
	void rollbackToSavepoint(Object savepoint) throws TransactionException;
 
	/**
	 * 释放Savepoint 当事务完成后,事务管理器基本上自动释放该事务所有的savepoint
	 */
	void releaseSavepoint(Object savepoint) throws TransactionException;
 

(3) Definition of transaction attributes - TransactionDefinition

TransactionDefinition represents the definition of a transaction, and the transaction will be opened according to its specified characteristics.
Constants for transaction propagation levels and isolation levels are also defined in this interface.

	/**
	 * 返回事务的传播级别
	 */
	default int getPropagationBehavior() {
    
    
		return PROPAGATION_REQUIRED;
	}
 
	/**
	 * 返回事务的隔离级别
	 */
	default int getIsolationLevel() {
    
    
		return ISOLATION_DEFAULT;
	}
 
	/**
	 * 事务超时时间
	 */
	default int getTimeout() {
    
    
		return TIMEOUT_DEFAULT;
	}
 
	/**
	 * 是否为只读事务(只读事务在处理上能有一些优化)
	 */
	default boolean isReadOnly() {
    
    
		return false;
	}
 
	/**
	 * 返回事务的名称
	 */
	@Nullable
	default String getName() {
    
    
		return null;
	}
 
 
	/**
	 * 默认的事务配置
	 */
	static TransactionDefinition withDefaults() {
    
    
		return StaticTransactionDefinition.INSTANCE;
	}

(4) Programmatic use of Spring transactions

With the above APIs, Spring's transaction control can already be implemented programmatically.
However, Spring officially recommends not to directly use the low-level API of PlatformTransactionManager for programming, but to use TransactionTemplate and TransactionCallback, two interfaces that are biased towards the user layer.
The sample code is as follows:

        //设置事务的各种属性;可以猜测TransactionTemplate应该是实现了TransactionDefinition
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setTimeout(30000);
        
        //执行事务 将业务逻辑封装在TransactionCallback中
        transactionTemplate.execute(new TransactionCallback<Object>() {
    
    
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
    
    
                    //....   业务代码
            }
        });

The above is the most basic principle of Spring affairs. But why do none of these processes seem to be visible to us? That's because these processes are woven into our business logic through AOP.
Therefore, if you want to understand the principles of Spring transactions in depth, you also need to understand the principles of AOP.

【3】AOP principle

There are two implementation mechanisms of AOP: Proxy-based and Weaving-based.
The former relies on the way of dynamic proxy to achieve the purpose of enhancing the proxy class. The latter should achieve the purpose of enhancement through bytecode enhancement.
In Spring, the former is generally used by default. Afterwards, only the former is analyzed.

There are also two ways for Spring to declare AOP, one is by declaring Aspect, and the other is by declaring Advisor.
Either way, you need to express clearly the logic (what) you want to enhance and the place you want to enhance (where). That is, you need to tell Spring what logic you want to enhance, and which beans/methods to enhance.

The what and where here are replaced by the concepts in AOP, which correspond to Advice and Pointcut respectively.

Because the transaction declares AOP through Advisor, this article only analyzes the implementation of Advisor.

(1) Dynamic proxy

Since it is a dynamic proxy, there must be a proxy class (Target), a proxy class (Proxy), and the process of the class being proxied (because the user does not know that the class is being proxied).

Proxied class

The proxy class is the easiest to know, that is, the class that is matched by Advisor's Pointcut (classFliter match or methodMatches).

proxy class

The proxy class is created directly at runtime. There are usually two ways:
(1) JDK's dynamic proxy
(2) CGLIB's dynamic proxy

The difference between the two is that JDK dynamic proxy implements the interface (the proxy object is the interface), so it can only proxy the methods in the interface.

The CGLIB dynamic proxy is through inheritance, so it can proxy the methods in the object, but because of the inheritance relationship, it cannot proxy final classes and methods (cannot be inherited), or private methods (not visible to subclasses) .

The process of creating a proxy and replacing the target class

Creating a proxy and replacing the target class is mainly to apply an extension point left by the Spring container when obtaining the Bean.
When Spring is getting Bean, if the Bean does not exist, it will create the Bean in three steps:
(1) instantiate
(2) fill in the properties
(3) initialize and instantiate
usually create an instance of the Bean object through reflection, and the obtained Bean at this time Still just a blank object.
The filling property is mainly to inject other beans into this bean to realize automatic assembly.
Initialization is to allow users to control the Bean creation process.
Creating a proxy for the Bean and replacing the original Bean occurs in the initialization step, more specifically in BeanPostProcessor.postProcessorAfterInitialization().

Among the many BeanPostProcessors, there is a class of post-processors dedicated to creating proxies. For example, we're going to introduce AbstractAdvisorAutoProxyCreator.
Take a look at the process of creating a proxy in AbstractAutoProxyCreator:
(1) First confirm whether a proxy object ( earlyProxyReferences , to avoid proxying the proxy object) has been created
(2) If not, consider whether proxying is required (through wrapIfNecessary )
(3) If If it is a special bean or a bean that has been judged not to create a proxy before, no proxy will be created
(4) Otherwise, check whether there is a matching Advise (the matching method is the direct matching class through PointCut or IntroductionAdvisor introduced above)
(5) If found Advisor, indicating the need to create a proxy, enter createProxy
(6) first create a ProxyFactory, this factory is used to create AopProxy, and AopProxy is used to create proxy objects. Because there are two underlying proxy methods (JDK dynamic proxy and CGLIB, corresponding to the implementation of AopProxy are JdkDynamicAopProxy and ObjenesisCglibAopProxy), so a simple factory design is used here. ProxyFactory will set the properties of this proxy, and then select the appropriate proxy method based on these properties to create a proxy object.
(7) The created object will replace the proxy object (Target) and be saved in BeanFactory.singletonObjects, so when other beans want to inject Target, they have already been injected with Proxy.
The above is the process of Spring implementing dynamic proxy.

[4] spring annotation transaction

In the above, we learned about the basic usage of Spring transaction API from programmatic transactions, and the principle of Spring Advisor. Now, we are going back to the Spring annotation transaction to verify whether the annotation transaction hides the specific transaction control logic through the above methods.

(1) Starting from @EnableTransactionManagement

@EnableTransactionManagement is a transaction that enables annotated transactions. If the annotated transaction really has a mystery, then @EnableTransactionManagement is the breakthrough for us to reveal the secret.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
    
    
 
	/**
	 * 用来表示默认使用JDK Dynamic Proxy还是CGLIB Proxy
	 */
	boolean proxyTargetClass() default false;
 
	/**
	 * 表示以Proxy-based方式实现AOP还是以Weaving-based方式实现AOP
	 */
	AdviceMode mode() default AdviceMode.PROXY;
 
	/**
	 * 顺序
	 */
	int order() default Ordered.LOWEST_PRECEDENCE;
 
}

The @EnableTransactionManagement annotation does not seem to be special, it is the configuration of some properties. But it introduces another configuration TransactionManagentConfigurationSelector through @Import.

(2)TransactionManangementConfigurationSelector

In Spring, Selector is usually used to select some beans and register BeanDefinition with the container (strictly speaking, Selector is only a selection process, and the specific process of registration is triggered by calling ConfigurationClassParser when ConfigurationClasspathPostProcessor parses).
The main logic is to register different BeanDefinitions according to the proxy mode.
For the Proxy mode, there are two injections:
1-AutoProxyRegistrar
2-ProxyTransactionManagementConfiguration

1:AutoProxyRegistrar

Registrar is also used to register beans to the container. In Proxy mode, it will call AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); to register InfrastructureAdvisorAutoProxyCreator with the container. And this class is a subclass of the AbstractAdvisorAutoProxyCreator we mentioned above.
Thus, we have completed our first condition - AOP proxy.

2:ProxyTransactionManagementConfiguration

ProxyTransactionManagementConfiguration is a configuration class. If you count its inherited parent class, a total of four classes are declared:
1-TransactionalEventListenerFactory
2-BeanFactoryTransactionAttributeSourceAdvisor
3-TransactionAttributeSource
4-TransactionInterceptor

The latter three categories are relatively important, and we will analyze them one by one.

(1) BeanFactoryTransactionAttributeSourceAdvisor
knows from the name that it is an Advisor, so it should have Pointcut and Advise.
Among them, Pointcut is TransactionAttributeSourcePointcut, mainly some methods such as filter and matches, which are used to match the proxied class.
And Adivise is the TransactionInterceptor we will introduce later.

(2) TransactionAttributeSource
TransactionAttributeSource is just an interface that extends TransactionDefinition and adds the method of isCandidateClass() (which can be used to help Pointcut match).
The concrete implementation used here is AnnotationTransactionAttributeSource. Because the annotated transaction candidate class (that is, the class to be proxied) is identified by the @Transactional annotation, and all transaction attributes also come from the @Transactional annotation.

(3) TransactionInterceptor
We just said that TransactionInterceptor is the Advise we are looking for.
This class is a little more complicated. First, the logic related to transaction processing is placed in its parent class TransactionAspectSupport. In addition, in order to adapt to the reflection call of dynamic proxy (two proxy methods), the MethodInterceptor interface is implemented.
That is to say, the entry point for reflection is MethodInterceptor.invoke(), and the reflection logic is in TransactionAspectSupport.invokeWithinTransaction().
We can simply look at part of the code in the invokeWithTransaction() method:

	@Nullable
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
    
    
 
		
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
		final TransactionManager tm = determineTransactionManager(txAttr);
 
		//省略部分代码
        
        //获取事物管理器
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
 
		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
    
    
			// 打开事务(内部就是getTransactionStatus的过程)
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
 
			Object retVal;
			try {
    
    
				// 执行业务逻辑 invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
    
    
				// 异常回滚
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
    
    
				cleanupTransactionInfo(txInfo);
			}
 
			//省略部分代码
            
            //提交事物
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

[5] Common situations of transaction failure and the reasons behind them

[6] Spring programmatic transaction TransactionTemplate case and implementation principle

【1】TransactionTemplate use case

public Object getObject(String str) {
    
    
        /*
         *  执行带有返回值<Object>的事务管理
         */
        transactionTemplate.execute(new TransactionCallback<Object>() {
    
    
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
    
    

                try {
    
    
                      ...
                    //.......   业务代码
                    return new Object();
                } catch (Exception e) {
    
    
                    //回滚
                    transactionStatus.setRollbackOnly();
                    return null;
                }
            }
        });
}

You can use the lamada expression shorthand

    @Autowired
    TransactionTemplate transactionTemplate;

    public Boolean update() {
    
    

        return transactionTemplate.execute(status->{
    
    
            try {
    
    
                int result1 = jdbcTemplate.update("update order_1 set count = ? where user_id=?;",1000,103);
                int result2 = jdbcTemplate.update("update order_1 set count = ? where user_id=?;",1000,101);
                // int i=1/0;
            } catch (Exception e) {
    
    
                status.setRollbackOnly();
            }
            return true;
        });
    }

【2】Principle of realization

  • The most primitive transaction management method provided by Spring is based on TransactionDefinition, PlatformTransactionManager, TransactionStatus programmatic transactions.
  • The programmatic transaction management of TransactionTemplate is the encapsulation of the original transaction management method using the template method design pattern.

(1) TransactionTemplate.java source code

public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean {
    
    

    protected final Log logger = LogFactory.getLog(this.getClass());
    private PlatformTransactionManager transactionManager;

    public TransactionTemplate() {
    
    
    }

    public TransactionTemplate(PlatformTransactionManager transactionManager) {
    
    
        this.transactionManager = transactionManager;
    }

    public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
    
    
        super(transactionDefinition);
        this.transactionManager = transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
    
    
        this.transactionManager = transactionManager;
    }

    public PlatformTransactionManager getTransactionManager() {
    
    
        return this.transactionManager;
    }

    public void afterPropertiesSet() {
    
    
        if (this.transactionManager == null) {
    
    
            throw new IllegalArgumentException("Property 'transactionManager' is required");
        }
    }

    /*
     *  控制事务管理主要依赖于这个方法
     */
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
    
    
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
    
    
            return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
        } else {
    
    
            TransactionStatus status = this.transactionManager.getTransaction(this);

            Object result;
            try {
    
    
                result = action.doInTransaction(status);
            } catch (RuntimeException var5) {
    
    
                this.rollbackOnException(status, var5);
                throw var5;
            } catch (Error var6) {
    
    
                this.rollbackOnException(status, var6);
                throw var6;
            } catch (Exception var7) {
    
    
                this.rollbackOnException(status, var7);
                throw new UndeclaredThrowableException(var7, "TransactionCallback threw undeclared checked exception");
            }

            this.transactionManager.commit(status);
            return (T) result;
        }
    }

    private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
    
    
        this.logger.debug("Initiating transaction rollback on application exception", ex);

        try {
    
    
            this.transactionManager.rollback(status);
        } catch (TransactionSystemException var4) {
    
    
            this.logger.error("Application exception overridden by rollback exception", ex);
            var4.initApplicationException(ex);
            throw var4;
        } catch (RuntimeException var5) {
    
    
            this.logger.error("Application exception overridden by rollback exception", ex);
            throw var5;
        } catch (Error var6) {
    
    
            this.logger.error("Application exception overridden by rollback error", ex);
            throw var6;
        }
    }


}

(2) The parameter TransactionCallback of the execute method

Look at the interface TransactionCallback.java and find that it has only one method doInTransaction:

public interface TransactionCallback<T> {
    
    
    T doInTransaction(TransactionStatus var1);
}

And there is an abstract class TransactionCallbackWithoutResult that implements the interface TransactionCallback.

public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {
    
    
    public TransactionCallbackWithoutResult() {
    
    
    }

    public final Object doInTransaction(TransactionStatus status) {
    
    
        this.doInTransactionWithoutResult(status);
        return null;
    }

    protected abstract void doInTransactionWithoutResult(TransactionStatus var1);
}

So when we use TransactionTemplate.execute( ... ) to perform transaction management, there are two options for the incoming parameters:
1. TransactionCallback
2.
The difference between TransactionCallbackWithoutResult is quite obvious from the naming, one has a return value, and the other is No return value. This choice depends on whether you are reading or writing.

(3) Introduction of important components

(1)TransactionCallback
(2)TransactionStatus

(3) Process analysis

(1) Use TransactionTemplate to call the execute method, and the parameter is TransactionCallback. TransactionTemplate will catch the unchecked exception thrown in TransactionCallback or TransactionCallbackWithoutResult transaction operation and roll back the transaction, and then throw the unchecked exception to the upper layer for processing. If there are no problems during transaction processing, the TransactionTemplate will eventually commit the transaction for us, and the only thing we need to intervene is the transaction rollback in some cases.
(2) Then rewrite the doInTransaction method in the TransactionCallback interface. The doInTransaction method is our business code
(3) The parameter of the doInTransaction method is TransactionStatus, and the TransactionStatus is this.transactionManager.getTransaction(this);
(4) If you need to return If rolling, the TransactionStatus object calls the setRollbackOnly method. Mark the transaction as rollBackonly using the TransactionStatus exposed by the Callback interface. When the TransactionTemplate finally commits the transaction, if it detects that the rollBackOnly flag status is set, it will change the committed transaction to a rollback transaction.

[7] The relationship between mysql transaction and spring transaction

[8] Distributed transactions

Guess you like

Origin blog.csdn.net/weixin_44823875/article/details/128391082