@Transactional使用中的三类坑

我们知道事务有声明式事务和编程式事务两种,编程式事务代码侵入较高,声明式事务侵入较低,在项目中常有使用,然而,不正确的使用声明式事务,可能让代码未能按照我们的预期执行。

一、事务可能没有生效

  1. @Transactional只有定义在public方法上才生效,因为spring使用动态代理的方式实现aop,来实现对目标方法增强,而private方法是无法代理的,故不能生效(CGLIB通过继承方式实现代理类,private在子类不可见,无法进行事务增强)
  2. 必须通过代理过的类从外部调用方法才生效
    下面这种方式是不会生效的,外部调用的createUserWrong2()方法,再由内部调用createUserPublic()方法
public int createUserWrong2(String name) {
    try {
        this.createUserPublic(new UserEntity(name));
    } catch (Exception ex) {
        log.error("create user failed because {}", ex.getMessage());
    }
  return userRepository.findByName(name).size();
}

//标记了@Transactional的public方法
@Transactional
public void createUserPublic(UserEntity entity) {
    userRepository.save(entity);
    if (entity.getName().contains("test"))
        throw new RuntimeException("invalid username!");
}

this指针代表对象自己,spring不可能注入this
可以通过自己注入自己的方式

@Autowired
private UserService self;

public int createUserWrong2(String name) {
    try {
        self.createUserPublic(new UserEntity(name));
    } catch (Exception ex) {
        log.error("create user failed because {}", ex.getMessage());
    }
  return userRepository.findByName(name).size();
}

//标记了@Transactional的public方法
@Transactional
public void createUserPublic(UserEntity entity) {
    userRepository.save(entity);
    if (entity.getName().contains("test"))
        throw new RuntimeException("invalid username!");
}

打断点可以看到,self是spring通过CGLIB方式增强过的类,二者调用的逻辑如下图

由上面两个坑可以得出,我们务必确认调用@Transactional注解标记的方法是public的,并且是通过Spring注入的 Bean进行调用的
在这里插入图片描述

二、生效了也不一定回滚

要想在出现异常后回滚,需要满足以下两个条件:

  1. 异常传播出了标记了@Transactional方法
  2. 出现RuntimeException或Error

对于第一点的理解,看下面这段代码

    @Transactional
    public void createUserWrong1(String name, Integer age) {
        try {
            UserInfo userInfo = new UserInfo(name, age);
            userInfoRepository.save(userInfo);
            throw new RuntimeException("error");
        } catch (Exception e) {
            log.error("create user failed", e);
        }
    }

由于在方法内捕获了所有异常,导致异常未能传播出去,事务无法回滚

对于第二点的理解,对于下面的受检异常,是无法回滚的

    @Transactional
    public void createUserWrong2(String name, Integer age) throws IOException {
        userInfoRepository.save(new UserInfo(name, 20));
        otherTask();
    }
    //因为文件不存在,一定会抛出一个IOException
    private void otherTask() throws IOException {
        Files.readAllLines(Paths.get("file-that-not-exist"));
    }

