详解事务:Mysql事务、Spring事务、分布式事务

详解事务:Mysql事务、Spring事务、分布式事务

  • (一)Mysql事务
    • 【1】4种隔离级别
    • 【2】测试案例
  • (二)Spring事务
    • 【1】准备测试代码
      • (1)添加依赖
      • (2)配置文件
      • (3)添加代码Controller、Service
      • (4)测试路径
      • (5)查看日志
    • 【2】隔离性
    • 【3】传播性
      • (1)什么是事务的传播性
      • (2)传播性的7种种类
      • (3)Required(内部方法和外部方法共用同一个事务)
      • (4)Requires_NEW(外部方法和内部方法各自有自己独立的事务)
          • 案例一:transfer方法是REQUIRED,update方法是Requires_NEW,两个方法都没有异常
          • 案例二:transfer方法是REQUIRED,没有异常;update方法是Requires_NEW,有异常(要看内部方法update的异常有没有被处理)
          • 案例三:transfer方法是REQUIRED,有异常;update方法是Requires_NEW,没有异常
      • (5)NESTED(外部方法为主事务,内部方法为子事务,事务嵌套)
          • 案例一:transfer方法有异常并回滚,update方法没有异常但是也会回滚
          • 案例二:transfer方法没有异常,update方法有异常,子事务回滚不影响主事务
      • (6)MANDATORY(强制性)
      • (7)SUPPORTS
      • (8)NOT_SUPPORTED(有事务就挂起)
      • (9)NEVER(禁止添加事务)
    • 【4】回滚规则
    • 【5】是否只读
    • 【6】超时时间
    • 【7】事务失效的场景和原因
        • (1)事务注解只有加在public方法上才会有效
        • (2)自调用会造成事务失效
        • (3)
    • 【8】声明式事务实现的底层原理
  • (三)分布式事务

(一)Mysql事务

【1】4种隔离级别

(1)序列化
同一时间只能有一个事务执行,所有事务串行执行
(2)可重复读
InnoDB引擎默认的隔离级别,当前事务的变化不会被外部看到
(3)提交读
当前事务可以看到其他事务对数据的修改
(4)未提交读

【2】测试案例

(1)查看全局的隔离级别和查看当前会话的隔离级别(mysql5.7版本的命令)
详解事务:Mysql事务、Spring事务、分布式事务_第1张图片(2)修改当前会话的隔离级别
详解事务:Mysql事务、Spring事务、分布式事务_第2张图片(3)测试序列化
先执行会话1的这前两行代码,但是不执行提交事务
详解事务:Mysql事务、Spring事务、分布式事务_第3张图片然后执行会话2的这前两行代码,也不执行提交事务
详解事务:Mysql事务、Spring事务、分布式事务_第4张图片会发现事务2的事务开启不了,必须要等前一个事务提交完
详解事务:Mysql事务、Spring事务、分布式事务_第5张图片执行完会话1的commit后,会话2就能开启事务

安全性最高,但是效率不好

(4)测试可重复读
修改当前会话的隔离级别
详解事务:Mysql事务、Spring事务、分布式事务_第6张图片然后会话1开启事务,并且查询值,但是不提交事务。此时查询的money结果是99
详解事务:Mysql事务、Spring事务、分布式事务_第7张图片然后会话2开启事务,并且修改值(此时两个事务可以同时进行了),将money值修改为100。但是不提交事务
详解事务:Mysql事务、Spring事务、分布式事务_第8张图片
再回到会话1,执行查询语句,money结果还是99
详解事务:Mysql事务、Spring事务、分布式事务_第9张图片会话1的事务提交完后,重新执行查询,money结果才是100(此时会话2的事务还没有提交)

也就是说,在当前事务中查询的结果不变,即使这个数据已经被其他事务修改过了,当前事务对其他事务的修改是不可见的

(5)可重复读造成的幻读
会话2事务提交成功了,但是因为事务未提交前不可见,所以会话1读到的值跟最终的实际值还是可能不一样。

(6)测试提交读(不可重复读)
会话1的事务提交完后,必须等到会话2的事务也提交完,才能查到会话2修改的值。即使会话2把money改成100了,只要会话2还没有提交事务,会话1读到的就一直是99。

