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
相关推荐
陈明勇3 分钟前
Go 1.26 新特性回顾:语言增强、工具升级与 Green Tea GC 默认启用
后端·go
咖啡八杯10 小时前
GoF设计模式——策略模式
java·后端·spring·设计模式
lizhongxuan11 小时前
AI Agent 上下文压缩利器 Headroom
后端
Csvn13 小时前
SSH 远程管理与安全加固 — 运维的守门之道
后端
IT_陈寒13 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
菜鸟谢15 小时前
Rust 智能指针完整详解
后端
菜鸟谢15 小时前
Rust 函数完整知识点详解
后端
爱勇宝15 小时前
淡泊名利之前,先承认我们都很焦虑
前端·后端·程序员
菜鸟谢15 小时前
Rust 闭包(Closure)完整详解
后端
ServBay15 小时前
如何利用本地技术栈构建 0 成本 AI SaaS 雏形
后端·aigc·ai编程