那么,这两个问题应当如何解决呢?
首先,第一个问题,如果实在是想自己捕获异常处理,可以手动设置回滚

    @Transactional
    public void createUserRight1(String name, Integer age) {
        try {
            userInfoRepository.save(new UserInfo(name, age));
            throw new RuntimeException("error");
        } catch (Exception e) {
            log.error("create user failed", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

对于第二个问题,想要对所有异常都进行回滚,要在注解中进行声明

    @Transactional(rollbackFor = Exception.class)
    public void createUserRight2(String name, Integer age) throws IOException {
        userInfoRepository.save(new UserInfo(name, 20));
        otherTask();
    }

三、确认事务传播机制符合业务逻辑

有这么一个场景:一个用户注册的操作,会插入一个主用户到用户表,还会注册一个关联的子用户。我们希望将子用户注册的数据库操作作为一个独立事务来处理,即使失败也不会影响主流程,即不影响主用户的注册

通常情况下,我们很容易想到下面这样的代码实现

    // userService
    @Transactional
    public void createUserWrong4(String name, Integer age) {
        UserInfo userInfo = new UserInfo(name, age);
        // 主流程
        userInfoRepository.save(userInfo);
        // 子流程
        subUserService.createSubUserWithExceptionWrong(name, age);
    }

    // subUserService
    @Transactional
    public void createSubUserWithExceptionWrong(String name, Integer age) {
        UserInfo userInfo = new UserInfo(name + "_sub", age);
        userInfoRepository.save(userInfo);
        throw new RuntimeException("invalid status");
    }

子用户抛出一个异常,很明显子任务会失败,如果不加以特殊处理,异常肯定会进一步逃离主任务的createUserWrong4方法,导致主任务也回滚
所以首先想到的是将子任务的异常捕获,这样异常就不会逃离主任务的方法了

    // userService
    @Transactional
    public void createUserWrong5(String name, Integer age) {
        UserInfo userInfo = new UserInfo(name, age);
        // 主流程
        userInfoRepository.save(userInfo);
        // 子流程
        try {
            subUserService.createSubUserWithExceptionWrong(name, age);
        } catch (Exception e) {
            log.error("create sub user error:{}", e.getMessage());
        }
    }

然而,实际情况却是主方法并没有抛出异常,却直接静默回滚了,他在提交的时候,发现子方法把当前事务设置了回滚,因此无法完成提交

org.springframework.transaction.UnexpectedRollbackException: Transaction silently rolled back because it has been marked as rollback-only

这是因为我们主任务和子任务都是同一个事务,子任务标记了事务回滚,主任务自然也不能提交了,处理办法就是将子任务在独立的事务中运行

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void createSubUserWithExceptionRight(String name, Integer age) {
        UserInfo userInfo = new UserInfo(name + "_sub", age);
        userInfoRepository.save(userInfo);
        throw new RuntimeException("invalid status");
    }

propagation指定事务传播策略,REQUIRES_NEW表示当前方法开启一个新事务运行,这样子任务和主任务就互不干扰了。
从上面可以看出,如果方法涉及多次数据库操作,我们务必仔细思考事务的传播方式,防止出现异常的结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony开发案例:【电影卡片】

介绍 本篇Codelab基于元服务卡片的能力,实现带有卡片的电影应用,介绍卡片的开发过程和生命周期实现。需要完成以下功能: 元服务卡片,用于在桌面上添加2x2或2x4规格元服务卡片。关系型数据库,用于创建、查询、添加、删…

前端知识学习笔记-五(ECMAScript 6)

命令行工具 在正式讲解ES6新特性之前,我们需要了解一些命令行工具,在日后的课程中,我们会经常用到命令行 常用命令行工具有两种 CMD 命令行工具 PowerShe门命令行工具 CMD命令行 打开命令行窗口 win: 左下角开始,找到运行&#…

IntelliJ IDEA(WebStorm、PyCharm、DataGrip等)设置中英文等宽字体,英文为中文的一半(包括标点符号)

1.设置前(idea默认字体为 JetBrains Mono) 2.设置后(楷体)

HIT The Wiorld,HIT世界官网地址+配置要求+测试时间+加速器分享

HIT The Wiorld,HIT世界官网地址配置要求测试时间加速器分享 NEXON新游《HIT:世界(HIT:The World)》将在4月17日上线,目前已在官网开启事前预约预创建角色。Hit :the world(HIT:世界&#xff…

苹果个人证书管理

根据近日工业和信息化部发布的《工业和信息化部关于开展移动互联网应用程序备案工作的通知》,相信不少要进行IOS平台App备案的朋友遇到了一个问题,就是apple不提供云管理式证书的下载,也就无法获取公钥及证书SHA-1指纹。 已经上架的应用不想重…

如何在浏览器Web前端在线编辑PPT幻灯片?

有时候在项目中我们会遇到需要在网页在线打开并编辑PPT文档保存到本地或者服务器指定位置,猿大师办公助手可以很方便的调用本机Office实现在网页上编辑PPT幻灯片,效果与本机Office打开PPT完全一样。 猿大师办公助手支持完整嵌入模式,也就是本…

React-样式使用

​🌈个人主页:前端青山 🔥系列专栏:React篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:React-样式使用 目录 1、行内样式 2、使用className属性 3、css module模块化 4、styled-c…

基于JSP本科生毕业设计选题系统的设计与实现(内附设计LW + PPT+ 源码下载)

基于JSP本科生毕业设计选题系统的设计与实现 项目名称: 基于JSP本科生毕业设计选题系统的设计与实现 项目技术栈 该项目采用了以下核心技术栈: 后端框架/库: SSM框架(Spring MVC、Spring、Mybatis)数据库&#xff…

OpenHarmony轻量系统开发【3】代码编译和烧录

3.1源码目录 下载完代码后,大家可以进入代码目录: 这里重点介绍几个比较重要的文件夹: 1 vendor文件夹 该文件夹存放的是厂商相关的配置,包括组件配置、HDF相关配置,代码目录如下: 可以看到有hisilicon文…

IDEA pom.xml显示灰色并被划线

在使用 IDEA 进行开发的过程中,有时候会遇到 pom.xml 显示灰色并被划线的情况,如下图: 这一般是因为该文件被 Maven 忽略导致的,可以进行如下操作恢复: 设置保存后,可以看到 pom.xml 恢复了正常&#xff1a…

github,raw.githubusercontent.com 等网址登陆不上不去的设置方法

目录 提示域名解析错误: 出现的现象: 解决办法:修改host host改完不生效 解决方案1: 解决方案2: 提示域名解析错误: 出现的现象: 登陆github,raw.githubusercontent.com 等网…

解读《算者生存:商业分析的方法与实践》:构建企业经营分析框架的必备指南

💂 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】🤟 一站式轻松构建小程序、Web网站、移动应用:👉注册地址🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交…

LOCK、ACC、ON、START的含义及正确使用

背景 前段时间在开发一个远程锁车的需求时,讨论到了电源状态的场景。由于初次进入汽车电子行业,对很多基础概念不清晰。当时听主机厂商的同事介绍一遍后,并不是很理解。于是趁着空闲,给自己充充电,也希望能够帮到有需…

前端打包webpack vite

起步 | webpack 中文文档 | webpack中文文档 | webpack中文网 npm run build 1webpack: mkdir webpack-demo cd webpack-demo npm init -y npm install webpack webpack-cli --save-dev vite : 快速上手 | Vue.js

Netty学习——实战篇2 NIO 群聊系统(简单版) 备份

需求: 1、编写一个NIO群聊系统,实现服务端和客户端之间数据简单通讯(非阻塞) 2、实现多人群聊 3、服务端:可以监测用户上线、离线、并实现消息转发功能。 4、客户端:通过channel可以无阻塞发送消息给其他所有用户,同时…

【位运算】3097. 或值至少为 K 的最短子数组 II

本文涉及知识点 位运算 LeetCode3097. 或值至少为 K 的最短子数组 II 给你一个 非负 整数数组 nums 和一个整数 k 。 如果一个数组中所有元素的按位或运算 OR 的值 至少 为 k ,那么我们称这个数组是 特别的 。 请你返回 nums 中 最短特别非空 子数组 的长度&…

AI大模型语言开源大语言模型完整列表

开源大语言模型完整列表 Large Language Model (LLM) 即大规模语言模型,是一种基于深度学习的自然语言处理模型,它能够学习到自然语言的语法和语义,从而可以生成人类可读的文本。 所谓"语言模型",就是只用来处理语言文…

游戏开发者必看:Perforce Helix Core 的功能特点及游戏开发中的常用工具、典型用例介绍

「不出海,即出局」随着全球化的加速发展,企业出海已成燎原之势。日前,2024 亚马逊云科技出海全球化论坛在深圳成功举办。龙智携手 Perforce 亮相游戏行业展区,展示了Perforce Helix Core如何与主流游戏开发引擎高效集成&#xff0…

关于《CS创世 SD NAND》的技术学习分享

最近发现一个好玩的东西《CS创世 SD NAND》,带大家一起体验一下。 本文引用了部分厂家产品资料及图像,如有侵权,请及时联系我删除,谢谢。 《CS创世 SD NAND》官方网站:http://www.longsto.com/ 什么是CS创世 SD NAND呢…

c++的学习之路:4、入门(3)

摘要 本章将介绍一下auto、for和指针空值,文章末附上入门的所有代码。 目录 摘要 一、auto 二、for 三、指针空值 四、代码 五、思维导图 一、auto 这个关键字是c提出的,可以自动识别变量的类型,可以看出下方图片,auto自…