这种隔离级别也可能会出现幻读。

可重复读造成幻读的原因就是会话2事务提交成功,导致会话1读到的值跟最终的实际值不一样。而不可重复读造成的幻读的原因就是会话2修改值后还没有提交事务,会话1就可以读到修改后的值100了,但是如果会话2回滚了呢,最终的真实结果应该还是99。但是会话1已经获取100并使用了

例如两个会话开启事务后,都先执行select语句,都发现没有这个数据,然后都执行一个insert方法插入数据,执行insert就会加锁,那第二个事务进来就会被锁住。

(7)未提交读

(二)Spring事务

【1】准备测试代码

(1)添加依赖


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>1.5.9.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.allengroupId>
    <artifactId>transactionalartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>transactionalname>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.21version>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

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

project>

(2)配置文件

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)添加代码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)测试路径

http://127.0.0.1:8081/hello

(5)查看日志

注意的是在transfer方法中添加了一行int i=1/0;导致抛出异常,触发事务,可以看看日志的详细内容

【2】隔离性

【3】传播性

(1)什么是事务的传播性

事务传播行为是为了解决业务层方法之间互相调用的事务问题,当一个事务方法被另一个事务方法调用时,事务该以何种状态存在?例如新方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行,等等,这些规则就涉及到事务的传播性。

(2)传播性的7种种类

默认是Required。

用的比较多的就是REQUIRED、REQUIRES_NEW、NESTED这三种,90%的情况下使用REQUIRED就可以解决了
详解事务:Mysql事务、Spring事务、分布式事务_第10张图片

(3)Required(内部方法和外部方法共用同一个事务)

@Transactional注解的默认属性就是REQUIRED
方法和它的内部方法共用同一个事务
(1)如果transfer方法已经开启事务了,那么update方法就不会再开启新的事务,而是加到transfer方法的事务中来
(2)如果transfer方法没有开启事务,那么update才会开启自己的新的事务
(3)如果transfer方法抛出异常也会造成update方法的回滚

详解事务:Mysql事务、Spring事务、分布式事务_第11张图片
测试前的数据
在这里插入图片描述
测试后的数据没有变化

查看控制台的日志内容

# 创建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 

因为是把update方法加入到transfer方法的事务中去的,所以两个方法共用一个事务,如果发生回滚的话,两个方法是都要回滚的

(4)Requires_NEW(外部方法和内部方法各自有自己独立的事务)

(1)如果外部有事务,内部也会开启自己的事务,会将外部的事务挂起,内部的事务独自运行。可能会同时存在两个事务
1-transfer方法抛出异常后会回滚,但是update方法不会跟着回滚,因为update方法开启的是自己的事务
2-如果transfer方法正常执行,而update方法抛出异常回滚,那就要看update方法的异常是否会处理掉,如果update的异常被处理了,transfer方法就不会回滚,否则受到update的异常影响也会回滚
(2)如果外部没有事务,内部还是会开启自己的事务

案例一:transfer方法是REQUIRED,update方法是Requires_NEW,两个方法都没有异常

详解事务:Mysql事务、Spring事务、分布式事务_第12张图片这里有个细节,如果对user_id字段没有加数据库索引,在执行的时候,transfer方法会对表加表锁,那么update方法执行sql的时候就会陷入死锁的情况,访问超时。所以可以给user_id字段加上唯一索引,这样transfer方法就只会加行锁,就不会出现死锁的问题了

查看控制台日志

# 创建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]]]

查看方法执行的结果,两个方法的sql都执行成功了
在这里插入图片描述

案例二:transfer方法是REQUIRED,没有异常;update方法是Requires_NEW,有异常(要看内部方法update的异常有没有被处理)

是因为内部的update方法抛出异常后没有进行处理,导致外部的方法也会出现异常,导致两个方法都会回滚
详解事务:Mysql事务、Spring事务、分布式事务_第13张图片查看控制台日志

# 创建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

两个方法都发生了回滚,接下来对内部update方法的异常进行处理然后再看update的方法对transfer方法有没有影响
详解事务:Mysql事务、Spring事务、分布式事务_第14张图片再来看看控制台日志

# 创建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

