spring事务(注解 @Transactional )失效场景

news2024/12/24 20:43:44

目录标题

  • 1. 代理不生效
    • 1.1 将注解标注在接口方法上
    • 1.2 被final、static关键字修饰的类或方法
    • 1.3 类方法内部调用
      • 示例
      • 解决方案:新加一个Service方法
    • 1.4 (类本身) 未被spring管理
  • 2. 框架或底层不支持的功能
    • 2.1 非public修饰的方法
    • 2.2 多线程调用
      • 举例1
      • 举例2
    • 2.3 数据库本身不支持事务
  • 3. 失效场景集三:错误使用@Transactional
    • 传播特性
    • 3.1 传播特性设置错
    • 3.2 rollbackFor属性设置错误
    • 3.3 异常被内部catch
    • 3.4 嵌套事务

1. 代理不生效

Spring中对注解解析都是基于代理的,如果目标方法无法被Spring代理到,那么它将无法被Spring进行事务管理。

Spring生成代理的方式有两种:
基于接口的JDK动态代理,要求目标代理类需要实现一个接口才能被代理
基于实现目标类子类的CGLIB代理

1.1 将注解标注在接口方法上

@Transactional是支持标注在方法与类上的。一旦标注在接口上,对应接口实现类的代理方式如果是CGLIB,将通过生成子类的方式生成目标类的代理,将无法解析到@Transactional,从而事务失效。
这种错误我们还是犯得比较少的,基本上我们都会将注解标注在接口的实现类方法上,官方也不推荐这种。

1.2 被final、static关键字修饰的类或方法

CGLIB是通过生成目标类子类的方式生成代理类的,被final、static修饰后,那么在它的代理类中,就无法重写该方法,而添加事务功能。

1.3 类方法内部调用

如果是方法内部调用,将不会走代理逻辑,也就调用不到了。

示例

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

  
    public void add(UserModel userModel) {
        userMapper.insertUser(userModel);
        updateStatus(userModel);
    }

    @Transactional
    public void updateStatus(UserModel userModel) {
        doSameThing();
    }
}

解决方案:新加一个Service方法

@Servcie
public class ServiceA {
   @Autowired
   prvate ServiceB serviceB;

   public void save(User user) {
         queryData1();
         queryData2();
         serviceB.doSave(user);
   }
 }

 @Servcie
 public class ServiceB {

    @Transactional(rollbackFor=Exception.class)
    public void doSave(User user) {
       addData1();
       updateData2();
    }

 }

1.4 (类本身) 未被spring管理

在我们平时开发过程中,有个细节很容易被忽略。即使用spring事务的前提是:对象要被spring管理,需要创建bean实例。
通常情况下,我们通过@Controller、@Service、@Component、@Repository等注解,可以自动实现bean实例化和依赖注入的功能。
如下所示, 开发了一个Service类,但忘了加@Service注解,比如:

//@Service
public class UserService {

    @Transactional
    public void add(UserModel userModel) {
         saveData(userModel);
         updateData(userModel);
    }    
}

从上面的例子,我们可以看到UserService类没有加@Service注解,那么该类不会交给spring管理,所以它的add方法也不会生成事务。

2. 框架或底层不支持的功能

2.1 非public修饰的方法

自定义的事务方法(即目标方法),它的访问权限不是public,而是private、default或protected的话,spring则不会提供事务功能。

众所周知,java的访问权限主要有四种:private、default、protected、public,它们的权限从左到右,依次变大。

在AbstractFallbackTransactionAttributeSource类的computeTransactionAttribute方法中有个判断,如果目标方法不是public,则TransactionAttribute返回null,即不支持事务。

 // Don't allow no-public methods as required.可以看到, 这里不支持public类型的方法
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
   return null;
}

2.2 多线程调用

举例1

主线程A调用线程B保存Id为1的数据,然后主线程A等待线程B执行完成再通过线程A查询id为1的数据。
这时你会发现在主线程A中无法查询到id为1的数据。因为这两个线程在不同的Spring事务中,本质上会导致它们在Mysql中存在不同的事务中。
Mysql中通过MVCC保证了线程在快照读时只读取小于当前事务号的数据,在线程B显然事务号是大于线程A的,因此查询不到数据。

举例2

@Slf4j
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RoleService roleService;

    @Transactional
    public void add(UserModel userModel) throws Exception {
        userMapper.insertUser(userModel);
        new Thread(() -> {
            roleService.doOtherThing();
        }).start();
    }
}

@Service
public class RoleService {

    @Transactional
    public void doOtherThing() {
        System.out.println("保存role表数据");
    }
}

从上面的例子中,我们可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的。

这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,add方法也回滚是不可能的。

如果看过spring事务源码的朋友,可能会知道spring的事务是通过数据库连接来实现的。当前线程中保存了一个map,key是数据源,value是数据库连接。

我们说的同一个事务,其实是指同一个数据库连接,只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。

2.3 数据库本身不支持事务

