Spring事务传播机制、实现方式、失效场景及原理

贴一篇源码分析的好文章:https://blog.csdn.net/qq_30905661/article/details/114400417

本质:

一个事务对应一个数据库连接。
通过 this 来调用某个带有 @Transactional 注解的方法时,这个注解是失效的

Spring的事务是如何实现的?

  1. spring事务底层是通过数据库事务和AOP实现的
  2. 首先对于使用@Transactional的注解的bean,spring会创建一个代理对象作为bean
  3. 当调用代理对象的方法时,spring会判断该方法上是否加了@Transactional注解
  4. 如果加了,就会利用事务管理器创建一个数据库连接,并修改数据库连接的 autocommit 为 false,禁止自动提交
  5. 然后执行该方法,若方法没有抛异常则会提交事务,反之亦然
  6. spring事物的隔离级别就是对应数据库的隔离级别
  7. spring事务的传播机制是spring自己实现的,是spring事务中最复杂的
  8. spring事物的传播机制是基于数据库连接来做的,一个连接一个事务,传播事务实际上是开了一个新的数据库连接,在此基础上执行sql

Spring事物的传播机制?

spring事务默认是注解是 REQUIRED,支持事务的传播,使用同一个数据库连接。
在这里插入图片描述

REQUIRED:spring默认的事务传播机制,A存在事务,则B加入A的事务;A没有事务则会新建一个数据库事务;

SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行

MANDATORY:(强制性使用第一个事务)A存在事务,则B加入A的事务;A没有事务,则抛异常

REQUIRES_NEW:创建一个新事务,B在这个新事务中执行;A如果有事务将会被挂起,等待B事务方法执行结束(commit or rollback),当B事务执行结束后,A事务被唤醒继续执行,若B抛出了异常给A 或 A 方法执行出了异常,那么在 A 事务中执行的 sql 将会被回滚,B 事务中的sql 由B的事务管理器控制,A、B中的sql不在同一数据库连接中执行,即内层事务B已经 commit 或 rollback, 外层事务干扰不了。

NOT_SUPPORTED:(不支持事务),若A存在事务,则挂起A的事务,以非事务方式运行

NEVER:(不支持事务),若A存在事务抛异常

NESTED:A存在事务,则在嵌套事务中执行;不存在则和 REQUIRED 一样开启一个新事务
在这里插入图片描述

那些情况会导致Spring事务的失效?失效的原因是?

  1. 数据库不支持事务

  2. 类没有被spring管理(ioc),没有加注解。

  3. 未启用Spring事务管理功能(@EnableTransactionManagement)

  4. 数据源没有配置事务管理器

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
    
  5. 没有加@Configuration注解:springboot基本没有这个问题;Spring可能会出现这个问题,原因是由于mybatis或JdbcTemplate会从ThreadLocal中获取数据库连接,但是ThreadLocal底层引用的是ThreadLocalMap,Map的key是一个DataSource对象,value是数据库连接。如果没有加@Configuration注解的话,会导致Map中的DataSource对象和mybatis、jdbcTenplate中的DataSource对象不相等,所有就拿不到数据库连接,以至于自己去创建连接了。

  6. 异常被吃掉:默认情况下Spring会捕获 error 和 RunTimeException ,spring捕获不到异常也就不会回滚了,例如 try-catch

  7. 方法是private的:spring事务基于CGLIB来进行AOP,CGLIB是基于父子类来实现,子类是代理类,子类无法重写父类的private方法,也就没有办法增加spring事务逻辑。

  8. 方法是 final 修饰的,和private原因一致,子类不能重写增强。

  9. 调用A方法和B方法不是同一个线程,不同的线程拿到的数据库连接不一样。TransactionSynchronizationManager.bindResource 会将线程与数据库连接绑定。

  10. rollbackFor = RuntimeException.class(默认),当抛出的异常大于定义的异常,则会导致事务失效

  11. 方法内自调用时对象不是同一个:Spring事务是基于Aop,只有使用代理对象调用 A 方法时,注解才能生效,而在A方法中调用 B 方法时( this.B() ),并不是使用的代理对象,所以导致B的注解失效。

自身调用失效问题:

方法A 通过 this.B() 调用方法B。
在这里插入图片描述

通过 this 来调用某个带有 @Transactional 注解的方法时,这个注解是失效的,可以看做这个方法(如上图B)上没有这个注解,当然书写的传播机制限制也是无效的,例如:propagation = Propagation.MANDATORY、propagation = Propagation.NEVER。
但是若调用A的是CGLIB生成的代理对象,并且A上有 @Transactional 注解,那么方法A是具有事务的,方法B中的sql 就在方法A的事务中执行,所以整体A,B是有事务的。

调用使用@Transactional注解的方法时,使用的是 Spring CGLIB 创建的代理对象
在这里插入图片描述

调用B方法的是存储在 Spring ioc容器的bean,两个不同的对象
在这里插入图片描述

A调用B的结论:

  • 只要A加@Transactional注解,A和B在不在同一个类中,B加不加@Transactional注解,事务都是有效的,则AB在同一事务中。
  • A 不加 B加,A和B同一个类中:调用A方法的是CGLIB生成的代理对象,但是A方法没有注解,所以A方法不会被拦截;this调用B,注解失效(下图)。
  • A 不加 B加,A和B不在同一个类中:不在同一个类,那么调用B的就是的就是CGLIB生成的代理对象,B的事务有效,A在外围没有事务(B已经commit或rollback了,事务管理器已经把设置auto commit = false的数据库连接释放了)。

图3

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值