当前位置: 首页 > article >正文

Spring中@Transactional注解与事务传播机制

文章目录

  • 事务传播机制
  • 事务失效的场景

事务传播机制

事务的传播特性指的是 当一个事务方法调用另一个事务方法时,事务方法应该如何执行。

事务传播行为类型外部不存在事务外部存在事务使用方式
REQUIRED(默认)开启新的事务融合到外部事务中@Transactional(propagation = Propagation.REQUIRED)适用于增删改查
PROPAGATION_SUPPORTS不开启新的事务融合到外部事务中@Transactional(propagation = Propagation.SUPPORTS)适用于查
REQUIRES_NEW开启新的事务不用外部事务,开启新的事务@Transactional(propagation = Propagation.REQUIRES_NEW)适用于内部事务和外部事务不存在业务关联的情况,如记录日志
NOT_SUPPORTED不开启新的事务不用外部事务@Transactional(propagation = Propagation.NOT_SUPPORTED)不常用
NEVER不开启新的事务抛出异常@Transactional(propagation = Propagation.NEVER)不常用
MANDATORY抛出异常融合到外部事务中@Transactional(propagation = Propagation.MANDATORY)不常用
NESTED开启新的事务融合到外部事务中,Savepoint 机制,外层回滚影响内层,内层回滚不影响外层@Transactional(propagation = Propagation.NESTED)不常用

我们平常在 Spring 中使用的注解 @Transactional,不声明 propagation 参数的情况下默认是 REQUIRED 级别。

@Transactional
public void trans() {
    upsert();
    query(); // 查
    log(); // 记录日志
}

@Transactional(propagation = Propagation.SUPPORTS) // 外部没有事务时不用开启新的事务
public info query() {

}

@Transactional(propagation = Propagation.REQUIRES_NEW) // 记录日志和主事务不存在业务关联
public void log() {

}

[注意] 从官方文档中可以看出,我们需要区分逻辑事务以及物理事务,在 Propagation.REQUIRES 等级下,尽管每个内部方法都可以区别于外部事务,独立定义 rollback-only 状态,但是内部逻辑事务的状态都会映射到同一个物理事务上,因此内部事务方法的 rollback-only 标志也会影响外部整个事务的 commit.

https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/tx-propagation.html

事务失效的场景

参考 https://mp.weixin.qq.com/s/m7Pfeq7n9_8s1i4Zoq8qWw

https://www.bilibili.com/video/BV14d4y1r74o/

8 大失效场景:

  1. 方法内的自调用:Spring事务是基于AOP的,只有使用代理对象调用某个方法时,Spring事务才能生效,而在一个方法中调用使用this.xxx 调用方法时,this并不是代理对象,所以会导致事务失效。

    解放办法1:把调用方法拆分到另外一个Bean中

    解决办法2:自己注入自己

    解決办法3:AopContext.currentProxy0+@EnableAspectAutoProxy(exposeProxy = true)

  2. 方法是private的:Spring事务会基于CGLIB来进行AOP,而CGLIB会基于父子类来失效(最终 Spring Bean 调用的是被增强的子类),子类是代理类,父类是被代理类,如果父类中的某个方法是private的,那么子类就没有办法重写它,也就没有办法额外增加Spring事务的逻辑。

  3. 方法是final的:原因和private是一样的,也是由于子类不能重写父类中的final的方法

  4. 单独的线程调用方法:当Mybatis或JdbcTemplate执行SQL时,会从ThreadLocal中去获取数据库连接对象,如果开启事务的线程和执行SQL的线程是同一个,那么就能拿到数据库连接对象,如果不是同一个线程,那就拿到不到数据库连接对象,这样,Mybatis或JdbcTemplate就会自己去新建一个数据库连接用来执行SQL,此数据库连接的autocommit为true,那么执行完SQL就会提交,后续再拋异常也就不能再回滚之前已经提交了的SQL了。

  5. 没加@Configuration注解:如果用SpringBoot基本没有这个问题,但是如果用的Spring,那么可能会有这个问题,这个问题的原因其实也是由于Mybatis或JdbcTemplate会从ThreadLocal中去获取数据库连接,但是ThreadLocal中存储的是一个MAP,MAP的key为Datasource对象,value为连接对象,而如果我们没有在AppConfig上添加@Configuration注解的话,会导致MAP中存的DataSource对象和Mybatis和JdbcTemplate中的DataSource对象不相等,从而也拿不到数据库连接,导致自己去创建数据库连接了。

  6. 异常被吃掉:如果Spring事务没有捕获到异常,那么也就不会回滚了,默认情况下Spring会捕获RuntimeException和Error。

    解释一下上面提到的注意点:外部事务方法 saveUserWithLog 在调用内部事务方法 logService.insertLog 时,虽然捕获了异常,但是内部事务方法有异常抛出时,整个外部事务仍然会回滚并抛出异常 Transaction rolled back because it has been marked as rollback-only,这是因为默认 @Transactional 用的是 REQUIRED 事务传播等级,内部事务方法会融合到外部事务中,内部事务方法出现异常时就会将整个 物理 connection 设置为 rollback-only,所以共用此 connection 的整个外部事务也会回滚。

    @Transactional(rollbackFor = Exception.class)
    public Integer saveUserWithLog() {
        try {
            // 默认 @Transactional(propagation = Propagation.REQUIRED) 抛出异常
            logService.insertLog();
        } catch (Exception e) {
    
        }
    
        User user = new User();
        user.setId(1001);
        user.setName("jxz_rollback");
        user.setAge(1);
        userMapper.insert(user);
    
        return user.getId();
    }
    
  7. 类没有被Spring管理

  8. 数据库不支持事务


http://www.kler.cn/a/419630.html

相关文章:

  • 企业品牌曝光的新策略:短视频矩阵系统
  • 恶意软件模拟sudo行为窃取密码的原理与实现(C/C++实现)
  • diff算法
  • mysql 查询所有的触发器
  • 蓝队基础(泷羽sec)
  • 【Oracle11g SQL详解】ORDER BY 子句的排序规则与应用
  • 【小记】如何刷机
  • Linux:内存文件 基础io
  • 【云原生系列】如何判断哪家云服务器提供商更适合我
  • 基于Matlab BP神经网络的电力负荷预测模型研究与实现
  • 大数据技术Kafka详解 ② | Kafka基础与架构介绍
  • 【手术显微镜】市场高度集中,由于高端手术显微镜的制造技术主要掌握于欧美企业
  • C++草原三剑客之一:继承
  • 1.使用docker 部署redis Cluster模式 集群3主3从
  • 网页端五子棋对战(二)---数据库连接用户登录注册接口设计postman验证
  • 神经网络中的参数(Parameter)和超参数(Hyperparameters)
  • 多线服务器和BGP服务器有什么区别
  • MySQL笔记-启动时log报错Table ‘mysql.user‘ doesn‘t exist
  • camera驱动开发(初学)
  • 复杂网络之BA无标度网络
  • Unity-Particle System属性介绍(一)基本属性
  • Redis——主从复制原理
  • 2024年09月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析
  • C# Winform WaitingForm等待窗体(CSFramework提供)
  • 移动式压力容器充装作业题库分享
  • 【NoSQL数据库】Hbase基本操作——数据库表的增删改查