比如Mysql的Myisam存储引擎是不支持事务的,只有innodb存储引擎才支持。

3. 失效场景集三:错误使用@Transactional

传播特性

我们在使用@Transactional注解时,是可以指定propagation参数的。
该参数的作用是指定事务的传播特性,spring目前支持7种传播特性:

REQUIRED 如果当前上下文中存在事务,那么加入该事务,如果不存在事务,创建一个事务,这是默认的传播属性值。
SUPPORTS 如果当前上下文存在事务,则支持事务加入事务,如果不存在事务,则使用非事务的方式执行。
MANDATORY 如果当前上下文中存在事务,否则抛出异常。
REQUIRES_NEW 每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
NOT_SUPPORTED 如果当前上下文中存在事务,则挂起当前事务,然后新的方法在没有事务的环境中执行。
NEVER 如果当前上下文中存在事务,则抛出异常,否则在无事务环境上执行代码。
NESTED 如果当前上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

上面不支持事务的传播机制为:PROPAGATION_SUPPORTS,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER。
这三种传播特性才会创建新事务:REQUIRED,REQUIRES_NEW,NESTED。

3.1 传播特性设置错

如果我们在手动设置propagation参数的时候,把传播特性设置错了,比如:

@Service
public class UserService {

    @Transactional(propagation = Propagation.NEVER)
    public void add(UserModel userModel) {
        saveData(userModel);
        updateData(userModel);
    }
}

我们可以看到add方法的事务传播特性定义成了Propagation.NEVER,这种类型的传播特性不支持事务,如果有事务则会抛异常。

3.2 rollbackFor属性设置错误

在这里插入图片描述

默认情况下事务仅回滚运行时异常和Error,不回滚受检异常(例如IOException)。
因此如果方法中抛出了IO异常,默认情况下事务也会回滚失败。
我们可以通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。

3.3 异常被内部catch

@Slf4j
@Service
public class UserService {
    
