别再跟我说你不理解 @Transactional 为什么会失效了!省流版解读

编程入门 行业动态 更新时间:2024-10-10 17:29:48

别再<a href=https://www.elefans.com/category/jswz/34/1736429.html style=跟我说你不理解 @Transactional 为什么会失效了!省流版解读"/>

别再跟我说你不理解 @Transactional 为什么会失效了!省流版解读

别再跟我说你不理解 @Transactional 原理了!省流版解读

    • 前言
    • user 表初始数据
    • 隔离级别:NESTED 案例一
    • 隔离级别:NESTED 案例一省流版答案解读
    • 隔离级别:NESTED 案例一省流版答案源码入口
    • 隔离级别 Propagation.NESTED 省流版源码剖析
    • 隔离级别:NESTED 案例二
    • 隔离级别:NESTED 案例二省流版答案解读
    • 其他隔离级别:REQUIRED、REQUIRES_NEW
    • 码字还是比较耗时的,后续发布对应的源码级别讲解视频到公众号上
    • 本文对应代码地址(包含SQL)

精读版:全面解读spring注解事务失效场景,伪代码+图文深度解析spring源码运行过程

前言

老早前精读过 @Transactional 的源码,现在有时间提炼提炼一下里面的精华出一期,从源码角度剖析 @Transactional 是怎么开启事务的?@Transactional 回滚流程又是怎样的?的角度出发,帮助大家工作中合理的使用 @Transactional 这个注解。

user 表初始数据

隔离级别:NESTED 案例一

  1. nestedMethodA 将 id 为 1 的数据的 username 修改 nestedMethodA
  2. nestedMethodB 将 id 为 2 的数据的 username 修改 nestedMethodB
  3. nestedMethodC 将 id 为 3 的数据的 username 修改 nestedMethodC
  4. 最后主事务 将 id 为 4 的数据的 username 修改 mainplus

思考:最后谁能更新成功?????

/*** A,B成功,C失败抛出异常进入 catch 逻辑*/
@Transactional(propagation = Propagation.NESTED)
@Override
public void main() {try {tabUserService.nestedMethodA();tabUserService.nestedMethodB();tabUserService.nestedErrorMethodC();tabUserService.update(new LambdaUpdateWrapper<TabUser>().eq(TabUser::getId, "4").set(TabUser::getUsername, "mainplus"));} catch (Exception e) {System.err.println(e.getMessage());}
}

隔离级别:NESTED 案例一省流版答案解读

只有 nestedMethodA、nestedMethodB 方法能更新成功


原因 NESTED 是基于回滚点回滚事务的,生成的回滚点的位置,如下,当 nestedErrorMethodC 方法抛出异常后,异常进入到 catch 代码块中,导致

tabUserService.update(new LambdaUpdateWrapper<TabUser>().eq(TabUser::getId, "4").set(TabUser::getUsername, "mainplus"))

这段代码没有执行,因此 id 为 4 的数据没有更新。由于 catch 代码块中并没有接着向上抛出此异常,因此没有触发回滚主事务的操作,同时因为 nestedErrorMethodC 抛出异常,触发了回滚到回滚点 C 的逻辑,因此回滚点 C 以下的代码全部被回滚了。而 nestedMethodA、nestedMethodB 事务不受影响,因此

只有 nestedMethodA、nestedMethodB 方法能更新成功

/*** A,B成功,C失败抛出异常进入 catch 逻辑*/
@Transactional(propagation = Propagation.NESTED)
@Override
public void main() {//主事务try {//回滚点 AtabUserService.nestedMethodA();//回滚点 BtabUserService.nestedMethodB();//回滚点 CtabUserService.nestedErrorMethodC();tabUserService.update(new LambdaUpdateWrapper<TabUser>().eq(TabUser::getId, "4").set(TabUser::getUsername, "mainplus"));} catch (Exception e) {System.err.println(e.getMessage());}
}

隔离级别:NESTED 案例一省流版答案源码入口

