Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
Home
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 设计模式
  • JavaSE
  • JVM
  • JUC
  • Netty
  • CPP
  • QT
  • UE
  • Go
  • Gin
  • Gorm
  • HTML
  • CSS
  • JavaScript
  • vue2
  • TypeScript
  • vue3
  • react
  • Spring
  • SpringMVC
  • Mybatis
  • SpringBoot
  • SpringSecurity
  • SpringCloud
  • Mysql
  • Redis
  • 消息中间件
  • RPC
  • 分布式锁
  • 分布式事务
  • 个人博客
  • 弹幕视频平台
  • API网关
  • 售票系统
  • 消息推送平台
  • SaaS短链接系统
  • Linux
  • Docker
  • Git
GitHub (opens new window)
  • Spring常见面试题
  • Spring常用注解与接口
  • 容器与Bean
  • AOP
  • Spring事务
    • Spring支持的事务管理方式
    • Spring事务管理接口
    • 事务传播行为
    • 事务失效场景
  • Spring中的设计模式
  • 手撸Spring

  • 事务嵌套
  • 事件监听器
  • Spring自定义异常
  • Spring
Nreal
2024-03-03
目录

Spring事务

# Spring支持的事务管理方式

  • 编程式事务

  • 声明式事务;

    通过AOP实现,@Transactional

# Spring事务管理接口

PlatformTransactionManager

Spring并不直接管理事务,而是提供了多种事务管理器,通过这个接口,Spring为多个平台:JDBC,JPA,Hibernate等提供了对应的事务管理器,具体实现由各个平台;

底层的事务实现取决于数据库;

# 事务传播行为

解决业务层方法之间互相调用的事务问题;

案例:A类的a方法中 调用了 B类的b方法,如果b发生回滚,如何配置事务传播行为才能让a也跟着回滚?

Spring的传播枚举类:

public enum Propagation {

    REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

    SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

    MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

    REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

    NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

    NEVER(TransactionDefinition.PROPAGATION_NEVER),

    NESTED(TransactionDefinition.PROPAGATION_NESTED);

    private final int value;

    Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • REQUIRED

    支持当前事务,如果当前不存在则新开启一个事务(默认配置);

    @Service
    public class T1Service {
    
        @Resource
        private TestMapper testMapper;
    
        @Resource
        private T2Service t2Service;
        
        @Transactional
        public void func() {
            testMapper.updateT1();
            t2Service.func();
        }
    }
    
    @Service
    public class T2Service {
    
        @Resource
        private TestMapper testMapper;
    
        @Transactional
        public void func() {
            testMapper.updateT2();
            int i = 1 / 0;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    当方法执行到int i = 1 / 0时会抛出异常,此时t1、t2表中的数据都不会被修改,因为这两个方法使用的是同一个事务,所以只要有一个遇到异常,两个更新就都不会成功;

  • NEW

    创建一个新事务,如果当前已存在事务则挂起当前事务;

    t2的数据不会被更新;

    // t1Service
    public void func() {
        testMapper.updateT1();
        t2Service.func();
    }
    
    // t2Service
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void func() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    与REQUIRED区别?

    // t1Service
    @Transactional
    public void func() {
        testMapper.updateT1();
        t2Service.func();
        int i = 1 / 0;
    }
    
    // t2Service
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void func() {
        testMapper.updateT2();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    t2的数据不会回滚,t1的数据会回滚,因为t2和t1不是一个事务;

  • NESTED

    如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务;

    案例1:与REQUIRED区别;

    在t1Service中调用t2Service时,对t2Service抛出的异常进行了捕获,并且自己也没有再抛出;

    当t2Service配置为REQUIRED时,t1、t2都进行了回滚,因为是同一个事务;

    // t1Service
    @Transactional
    public void func() {
        testMapper.updateT1();
        // catch异常的原因是想说明,在t1Service的func方法中,是不会因为调用t2Service遇到异常而被回滚的,因此异常已经被catch了。回滚主要是因为使用的是同一个事务。
        try {
            t2Service.func();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // t2Service
    @Transactional(propagation = Propagation.REQUIRED)
    public void func() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    当t2Service配置为NESTED,t1则不会回滚;

    案例2:与NEW区别;

    // t1Service
    @Transactional
    public void func() {
        testMapper.updateT1();
        System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
        try {
            t2Service.func();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // t2Service
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void func() {
        testMapper.updateT2();
        System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    如果是NEW,输出结果为:

    com.demo.transaction.service.T1Service.func
    com.demo.transaction.service.T2Service.func
    
    1
    2

    如果是NESTED,输出结果为:

    com.demo.transaction.service.T1Service.func
    com.demo.transaction.service.T1Service.func
    
    1
    2

    使用NESTED时,虽然还是同一个事务,但却可以在多个方法中进行控制;

# 事务失效场景

  1. 注解作用private方法上;

    private修饰的方法,spring无法为其生成代理;

    public void func() {
        t1Service.func2();
    }
    @Transactional
    private void func2() {
        testMapper.updateT1();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. final类型方法;

    同spring无法生成代理;

  3. 方法内部调用;

    func2方法是由func调用,虽然func2方法上加了@Transactional注解,但事务不会生效,testMapper.updateT2()执行的方法并不会回滚;

    public void func() {
        testMapper.updateT1();
        func2();
    }
    
    @Transactional
    public void func2() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    解决方式:

    public void func() {
        testMapper.updateT1();
        T1Service t1Service = (T1Service) AopContext.currentProxy();
        t1Service.func2();
    }
    @Transactional
    public void func2() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. 另一个线程中使用事务;

    Spring事务管理的方式就是通过ThreadLocal把数据库连接与当前线程绑定,如果新开启一个线程自然就不是一个数据库连接了,自然也就不是一个事务;

    @Transactional
    public void func() {
        testMapper.updateT1();
        new Thread(() -> t2Service.func()).start();
        int i = 1 / 0;
    }
    
    1
    2
    3
    4
    5
    6
AOP
Spring中的设计模式

← AOP Spring中的设计模式→

Theme by Vdoing | Copyright © 2021-2024
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式