@Transactional 什么情况下会失效

@Transactional 注解在 Spring 中用于声明式事务管理,但在某些场景下会失效。

1、方法非 public 修饰

  • 原因: Spring AOP 代理(CGLIB/JDK 动态代理)无法拦截 private/protected 的方法。
  • 解决: 确保事务方法为 public。
java 复制代码
// ✅ 正确
@Transactional
public void createUser() { ... }

// ❌ 失效
@Transactional
private void createUser() { ... }

2、自调用问题(同类内调用)

  • 原因: 类内部方法调用(如 A.a() 调用 A.b() )会绕过代理对象,导致 @Transactional 失效。

  • 解决:

    • 注入自身代理对象: @Autowired private MyService self; 后调用 self.b()

    • 通过 AopContext 获取代理:((MyService) AopContext.currentProxy()).b()(需开启 @EnableAspectJAutoProxy(exposeProxy = true))。我更喜欢把逻辑写到另一个类中,然后再进行调用。

java 复制代码
@Service
public class UserService {
    @Autowired
    private UserService self; // 注入自身代理

    public void methodA() {
        // ❌ 直接调用失效
        // methodB(); 

        // ✅ 通过代理调用生效
        self.methodB(); 
    }

    @Transactional
    public void methodB() { ... }
}

3、异常类型未被捕获

  • 原因:

    • 默认只捕获 RuntimeExceptionError

    • 若抛出 IOException受检异常,事务不会回滚。

    • 若异常被 catch 后未重新抛出,事务失效。

  • 解决:

    • 使用 rollbackFor 指定异常类型: @Transactional(rollbackFor = Exception.class)

    • 避免在方法内吞没异常。

java 复制代码
// ✅ 回滚受检异常
@Transactional(rollbackFor = Exception.class)
public void update() throws IOException {
    try {
        // 数据库操作
    } catch (Exception e) {
        // ❌ 错误:吞没异常
        // log.error(e); 

        // ✅ 正确:重新抛出
        throw new BusinessException(e); 
    }
}

4、数据库引擎不支持事务

  • 原因 :如 MySQL 的 MyISAM 引擎不支持事务(仅 InnoDB 支持)。
  • 解决:确认使用支持事务的引擎(比如 InnoDB 引擎)。

5、事务传播行为配置不当

  • 原因 :若内层方法使用 Propagation.NOT_SUPPORTEDPropagation.NEVER 等传播行为,会挂起外层事务。
  • 解决 :根据业务需求调整传播行为(如 Propagation.REQUIRED)。
java 复制代码
// ❌ 内层方法挂起事务
@Transactional(propagation = Propagation.NEVER)
public void innerMethod() { ... }

6、多线程调用( 异步调用

  • 原因 :新线程内执行数据库操作不归属同一事务(事务绑定到 ThreadLocal)。异步方法(如使用 @Async 注解的方法)不会参与当前事务。即使该方法被 @Transactional 注解标记,事务也不会生效。
  • 解决:避免跨线程操作,或手动传递事务上下文(比较复杂)。

7、非 Spring 管理的 Bean

  • 原因 :通过 new 创建的对象不受 Spring 代理管理。
  • 解决 :确保对象由 Spring 容器创建(如 @Component)。

8、方法 final 或 static

  • 原因 :CGLIB 无法代理 final/static 方法(JDK 代理无法代理 static)。
  • 解决 :避免修饰事务方法为 finalstatic

9、未启用事务管理

  • 原因 :忘记添加 @EnableTransactionManagement(Spring Boot 中自动配置)。
  • 解决:检查配置类是否启用事务管理。

10、非事务性方法调用

  • 原因 :如果在事务方法内部调用了另一个没有事务管理的方法,事务将不会传播到被调用的方法中。事务只会在当前方法的调用栈中生效。
  • 解决:确保所有需要事务的操作都在事务方法中。

这些都是 @Transactional 可能失效的常见原因。如果遇到事务失效的问题,可以逐一排查这些情况。在工作中,有时出现了数据不一致的情况,去排查才发现是事务失效了。

知是行之始,行是知之成。-- 烟沙九洲

相关推荐
float_六七3 小时前
IntelliJ IDEA双击Ctrl的妙用
java·ide·intellij-idea
能摆一天是一天4 小时前
JAVA stream().flatMap()
java·windows
颜如玉5 小时前
🤲🏻🤲🏻🤲🏻临时重定向一定要能重定向🤲🏻🤲🏻🤲🏻
java·http·源码
程序员的世界你不懂7 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
星空寻流年7 小时前
设计模式第一章(建造者模式)
java·设计模式·建造者模式
gb42152877 小时前
java中将租户ID包装为JSQLParser的StringValue表达式对象,JSQLParser指的是?
java·开发语言·python
曾经的三心草8 小时前
Python2-工具安装使用-anaconda-jupyter-PyCharm-Matplotlib
android·java·服务器
Metaphor6928 小时前
Java 高效处理 Word 文档:查找并替换文本的全面指南
java·经验分享·word
ChinaRainbowSea8 小时前
7. LangChain4j + 记忆缓存详细说明
java·数据库·redis·后端·缓存·langchain·ai编程
stormsha8 小时前
飞算JavaAI炫技赛电商系统商品管理模块的架构设计与实现
java·架构·鸿蒙系统