侧边栏壁纸
博主头像
zcarry博主等级

BUG,永无止境

  • 累计撰写 13 篇文章
  • 累计创建 28 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Spring 事物管理的使用

zcarry
2022-07-15 / 0 评论 / 0 点赞 / 3,545 阅读 / 2,024 字 / 正在检测是否收录...

Spring事物写法

  • 编程式事务
    指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
    优点
    事物管理的细度可以作用到方法内部
    缺点
    侵入代码,开发量较大

  • 声明式事物
    管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
    优点
    代码无侵入性,可以使用xml配置或者@Transactional注解 来实现一个方法或者类的事物管理
    缺点
    事物管理的细度做大作用到类上(可以规划好业务代码块来规避该问题)
    该文章是基于 声明式事物的使用讲解

事物隔离级别

多个用户对同一条数据进行操作时,不可避免就会产生事物问题,这里需要先了解是事物隔离级别 以及所带来的问题

  • 脏读(Dirty read):
    当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

  • 不可重复读(Unrepeatableread):
    一个事务A在第一次读数据和第二次读数据之间,有另一个事务B把这个数据更改并提交了,所以就出现了事务A在读取一个数据两次的情况下,读到的结果不一样

  • 幻读(Phantom read):
    事务A第一次用where条件筛选出了10条数据,事务A第二次用同样的条件筛选出的却是11条数据。因为事务B在事务A的第一次和第二次查询之间进行了插入操作,并且插入的这个数据满足事务A的where筛选条件

    不可重复度与幻读 个人理解的区别: 首先不能仅仅片面的通过名字去区分本质;不可重复度针对的是 同一条数据 在一个事物里 多次查询的结果不一样。幻读针对的是相同条件多次查询的数据条数不一样。前者侧重 单条数据是否发生变化,后者针对,相同条件查询结果集不一样。【个人理解,网上针对两者的区分也很多描述,其本质都是 数据发生了变化】

名称 结果 脏读 不可重复度 幻读
UnCommitted(读未提交) 什么都不解决
Read Committed(读提交) 解决了脏读的问题
Repeatable Read(重复读) (mysql的默认级别)解决了不可重复读 )
Serializable(序列化) 解决所有问题

Spring事物传播性

  • TransactionDefinition.PROPAGATION_REQUIRED:
    如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS:
    如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY:
    如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:
    创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
    以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:
    以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:
    如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

本篇文章主要针对 常用的 PROPAGATION_REQUIRED 以及 PROPAGATION_REQUIRES_NEW的使用

代码部分

Demo 采用的是SpringBoot 以及MybatisPlus ,代码放在Gitee上 请自行下载调试验证 DemoMp: springboot 搭建使用 mybatisplus 使用,事物讲解使用.代码地址
PROPAGATION_REQUIRED 是Spring 默认的隔离级别,也是我们使用最多的场景。

PROPAGATION_REQUIRES_NEW 也是较常见的一种隔离级别,适用于子事物要求单独回滚于提交 不受主事物影响,且子事物也不会影响主事物。例如创建订单后需要写入两个 日志表,但是两张日志表的写入不影响 订单的创建,但是两个日志表 必须要么都成功 要么都失败,此时 可以将写入2个日志的 方法写如一个单独的 PROPAGATION_REQUIRES_NEW 事物,并在创建订单的主事物try 日志的异常。

此处有几个常见的问题:

  1. 为什么我用this.XXX() 事物不生效?Spring @Transactional 是基于AOP切片代理来实现的,如果用this.XXX() 根本没有走代理 故 事物不会生效,注意【这里的this.不生效的前提是调用放本身也没有事务,如果调用方有事务,那么this.也会走加入父级事务的,所以事务还是会生效,简单说 就是走this.那么 this.方法 上的注解不会生效,从而可能会影响了事务。】。

  2. 为什么我事物采用了 PROPAGATION_REQUIRES_NEW 但是 子事物抛异常为什么主事物依然回滚了。此处要说明的是,子事物与主事物互不影响说法是不严谨的,如果在主事物中 并没有处理子事物的异常,那么主事物依然会回滚的。

  3. 基于1的问题,有人可能会问,那我把子事物try住,那我子事物直接用 PROPAGATION_REQUIRED 效果不是一样的吗?如果你这样用 Spring 会抛出异常 Transaction rolled back because it has been marked as rollback-only,因为此处子事物加入了主事物,但是子事物的异常却被try住了,这是不被允许的,所以此处子事物 只能新开启事物。

  4. 为什么 我采用了 PROPAGATION_REQUIRES_NEW 主事物插入的数据,子事物却查询不到,此处就正好解释了可重复度问题,因为子事物是新开的事物与主事物是隔离的互不影响的,此时 主事物被挂起,并没有提交 所以查询不到是正常的。如果想查询到 就 那么就不能采用子事物的方式,采用并列事物,A事物先提交,B事物再查询

  5. 还有问题请欢迎补充

总结:PROPAGATION_REQUIRED 满足于一个流程往下走到底 要么都成功要么都失败的场景, PROPAGATION_REQUIRES_NEW 适用于部分事物 是独立的 且不影响其他事物的,较多记录日志,流水,或者多个流程穿插的一起且没有直接的联系,都需要开启一个新的事物。

有问题或者有疑问的 欢迎留言补充。

0

评论区