查看结果,可以看到transfer方法修改成功了,update方法没有修改成功
在这里插入图片描述

案例三:transfer方法是REQUIRED,有异常;update方法是Requires_NEW,没有异常

详解事务:Mysql事务、Spring事务、分布式事务_第15张图片查看控制台日志信息

# 创建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

查看数据库结果,transfer方法回滚了,没有修改成功,而update方法事务成功提交了,修改成功
在这里插入图片描述

(5)NESTED(外部方法为主事务,内部方法为子事务,事务嵌套)

如果主事务发生了回滚,子事务也会跟着回滚

案例一:transfer方法有异常并回滚,update方法没有异常但是也会回滚

详解事务:Mysql事务、Spring事务、分布式事务_第16张图片

# 创建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

两个方法都没有修改数据库,数据库的数据不变

案例二:transfer方法没有异常,update方法有异常,子事务回滚不影响主事务

给update方法加上异常处理,否则主事务也还是会回滚
详解事务:Mysql事务、Spring事务、分布式事务_第17张图片

# 创建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 

在这里插入图片描述

(6)MANDATORY(强制性)

MANDATORY的方法事务不能独立存在,必须加到一个事务中去
(1)如果transfer方法有事务,那么update方法就加入到transfer方法的事务中去
(2)如果transfer方法没有事务,那么update方法的事务就会报错
在这里插入图片描述

(7)SUPPORTS

(1)如果transfer方法有事务,那么update方法就加入到transfer方法的事务中去
(2)如果transfer方法没有事务,那么update方法的事务加了也没用,直接用非事务的方式继续运行

(8)NOT_SUPPORTED(有事务就挂起)

加了这个属性以后,如果当前方法加了事务,就把事务挂起,以非事务的方式运行

(9)NEVER(禁止添加事务)

加了这个属性以后,如果当前方法加了事务,就会直接抛出异常,必须以非事务的方式运行

【4】回滚规则

什么情况下才会回滚?
只有在遇到运行时异常RuntimeException 的时候才会回滚,如果遇到的是检查性异常,就不会触发回滚

(1)添加一个IOException,那么就不会触发回滚,因为不是运行时异常
详解事务:Mysql事务、Spring事务、分布式事务_第18张图片
(2)添加rollbackfor参数,修改回滚的规则,指定哪种异常会回滚
这个时候就可以触发回滚了
详解事务:Mysql事务、Spring事务、分布式事务_第19张图片
(3)也可以用noRollbackFor参数,指定哪种异常不回滚

【5】是否只读

如果一个业务方法只有一个查询sql,那就没有必要添加事务了,添加事务反而会导致执行的效率变慢。

但是如果一个业务方法中有多个查询sql,默认情况下,每个查询sql都会开启一个独立的事务,这个时候如果有并发任务修改了数据的话,一个方法中的多个查询sql可能就会查出不同的结果,多个事务使用隔离级别就无法解决。

开启只读之后,就可以保证多个查询事务的查询结果是一样的
详解事务:Mysql事务、Spring事务、分布式事务_第20张图片如果只有一个查询sql就没必要添加了,如果有多个查询sql,可以视情况判断是否要添加

【6】超时时间

设置一个超时时间,如果在这个时间内事务还没有执行完,就会自动回滚这个事务,避免出现大事务

详解事务:Mysql事务、Spring事务、分布式事务_第21张图片

【7】事务失效的场景和原因

(1)事务注解只有加在public方法上才会有效

如果加在private方法,会直接爆红线;如果加在protected方法,虽然不会爆红,但是事务不会生效
详解事务:Mysql事务、Spring事务、分布式事务_第22张图片

(2)自调用会造成事务失效

没有事务的m1方法内部调用了有事务的transfer方法,但是其他方法调用m1方法的时候,transfer方法的事务会失效
详解事务:Mysql事务、Spring事务、分布式事务_第23张图片
原因就是事务注解的原理为AOP代理,代理模式只会拦截通过代理传入的方法

注解写到方法的实现类上,不要写到接口上去

(3)

【8】声明式事务实现的底层原理

(三)分布式事务

你可能感兴趣的:(Java基础与算法,Mysql,spring,mysql,分布式)