    @Transactional
    public void add(UserModel userModel) {
        try {
            saveData(userModel);
            updateData(userModel);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }
}

这种情况下spring事务当然不会回滚,因为开发者自己捕获了异常,又没有手动抛出,换句话说就是把异常吞掉了。
如果想要spring事务能够正常回滚,必须抛出它能够处理的异常。如果没有抛异常,则spring认为程序是正常的。

3.4 嵌套事务

UserService
在这里插入图片描述
UserService1
在这里插入图片描述
上面是我想同时回滚UserService与UserService1。但是也会有这种场景只想回滚UserService1中报错的数据库操作,不影响主逻辑UserService中的数据落库。

有两种方式可以实现上述逻辑:

1.直接在UserService1内的整个方法用try/catch包住

2.在UserService1使用Propagation.REQUIRES_NEW传播机制

在这里插入图片描述

一口气怼完12种@Transactional的失效场景
spring事务(注解 @Transactional )失效的12种场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C. Uncle Bogdan and Country Happiness(dfs + 回溯)

Problem - C - Codeforces 波格丹叔叔在弗林特船长的团队里待了很长一段时间,有时会怀念他的家乡。今天他告诉你他的国家是如何引入幸福指数的。有n个城市和n -1条连接城市的无方向道路。任何城市的公民都可以通过这些道路到达任何其他城市。城市编号从1到n&#xf…

【软考:软件设计师】 4 计算机组成与体系结构(三)计算机安全 | 加密技术

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于软考中级:软件设计师系列专栏,本专栏服务于软考中级的软件设计师考试,包括不限于知识点讲解与真题讲解两大部分,并且提供电子教材与电子版真题,关注私聊即可 …

服务(第二篇)LAMP

一、编译安装apache ①关闭防火墙,将安装Apache所需软件包传到/opt目录下 systemctl stop firewalld.service setenforce 0 [rootxxx opt]# ls apr-1.6.2.tar.gz apr-util-1.6.0.tar.gz httpd-2.4.29.tar.bz2 ②安装环境依赖包 yum -y install gcc gcc-c mak…

专业排名全美top6|建筑学硕士学历CSC获批顺利赴美

E老师人文社科背景,二本院校任教,硕士毕业,没有英文文章,且申请周期只有一个月。据此我们提出,以赶上CSC申报为前提,尽量申请美国综合或者专业排名靠前的学校。最终我们助E老师获得美国专业排名TOP6的弗吉尼…

六个阶段形成CRM销售漏斗,优点有哪些

CRM销售漏斗是反映机会状态以及销售效率的重要的销售管理模型。对企业来说,CRM销售漏斗是一个必不可少的工具。通过销售漏斗,企业可以跟踪和分析客户旅程的每个阶段,并制定相应的销售战略。下面来说说,什么是CRM销售漏斗&#xff…

高频PCB电路设计常见的66个问题

随着电子技术快速发展,以及无线通信技术在各领域的广泛应用,高频、高速、高密度已逐步成为现代电子产品的显著发展趋势之一。信号传输高频化和高速数字化,迫使PCB走向微小孔与埋/盲孔化、导线精细化、介质层均匀薄型化,高频高速高…

Redis消息队列实现异步秒杀

Redis秒杀优化 改进秒杀业务,提高并发性能 需求: 1.新增秒杀优惠券的同时,将优惠券的信息保存到redis中 2.基于Lua脚本,判断秒杀库存,一人一单,决定用户是否抢购成功 3.如果抢购成功,将优惠…

Android系统启动流程--init进程的启动流程

这可能是个系列文章,用来总结和梳理Android系统的启动过程,以加深对Android系统相对全面的感知和理解(基于Android11)。 1.启动电源,设备上电 引导芯片代码从预定义的地方(固化在ROM,全称Read …

hive 入门 一般用于正式环境 修改元数据(二)

安装配置可参考 https://blog.csdn.net/weixin_43205308/article/details/130020674 1、如果启动过derby,最小初始化过 在安装路径下删除 derby.log metastore_db rm -rf derby.log metastore_db此处省略安装mysql数据库 2、配置MySQL 登录mysql mysql -uroot …

EightCap易汇:外汇投资入门需要了解哪些必要知识?

外汇市场是国际投资市场,日内交易量巨大,盈利机会极多。外汇是一种含有杠杆的投资产品,杠杆带来了高收益,也会带来高风险,对于外汇新手来说存在一定难度。新手投资者要如何交易,才能抓住外汇市场的盈利机会…

C++标准库 -- 关联容器 (Primer C++ 第五版 · 阅读笔记)

C标准库 -- 关联容器(Primer C 第五版 阅读笔记)第11章 关联容器------(持续更新)11.1、使用关联容器11.2、关联容器概述11.3、关联容器操作11.4、无序容器第11章 关联容器------(持续更新) 关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和…

助力AI语音开发者的社区-语音之家

语音之家简介 语音之家成立于2021年4月,是一家助力AI语音开发者的社区,我们希望通过知识传播、在线学习、资源分享、各类活动等方式提供全生命周期的服务,帮助全球的AI语音开发者获得成长,洞见AI语音技术领域的发展。目前&#x…

TiDB实战篇-TiDB Lightning 导入数据

简介 使用TiDB Lightning 导入数据。 原理 TiKV进入导入模式 它是使用物理导入的模式,将SQL文件直接导入到TiKV中,它是一种初始化的导入,也就是说目标的数据库和表都是不能够存在的(注意事项,在这种方式导入的时候T…

论文笔记 U-Net: Convolutional Networks for Biomedical Image Segmentation

摘要:人们普遍认为,深度网络的成功训练需要数千个带注释的训练样本。在本文中,我们提出了一种网络和训练策略,该策略依赖于大量使用数据增强来更有效地使用可用的注释样本。该体系结构包括用于捕获上下文的收缩路径和用于实现精确…

计算机组件介绍

1. CPU 1.1 主频 1.2 CPU缓存 注:越高越好 2. Memory 注:只有内存是主存(因为CPU只能和内存打交道),硬盘这种就是外存(因为硬盘太慢了,跟不上cpu的运行速度) 3. I/O 注:输…

Segment Anything论文翻译,SAM模型,SAM论文,SAM论文翻译;一个用于图像分割的新任务、模型和数据集;SA-1B数据集

【论文翻译】- Segment Anything / Model / SAM论文 论文链接: https://arxiv.org/pdf/2304.02643.pdfhttps://ai.facebook.com/research/publications/segment-anything/ 代码连接:https://github.com/facebookresearch/segment-anything 论文翻译&…

微软 AI 作图上线完全免费,“奖励自己”可提升速度

ChatGPT 的横空出世应该已经让大家意识到了 AI 的恐怖。 称不上啥都能干,但给东西它真学,学得还比你快。 最近一段时间 AI 在作图领域又一次人气暴涨。 什么小姐姐写真、突破时间线的历史古图、甚至是抽象的表情包都可能源于 AI 之手。 看着手痒想玩玩…

滴滴滴,请看MYSQL事务的四大特征(ACID)的实现原理:晓其原理而通其实现。

一.什么是事务的四特征 原子性(Atomicity,或称不可分割性)一致性(Consistency)隔离性(Isolation)持久性(Durability) 接下来,我们将对四大特性的具体概念以及…

本地快速搭建Kubernetes单机版实验环境(含问题解决方案)

Kubernetes是一个容器编排系统,用于自动化应用程序部署、扩展和管理。本指南将介绍Kubernetes的基础知识,包括基本概念、安装部署和基础用法。 一、什么是Kubernetes? Kubernetes是Google开发的开源项目,是一个容器编排系统&…

长沙基层公务员待遇调查结果

之前发放了1000份调查问卷,统计过长沙各个行业(其中一半是信息产业从业人员)的待遇情况,发现很多人对长沙公务员(包含有编制/合同工)的待遇很感兴趣。我随手翻了翻几个基层政府单位的财政决算公开说明&…