Spring 事务失效的 8 大场景 + 原因 + 解决方案

1. 同类内部方法调用(最常见)

场景

java 复制代码
@Service
public class UserService {

    // 没有 @Transactional
    public void saveUser() {
        // 同类内部直接调用 → 不经过代理 → 事务失效
        doSave();
    }

    @Transactional
    public void doSave() {
        // 增删改操作
    }
}

原因

  • Spring 事务基于 AOP 动态代理
  • 同类内部 this.方法() 调用,不走代理对象 → 切面不生效

解决方案

  1. 注入自身 Bean

    java 复制代码
    @Autowired
    private UserService userService;
    
    public void saveUser() {
        userService.doSave(); // 走代理
    }
  2. 使用 AopContext.currentProxy()

  3. 把方法拆到不同类


2. 方法不是 public(private/protected/default)

场景

java 复制代码
@Transactional
private void update() {
    // 事务不生效
}

原因

  • Spring AOP 代理只支持 public 方法
  • 非 public 方法会被直接跳过事务切面

解决方案

  • 改成 public

3. @Transactional 加在了接口上,类没被 Spring 管理

场景

java 复制代码
public interface UserService {
    @Transactional
    void add();
}

// 没加 @Service、@Component
public class UserServiceImpl implements UserService {}

原因

  • 没被 Spring 管理 → 不生成代理 → 事务无效

解决方案

  • 类上加 @Service

4. 异常被 try-catch 吃掉,没抛出去

场景

java 复制代码
@Transactional
public void save() {
    try {
        // 数据库操作
    } catch (Exception e) {
        // 只打印日志,不抛异常
        log.error("...");
    }
}

原因

  • Spring 默认只对 RuntimeException & Error 回滚
  • 异常被捕获 → 事务感知不到 → 不会回滚

解决方案

  1. catch 后重新抛出

    java 复制代码
    catch (Exception e) {
        throw new RuntimeException(e);
    }
  2. 注解指定回滚异常

    java 复制代码
    @Transactional(rollbackFor = Exception.class)

5. 抛出了非 RuntimeException(受检异常)

场景

java 复制代码
@Transactional
public void test() throws Exception {
    // 抛出 Exception / IOException 等
}

原因

  • 默认只回滚 RuntimeExceptionError
  • Exception 这种受检异常 不触发回滚

解决方案

java 复制代码
@Transactional(rollbackFor = Exception.class)

6. 多线程调用,事务不共享

场景

java 复制代码
@Transactional
public void save() {
    new Thread(() -> {
        // 子线程里的数据库操作
    }).start();
}

原因

  • Spring 事务基于 ThreadLocal
  • 子线程和主线程不是同一个线程 → 独立连接,独立事务

解决方案

  • 子线程方法自己加 @Transactional
  • 不用异步,或用 CountDownLatch 同步控制

7. 使用了错误的传播机制(NOT_SUPPORTED/NEVER)

场景

java 复制代码
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void update() {
    // 以非事务方式执行
}

原因

  • NOT_SUPPORTED:挂起事务,非事务执行
  • NEVER:强制非事务,有事务直接抛异常

解决方案

  • 使用默认 REQUIRED

8. 数据库引擎不支持事务(MyISAM)

场景

建表时用了 MyISAM

sql 复制代码
CREATE TABLE t(...) ENGINE=MyISAM;

原因

  • MyISAM 不支持事务
  • 代码怎么写都不会回滚

解决方案

  • 改成 InnoDB

一张表速记

场景 失效原因 一句话解决
同类内部调用 不走代理 注入自身调用
非 public AOP不代理 改成public
异常被catch 没抛出去 重新抛出
抛受检异常 默认不回滚 rollbackFor=Exception
多线程 ThreadLocal不共享 子线程独立事务
传播机制错 禁用事务 用REQUIRED
没被Spring管理 无代理 加@Service
MyISAM 引擎不支持 换InnoDB
相关推荐
我是若尘2 小时前
我的需求代码被主干 revert 了,接下来我该怎么操作?
前端·后端·代码规范
dweizhao2 小时前
这份AI报告,把美股干崩了
后端
野生技术架构师2 小时前
1000道互联网大厂Java岗面试原题解析(八股原理+场景题)
java·开发语言·面试
jiankeljx3 小时前
Java实战:Spring Boot application.yml配置文件详解
java·网络·spring boot
cyforkk3 小时前
Java 开源项目指南:如何规范地发布首个 GitHub Release
java·开源·github
qqty12173 小时前
Java进阶学习之路
java·开发语言·学习
是苏浙3 小时前
初识集合框架
java·数据结构
Zzxy3 小时前
Spring Boot 参数校验
java·spring boot
黑眼圈子3 小时前
总结一下用Java做算法的常用类和方法
java·开发语言·算法