Spring 学习笔记之 @Transactional 异常不回滚汇总

使用springboot时,只要引入spring-jdbc/jpa相关的依赖后,在想要启用事务的方法上加上@Transactional注解就能开启事务,碰到异常就能自动回滚。大大的提高了编码的便捷性性,同时也不侵入代码,保持了代码的简洁性。

默认情况下,Spring时使用的Spring AOP (mode=Mode.Proxy, proxyTargetClass=false)方式启动数据库事务拦截。只有了解清楚了具体背景,才能清除知道事务为什么在碰到异常时没有能够正确回滚。下面是一些常用场景分析:

场景1、未正确配置TransactionManager

使用springboot开发时,引入以下依赖后通常会自动启用TransactionManager。

  • spring-boot-starter-jdbc 是 Spring Boot 提供的用于简化 JDBC(Java Database Connectivity)开发的启动器,引入该依赖后,Spring Boot 会自动配置 DataSourceTransactionManager

  • spring-boot-starter-data-jpa 是 Spring Boot 提供的用于简化 JPA(Java Persistence API)开发的启动器,它集成了 Hibernate 等 JPA 实现框架,方便开发者进行数据库操作。引入该依赖后,Spring Boot 会自动配置 JpaTransactionManager

通过下面代码中的printTransactionManager(TransactionManager transactionManager) 方法可以检查是否配置正常。

@SpringBootApplication
public class MybatisApplication {public static void main(String[] args) {org.springframework.boot.SpringApplication.run(MybatisApplication.class, args);}@BeanObject printTransactionManager(TransactionManager transactionManager) {System.out.println("transactionManager: " + transactionManager);return null;}
}

transactionManager: org.springframework.jdbc.support.JdbcTransactionManager@590765c4 

通过打印语句,可以看到spring中的TransactionManager是否正确配置。

场景2、@Transaction注解不在public方法上

默认情况下,事务是在proxy模式下(即Spring AOP负责拦截事务),proxyTargetClass=false ,有接口的时候使用JDK动态代理实现。没有接口时使用CGLIB进行代理。

JDK代理接口时,都是public方法。

CGLIB代理时,在public方法上能生效。在Spring 6.0 以后,除public方法外,可以代理protected, package-visable修饰的方法。

当@Transactional注解位于private/final修饰的方法上时,事务碰到异常不能正常回滚。

详情参考文档:

Method visibility and @Transactional in proxy mode

The @Transactional annotation is typically used on methods with public visibility. As of 6.0, protected or package-visible methods can also be made transactional for class-based proxies by default. Note that transactional methods in interface-based proxies must always be public and defined in the proxied interface. For both kinds of proxies, only external method calls coming in through the proxy are intercepted.

Using @Transactional :: Spring Framework

示例代码:

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalprivate void createUser(User user) {// 插入用户数据userRepository.save(user);// 可能会抛出异常if (user.getName().equals("error")) {throw new RuntimeException("创建用户失败");}}
}

因为方法的可见性,private修饰的方法不能被代理拦截

场景3、调用内部方法

@Transactional 注解使用 AOP 实现事务管理,而 AOP 是基于代理模式的。当在同一个类内部一个没有事务注解的方法调用有 @Transactional 注解的方法时,事务注解会失效,因为这种调用没有经过代理对象。

示例代码如下:

// 定义 UserService 类,处理用户相关业务逻辑
@Service
class UserService {@Autowiredprivate UserRepository userRepository;// 无事务注解的方法,内部调用有事务注解的方法public void outerMethod() {try {innerMethod();} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());}}// 有事务注解的方法@Transactionalpublic void innerMethod() {User user = new User("John");userRepository.save(user);// 模拟抛出异常throw new RuntimeException("Simulated exception");}
}

示例代码中,虽然Spring AOP代理了innerMethod方法,但是原始事务不是通过代理的innerMethod进入,而是通过原始类的outerMethod进入,这样就调用的是原始类的innerMethod方法,导致不能进入代理类的innerMethod方法,事务拦截不能生效。

场景4、方法内部Catch了异常