@Transactional 注解封装了如下三大逻辑

  1. 生成切面,(被 @Transactional 标注的方法的所在类会被生成一个代理对象)
  2. 开启事务,(执行完目标方法前,会先执行被 Spring 封转好的开启事务的逻辑)
  3. 提交事务,(执行完目标方法后,会执行被 Spring 封转好的提交事务的逻辑)
  4. 回滚事务

就拿下面这段代码举例来说,首先会为 TabUserService 生成一个代理对象,@Transactional 注解封装了如下三大逻辑中的点一。

@Service
public class TabUserServiceImpl extends ServiceImpl<TabUserMapper, TabUser> implements TabUserService {@Autowiredprivate TabUserService tabUserService;/*** A,B成功,C失败抛出异常进入 catch 逻辑*/@Transactional(propagation = Propagation.NESTED)@Overridepublic void main() {try {//回滚点 AtabUserService.nestedMethodA();//回滚点 BtabUserService.nestedMethodB();//回滚点 CtabUserService.nestedErrorMethodC();tabUserService.update(new LambdaUpdateWrapper<TabUser>().eq(TabUser::getId, "4").set(TabUser::getUsername, "mainplus"));} catch (Exception e) {System.err.println(e.getMessage());}}
}

@Transactional 注解封装了如下三大逻辑中的 点二源码入口 如下 DataSourceTransactionManager.doBegin(),省流版不多逼逼,大家自行去 debug,debug 下面的这段代码你将获得如下问题的全套解析:

  1. ThredLocal 在 @Transactional 源码中是如何应用的?
  2. @Transactional(propagation = Propagation.NESTED) Spring 事务中的每个隔离级别是如何生效的?
  3. @Transactional 注解设计的思想

    @Transactional 注解封装了如下三大逻辑中的 点三、点四源码入口 如下 TransactionAspectSupport.invokeWithinTransaction。最新的 @Transactional 源码里面用到了一些函数式接口,用于回掉用的,不清楚这方面的小伙伴,可以先去了解下函数式接口。

隔离级别 Propagation.NESTED 省流版源码剖析


回滚点是如何被保存的?源码入口如下:

AbstractPlatformTransactionManager.handleExistingTransaction()


如何根据回滚点回滚,源码入口如下

AbstractTransactionStatus.rollbackToHeldSavepoint()

隔离级别:NESTED 案例二

nestedMethodA、nestedMethodB、nestedErrorMethodC、主事务均更新失败。

/*** A,B成功,C失败抛出异常进入 catch 逻辑*/
@Transactional(propagation = Propagation.NESTED)
@Override
public void main() {//主事务try {//回滚点 AtabUserService.nestedMethodA();//回滚点 BtabUserService.nestedMethodB();//回滚点 CtabUserService.nestedErrorMethodC();tabUserService.update(new LambdaUpdateWrapper<TabUser>().eq(TabUser::getId, "4").set(TabUser::getUsername, "mainplus"));} catch (Exception e) {System.err.println(e.getMessage());//异常抛到主事务throw e;}
}

隔离级别:NESTED 案例二省流版答案解读

NESTED 隔离级别下的事务,会和主事务共用一个数据库连接(源码入口如下),虽然 nestedErrorMethodC 方法也是 NESTED 隔离级别,只会回滚自己,但是异常被 catch 捕获后,接着向上抛,抛到了主事务中,导致了主事务回滚了,因为 nestedMethodB、nestedMethodA 与主事务共用一个数据库连接,因此一块回滚了。导致nestedMethodA、nestedMethodB、nestedMethodC连体回滚更新失败。主事务更新失败是因为代码没有得到执行。

其他隔离级别:REQUIRED、REQUIRES_NEW

使用上,就字面意思,有时间接着举几个对应的例子,持续更新中~~~~

码字还是比较耗时的,后续发布对应的源码级别讲解视频到公众号上

大家可以先关注我一波,或者关注一下公众号,这段时间上班有点忙,后续有时间更新,敬请期待~~~~~~~~

本文对应代码地址(包含SQL)

代码仓库里面自行查找下载即可

更多推荐

别再跟我说你不理解 @Transactional 为什么会失效了!省流版解读

本文发布于:2023-11-15 13:52:29,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1600769.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:跟我   说你   别再   不理解   省流版

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!