AOP声明式事物@Transactional失效的几种场景(7种)

本文解释了声明式事务和编程式事务的区别,并详细列举了7种可能导致@Transactional注解失效的情况,帮助开发者更好地理解和应用Spring框架中的事务管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是声明式事物什么是编程式事物?


大白话讲,编程事物就是在代码手动实现事物的回滚,例如try-catch-finally捕获了异常,在finall手动调用方法实现rollback下;声明事物常用的就是AOP实现的切面原理,切入一个@Transactional注解,交给spring自己管理实现,可以被标记在类上、接口、方法上。

二、@Transactional在某些场景下会失效,下边详谈(列举7种)


(1)@Transactional配置的方法非public权限修饰(例如private的就别加了);
(2)@Transactional所在类非Spring容器管理的bean(例如一个Util就别加了,都没注入容器,你叫Spring怎么用他的AOP帮你管理事务?);
(3)@Transactional所在类中,注解修饰的方法被类内部方法调用(例如同一个class类中,方法A调用方法B,只在方法B加了@Transactional注解,那就失效了);
(4)业务代码抛出异常类型非RuntimeException,事务失效

想要Exception都会滚咋办?

设置:@Transactional(rollbackFor = Exception.class)

借一张图展示下:

在这里插入图片描述
(5)业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常(只有该异常或者他的父异常例如Exception可以回滚);
(6)注解@Transactional中Propagation属性值设置错误(例如Propagation.NOT_SUPPORTED,不过谁会设置这玩意?)
(7)mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(简单来说就是你用的DB压根不支持事物,那就没得谈了);

### Spring Boot 中事务失效的常见场景及其原因 #### 非公共方法上使用 `@Transactional` 注解 当在一个非 `public` 方法上应用 `@Transactional` 注解时,该注解不会起作用。这是因为 Spring事务管理依赖于 AOP 动态代理机制来拦截被标记的方法调用并为其提供事务支持。然而,默认情况下,只有对外暴露的服务接口中的公开方法才会创建代理实例[^1]。 ```java @Service class MyService { @Transactional protected void nonPublicMethod() { /* ... */ } // 事务不起效 @Transactional public void publicMethod() { /* ... */ } // 正常工作 } ``` #### 同一 Bean 内部自我调用 如果在同一个类内的某个带有 `@Transactional` 的方法尝试去调用另一个同样位于此类下的无此标注或者有不同传播行为设置的方法,则后者将不在前者开启的事物上下文中运行。因为内部调用并不会触发外部由AOP所建立起来的那个环绕通知链路,所以也就失去了原本预期得到的那种跨多个操作共享同一笔交易的能力[^4]。 ```java @Service class SelfCallingBean { @Transactional public void outerCall(){ innerNonTx(); // 不会在outerCall的事务中执行 } private void innerNonTx(){/*...*/} } ``` 为了使这种情形下的事物能够正常运作,可以通过让服务层组件实现自定义接口的方式来自我引用自己;或者是利用 `ApplicationContextAware` 接口获取当前应用程序环境进而间接访问到目标bean对象再做相应处理。 #### 跨线程异步调用破坏了原有事务边界 对于多线程环境下发起的任务来说,由于每个新启动出来的子线程都拥有独立堆栈空间以及局部变量表结构等原因使得它们之间相互隔离互不影响。因此即使父级进程中存在有效的事务范围也难以传递给这些新开辟出来的工作单元使其继承下来继续沿用下去[^3]。 ```java @Transactional void asyncOperation(){ CompletableFuture.runAsync(() -> {/* 这里的代码不是在事务范围内*/}); } ``` 要解决这个问题可以在配置文件里指定合适的任务执行器,并确保所有涉及数据库变更的操作都在主线程完成后再提交结果给其他协作者知晓即可。 #### 错误异常捕获阻止回滚发生 默认状况之下只要遇到未被捕获住的 RuntimeException 或者 Error 类型错误就会自动触发整个过程来回退至最初状态之前的样子。但是假如有开发者不小心把 try-catch 块写到了不该有的地方导致那些本应抛出去供框架层面捕捉识别的信息反而提前得到了遏制的话,那么最终很可能造成部分更新成功而另一些却失败的现象出现[^2]。 ```java @Transactional(rollbackFor=Exception.class) public void operationWithProperRollback(Exception e){ throw new Exception("This will cause rollback",e); } @Transactional(noRollbackFor=IllegalArgumentException.class) public void operationWithoutRollback(IllegalArgumentException e){ throw new IllegalArgumentException("Won't trigger a rollback"); } ``` 通过合理设定 `rollbackFor` 和 `noRollbackFor` 参数可以精确控制哪些类型的异常应该引起回滚动作的发生与否。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值