在 Spring 中使用 @Transactional 注解时,如果在方法内部捕获了异常且没有重新抛出,会导致事务无法正常回滚,从而使 @Transactional 注解失效。

@Transactional 注解的事务管理是基于 AOP 实现的,它会在目标方法抛出异常时进行事务回滚。默认情况下,@Transactional 注解只对未被捕获的 RuntimeException 及其子类异常进行回滚操作。如果在方法内部捕获了异常,Spring 就无法感知到异常的抛出,从而不会触发事务回滚逻辑,导致事务继续提交,@Transactional 注解的功能失效。

示例代码

// 定义 UserService 类,处理用户相关业务逻辑
@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() {User user = new User("John");userRepository.save(user);try {// 模拟抛出异常throw new RuntimeException("Simulated exception");} catch (Exception e) {// 捕获异常但未重新抛出System.out.println("Exception caught: " + e.getMessage());}}
}

createUser() 方法:使用 @Transactional 注解标记,在方法内部保存用户信息后抛出一个 RuntimeException 异常,并在 catch 块中捕获该异常,但没有重新抛出。

解决办法:重新抛出异常

在 catch 块中重新抛出异常,让 Spring 能够感知到异常的发生,从而触发事务回滚逻辑。

@Transactional
public void createUser() {User user = new User("John");userRepository.save(user);try {throw new RuntimeException("Simulated exception");} catch (Exception e) {System.out.println("Exception caught: " + e.getMessage());// 重新抛出异常throw e;}
}

场景5、 rollbackFor/rollbackForClassName属性未正确配置

  • 默认回滚规则:@Transactional注解默认只对RuntimeException及其子类和Error进行回滚。若抛出的是受检查异常(如IOExceptionSQLException),默认不会触发回滚。

@Service
class UserService {@Autowiredprivate UserRepository userRepository;@Transactionalpublic void createUser() throws IOException {User user = new User("John");userRepository.save(user);// 抛出受检查异常throw new IOException("Simulated IOException");}
}

IOException是一个CheckedException的子类,不在默认的回滚体系内,所以不能自动回滚。需要使用rollbackFor属性显式指定才能生效。

  • rollbackFor=BaseException.class, 针对BaseException和它的子类回滚。若抛出的异常不在继承体系内,则不能自动回滚。
    @Service
    public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackFor = BaseException.class)public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollback// throw new SubException("出现未知错误");// will not rollbackthrow new OtherException("出现未知错误");}
    }class BaseException extends Exception {public BaseException(String message) {super(message);}
    }class SubException extends BaseException {public SubException(String message) {super(message);}
    }class OtherException extends Exception {public OtherException(String message) {super(message);}
    }

    rollbackForClassName=exceptionPattern, exceptionPattern可以包含异常名字全部或者部分字符。注意这里不是正则表达式,而是基于String.contains(exceptionPattern)来判断的。

    匹配规则的源码如下:

private int getDepth(Class<?> exceptionType, int depth) {if (this.exceptionType != null) {if (this.exceptionType.equals(exceptionType)) {// Found it!return depth;}}else if (exceptionType.getName().contains(this.exceptionPattern)) {// Found it!return depth;}// If we've gone as far as we can go and haven't found it...if (exceptionType == Throwable.class) {return -1;}return getDepth(exceptionType.getSuperclass(), depth + 1);}

不能匹配的示例代码如下:

@Slf4j
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base*Exception")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will not rollbackthrow new Base1Exception("出现未知错误");}
}

 因为exception.typeName="Base1Exception" contains("Base*Exception") 结果未false,所以不匹配,导致不能回滚。

正确的用法如下:

@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Transactional(rollbackForClassName = "Base")public void createOrder(Order order) throws Exception {orderRepository.createOrder(order);// will rollbackthrow new Base1Exception("出现未知错误");}
}

以上是我过去经常碰到的@Transactional碰到异常不能正常回滚的案例总结,若有遗漏欢迎下方留言。

参考文档:

Declarative Transaction Management :: Spring Framework

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

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

相关文章

React 与 Vue 虚拟 DOM 实现原理深度对比:从理论到实践

在现代前端开发中&#xff0c;React 和 Vue 作为最流行的两大框架&#xff0c;都采用了虚拟 DOM&#xff08;Virtual DOM&#xff09; 技术来优化渲染性能。虚拟 DOM 的核心思想是通过 JavaScript 对象模拟真实 DOM&#xff0c;减少直接操作 DOM 的开销&#xff0c;从而提高页面…

WordPress AI 原创文章自动生成插件 24小时全自动生成SEO原创文章 | 多语言支持 | 智能配图与排版

为什么选择Linkreate AI内容生成插件&#xff1f; ✓ 全自动化工作流程 - 从关键词挖掘到文章发布一站式完成 ✓ 多语言支持 - 轻松覆盖全球市场&#xff08;中/英等多语种&#xff09; ✓ 智能SEO优化 - 自动生成搜索引擎友好的内容结构 ✓ AI智能配图 - 每篇文章自动匹配高质…

GPU加速-系统CUDA12.5-Windows10

误区注意 查看当前系统可支持的最高版本cuda&#xff1a;nvidia-smi 说明&#xff1a; 此处显示的12.7只是驱动对应的最高版本&#xff0c;不一定是 / 也不一定需要是 当前Python使用的版本。但我们所安装的CUDA版本需要 小于等于它&#xff08;即≤12.7&#xff09;因此即使…

IOT项目——DIY 气象站

开源项目&#xff1a;ESP32 气象站 作者&#xff1a;GiovanniAggiustatutto 原文链接&#xff1a;原文 开源项目&#xff1a;太阳能 WiFi 气象站 V4.0 作者&#xff1a;opengreenenergy 原文链接&#xff1a;原文 DIY 气象站 简介1-制版2-物料 温度设备塔风向标风速计雨量计框…

5G助力智慧城市的崛起——从概念到落地的技术实践

5G助力智慧城市的崛起——从概念到落地的技术实践 引言&#xff1a;智慧城市中的“隐形脉络” 随着城市化的快速推进&#xff0c;传统的城市管理方式已经难以满足人口增长和资源优化的需求。智慧城市的概念应运而生&#xff0c;通过技术创新实现智能化、可持续发展的城市生态…

【Linux】web服务器的部署和优化

目录 nginx的安装与启用--/usr/share/nginx/html默认发布目录 nginx的主配置文件--/etc/nginx/nginx_conf nginx的端口 nginx默认发布文件--index.html nginx默认发布目录 nginx的访问控制 基于IP地址的访问控制 基于用户认证的访问控制 nginx的虚拟主机--/etc/nginx/…

结合五层网络结构讲一下用户在浏览器输入一个网址并按下回车后到底发生了什么?

文章目录 实际应用第一步&#xff1a;用户在浏览器输入 www.baidu.com 并按下回车1. 浏览器触发域名解析&#xff08;DNS查询&#xff09; 第二步&#xff1a;DNS请求的逐层封装与传输1. 应用层&#xff08;DNS协议&#xff09;2. 传输层&#xff08;UDP协议&#xff09;3. 网络…

深入理解N皇后问题:从DFS到对角线优化

N皇后问题是一个经典的算法问题&#xff0c;要求在NN的棋盘上放置N个皇后&#xff0c;使得它们互不攻击。本文将全面解析该问题的解法&#xff0c;特别聚焦于DFS算法和对角线优化的数学原理。 问题描述 在NN的国际象棋棋盘上放置N个皇后&#xff0c;要求&#xff1a; 任意两个…

Java面试场景篇:分布式锁的实现与组件详解

互联网大厂Java求职者面试&#xff1a;分布式锁的实现与组件 在一场紧张而又充满挑战的面试中&#xff0c;Java架构师马架构正面对着一位经验丰富的面试官。以下是他们之间关于分布式锁实现方式及相关问题的对话。 第一轮提问 面试官&#xff1a;请介绍一下分布式锁的概念。…

关于使用 读光-文字检测-DBNet行检测模型-中英-通用领域,版本问题

关于使用 读光-文字检测-DBNet行检测模型-中英-通用领域&#xff0c;版本问题 pip install modelscopeSuccessfully installed certifi-2025.4.26 charset-normalizer-3.4.1 colorama-0.4.6 idna-3.10 modelscope-1.25.0 requests-2.32.3 tqdm-4.67.1 urllib3-2.4.0 pip insta…

刷刷刷刷刷RCE

云曦历年考核 25年春开学考 RCCCE 开启题目进行代码审计 GET传参传入一个参数cmd&#xff0c;但对参数内容给了黑名单进行过滤 $blacklist /bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|"|\>|\<|\%|\$/i; ls、cat等都…

2024江西ICPC部分题解

题目列表 A - Maliang Learning PaintingC - LiarG - Multiples of 5H - ConvolutionJ - Magic MahjongK - Magic Tree A - Maliang Learning Painting 题目来源&#xff1a;A - Maliang Learning Painting 思路分析 这是个签到题&#xff0c;直接输出abc即可 #include<b…

Pytorch图像数据转为Tensor张量

PyTorch的所有模型&#xff08;nn.Module&#xff09;都只接受Tensor格式的输入&#xff0c;所以我们在使用图像数据集时&#xff0c;必须将图像转换为Tensor格式。PyTorch提供了torchvision.transforms模块来处理图像数据集。torchvision.transforms模块提供了一些常用的图像预…

为什么vllm能够加快大模型推理速度?

vLLM加速大模型推理的核心技术原理可分解为以下关键创新点&#xff1a; 一、‌内存管理革命&#xff1a;PagedAttention‌ KV Cache分页机制‌ 将传统连续存储的KV Cache拆分为非连续内存页&#xff0c;类似操作系统内存分页管理&#xff0c;消除内存碎片并实现动态分配。13B…

第十一章 多态

多态是面向对象开发过程中一个非常重要的概念。 11.1 多态概述 11.1.1 什么是多态 多态&#xff08;polymorphism&#xff09;&#xff0c;从字面理解是“多种形态&#xff0c;多种形式”&#xff0c;是一种将不同的特殊行为泛化为当个特殊记号的机制。 多态从实现的角度可划…

RNN——循环神经网络

一.基本结构 1.目标&#xff1a;处理序列数据&#xff08;时间序列&#xff0c;文本&#xff0c;语音等&#xff09;&#xff0c;捕捉时间维度上的依赖关系 核心机制&#xff1a;通过隐藏状态&#xff08;hidden State&#xff09;传递历史信息&#xff0c;每个时间步的输入包…

性能提升手段--池化技术

看到hadoop代码里有ByteBufferPool,使用池子来避免频繁创建、销毁ByteBuffer,减轻GC压力,提高性能。 顺便总结一下池化技术 一、什么是池化技术?​​ ​​池化(Pooling)​​ 是一种资源管理策略,通过​​预先创建并复用资源​​(如数据库连接、线程、内存对象等)来提…

数据安全和合规性市场分析

一、什么是数据安全和合规性 在数据安全和合规性方面&#xff0c;存在着一系列重要的法律、法规和行业标准&#xff0c;这些规定了组织如何收集、存储、处理和保护个人数据及其他敏感信息。企业之所以要遵守这些规定&#xff0c;是出于多方面的考量&#xff0c;既有法律责任&a…

【每日八股】复习计算机网络 Day4:TCP 协议的其他相关问题

文章目录 昨日内容复习已经建立了 TCP 连接&#xff0c;客户端突然出现故障怎么办&#xff1f;什么时候用长连接&#xff1f;短连接&#xff1f;TCP 的半连接队列与全连接队列&#xff1f;什么是 SYN 攻击&#xff1f;如何避免&#xff1f;TIME_WAIT 的作用&#xff1f;过多如何…

React:<></>的存在是为了什么

1. <></> 是什么&#xff1f; <></> 是 React 的Fragment&#xff08;片段&#xff09;语法糖&#xff0c;等价于 <React.Fragment></React.Fragment>。 2. 它的作用 主要作用&#xff1a; 允许你在组件里返回多个元素&#xff0c;而不需…