为什么@Transactional不生效?Spring事务失效的排查手册

本文发布于 2025年07月02日,阅读 4 次,点赞 0 次,归类于 后端技术
为什么@Transactional不生效?Spring事务失效的排查手册

本文系统解析Spring中@Transactional事务失效的13类常见原因(如代理机制限制、异常处理不当、配置缺陷等),并提供针对性解决方案,助力开发者高效避坑。


博客:https://www.emanjusaka.com
博客园:https://www.cnblogs.com/emanjusaka
公众号:emanjusaka的编程栈

by emanjusaka from https://www.emanjusaka.com/archives/spring-transactional-invalid 彼岸花开可奈何
本文为原创文章,可能会更新知识点以及修正文中的一些错误,全文转载请保留原文地址,避免产生因未即时修正导致的误导。

37-1

在 Java 中,事务管理通常使用 Spring 框架提供的事务管理功能。事务不生效可能由多种原因导致。

下面介绍一些常见的情况:

  • 方法访问权限问题
  • 自调用问题
  • 异常处理不当
  • 事务传播设置不当
  • 没有启用事务管理
  • 数据库引擎不支持事务
  • 在非 Spring 管理的类中使用事务注解
  • 事务方法被 final 或 static 修饰
  • 使用了错误的注解
  • 事务超时或只读设置不当
  • 多个事务管理器的情况下未指定事务管理器
  • 嵌套事务传播行为设置不当
  • 在异步方法中事务上下文丢失

方法访问权限问题

Spring 默认使用 AOP 代理,对于非 public 方法,事务可能不会生效(因为 Spring 的事务拦截器只能拦截 public 方法)。除非使用 AspectJ 模式。

@Transactional
private void updateData() { // 非 public 方法,事务失效!
    // ...
}

自调用问题

在同一个类中,一个非事务方法调用另一个事务方法,事务不会生效。这是因为事务是通过代理对象实现的,自调用不会经过代理对象。

public void outerMethod() {
    innerMethod(); // 自调用,事务不生效!
}

@Transactional
public void innerMethod() {
    // 数据库操作
}

异常处理不当

如果在事务方法中捕获了异常,而没有重新抛出,则事务不会回滚。因为事务管理器只有检测到未处理的异常时才会回滚。

  • 捕获异常未重新抛出

    @Transactional
    public void update() {
        try {
            // 数据库操作(抛出异常)
        } catch (Exception e) {
            // 捕获后未抛出,事务不回滚!
        }
    }
    
  • 默认只对 RuntimeExceptionError 回滚,检查异常不回滚。

    @Transactional
    public void update() throws IOException { // 检查异常
        // ...
        throw new IOException(); // 事务不回滚!
    }
    

    一般会明确指定回滚异常类型

    @Transactional(rollbackFor = Exception.class)
    

事务传播设置不当

如果内部方法的事务传播行为设置为Propagation.NOT_SUPPORTED,则它会以非事务方式执行。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void noTransactionMethod() {
    // 此方法无事务
}

没有启用事务管理

在配置类中没有使用@EnableTransactionManagement注解(对于Java配置)或没有配置事务管理器(如PlatformTransactionManager)。

  1. Spring Boot场景:

若使用Spring Boot + 数据源(如⁠DataSourceTransactionManager),默认会自动配置事务管理器,无需显式声明⁠PlatformTransactionManager Bean。


  1. 传统Spring配置:
    • 使用⁠@Transactional时需至少有一个⁠PlatformTransactionManager Bean存在(如⁠DataSourceTransactionManager、⁠JpaTransactionManager等)。

    • 可通过⁠@EnableTransactionManagement激活事务管理,Spring会自动选择容器中的唯一事务管理器。

  2. 多数据源场景:
    需显式定义多个⁠PlatformTransactionManager Bean,并通过⁠@Transactional(transactionManager="名称")指定。

数据库引擎不支持事务

例如,MySQL的MyISAM引擎不支持事务,而InnoDB支持。确保数据库表使用的是支持事务的引擎。

在非Spring管理的类中使用事务注解

只有Spring容器管理的Bean上的事务注解才会被处理。

事务方法被final或static修饰

因为Spring事务使用动态代理(JDK动态代理或CGLIB),对于final或static方法,代理无法重写这些方法,因此事务不会生效。

@Transactional
public final void finalMethod() { // 事务失效!
    // ...
}

使用了错误的注解

错误地使用了JPA的@Transactional而不是Spring的@Transactional(尽管通常可以混用,但要注意包名:javax.transaction.Transactionalorg.springframework.transaction.annotation.Transactional)。

事务超时或只读设置不当

虽然不会导致事务完全不生效,但可能导致不符合预期的行为。

多个事务管理器的情况下未指定事务管理器

如果配置了多个事务管理器,需要在@Transactional注解中指定使用哪个事务管理器(通过transactionManager属性)。

@Transactional(transactionManager = "otherTxManager")

嵌套事务传播行为设置不当

在嵌套事务中,内部事务设置了REQUIRES_NEW,但外部事务捕获了内部事务的异常,导致内部事务回滚而外部事务提交。

@Transactional
public void outer() {
    inner(); // 内层事务独立提交
    // 即使 inner() 失败,outer() 仍可能提交
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {
    // 操作数据库
}

在异步方法中事务上下文丢失

在异步方法中,事务上下文可能不会传递,需要额外的配置。

@Transactional
public void asyncUpdate() {
    new Thread(() -> {
        // 此处无事务控制!
    }).start();
}

可以使用 Spring 的 @Async 代替手动线程。

本篇完
下一篇: 【微码】Java 基于 Redis 实现登录频率限制