必看!导致事务失效的7大典型场景!

@Transactional 声明式事务失效的场景是 Java 面试中经常被问到的问题,所以今天咱们就来系统的盘点一下导致 @Transactional 失效的场景有哪些?以及导致的原因和解决方案详解。

1.方法访问修饰符不是public

以下代码会导致 @Transactional 失效:

java 复制代码
@Service
public class OrderService {
    @Transactional
    private void createOrder() { // private方法,事务不生效
        // ...
    }
}

原因分析

Spring AOP 代理在生成代理类时,只对 public 方法生成事务代理,这是 Java 源码层面设计原因,设计源码如下:

java 复制代码
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
   // Don't allow no-public methods as required.
   // 非 public 方法,设置为 null
   if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
      return null;
   }
   // 后面代码省略....
 }

解决方案

将方法改为 public。

2.自调用问题

以下代码会导致 @Transactional 失效:

java 复制代码
@Service
public class OrderService {
    public void placeOrder() {
        createOrder(); // 直接调用,绕过代理
    }

    @Transactional
    public void createOrder() {
        // 事务操作
    }
}

原因分析

当一个类中的非事务方法调用本类的事务方法时,调用是通过 this 直接调用,而不是通过 Spring 代理对象调用,因此事务不能生效。

解决方案

  • 将事务方法移到另一个 Service 类中
  • 使用 ApplicationContext 获取当前 Bean 的代理对象,如下代码所示:
java 复制代码
@Autowired
private ApplicationContext context;

public void placeOrder() {
    OrderService proxy = context.getBean(OrderService.class);
    proxy.createOrder(); // 通过代理调用
}

3.异常被捕获且未重新抛出

以下代码会导致 @Transactional 失效:

java 复制代码
@Transactional
public void transferMoney() {
    deductMoney();
    try {
        addMoney();
    } catch (Exception e) {
        log.error("异常", e);
        // 捕获但未抛出,事务不会回滚
    }
}

原因分析

Spring 事务默认只在抛出未被捕获的 RuntimeException 或 Error 时回滚。如果异常被捕获且未抛出,代理认为方法执行成功,会提交事务。

解决方案

  • 手动设置回滚:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  • 或重新抛出异常
java 复制代码
@Transactional
public void transferMoney() {
    deductMoney();
    try {
        addMoney();
    } catch (Exception e) {
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        throw e; // 或不抛出,仅setRollbackOnly
    }
}

4.事务传播机制配置错误

例如:使用了 Propagation.NOT_SUPPORTED 或 Propagation.NEVER,会导致事务被挂起或拒绝。

特别注意 Propagation.REQUIRES_NEW:会挂起当前事务,开启新事务,需谨慎使用,避免嵌套事务导致性能问题或死锁。

5.数据库引擎不支持事务

例如:MySQL 的 MyISAM 引擎不支持事务,必须使用 InnoDB。

即使代码配置了@Transactional,底层数据库不支持也无法实现事务。

6.方法被final或static修饰

CGLIB 无法代理 final 方法(不能被重写),JDK 代理也无法处理 static 方法,都会导致事务无法生效。因为 Spring/Spring Boot 是使用 CGLIB 或 JDK 代理实现的。

7.多线程中调用事务方法

以下代码会导致 @Transactional 失效:

java 复制代码
@Transactional
public void process() {
    new Thread(() -> {
        dao.update(); // 在子线程中,无事务
    }).start();
}

原因分析

事务是基于线程绑定的(通过 ThreadLocal 存储事务上下文),子线程中调用事务方法时,无法继承父线程的事务上下文。

解决方案

使用事务同步或手动管理事务。

小结

@Transactional 声明式事务底层是通过 CGLIB 或 JDK 代理实现的,所以事务失效的场景多半与二者相关,本文总共介绍了 7 种导致事务失效的场景,您至少要记住其中 4 种以上事务失效场景,这样才能在面试中崭露头角。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:场景题、SpringAI、SpringAIAlibaba、并发编程、MySQL、Redis、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、JVM、设计模式、消息队列、Dify、Coze、AI常见面试题等。

相关推荐
血小溅8 分钟前
Spring AI 对 Skill/MCP 的支持全景整理
后端
程序员黑豆18 分钟前
Java中的字符串【AI全栈开发】
java
晓杰'19 分钟前
从0到1实现Balatro游戏后端(8):Skip Blind与Tag奖励机制设计与实现
后端·websocket·typescript·项目实战·nestjs·状态管理·游戏服务器
叫我:松哥23 分钟前
基于Flask框架的校园二手书籍交易平台,注重校园场景的特殊需求,通过学号认证保障用户真实性
后端·python·sqlite·flask·bootstrap
namexingyun35 分钟前
开源前端生态如何成为 AI UI 生成的“燃料“:shadcn/ui、Tailwind CSS、Storybook 技术价值全解剖
java·前端·人工智能·python·ui·开源·ai编程
终将老去的穷苦程序员1 小时前
基于SpringBoot的餐饮管理系统
java·spring boot·后端
心之伊始1 小时前
Spring AI Tool Calling 实战:让 Java Agent 调用本地 Bean 工具方法
java·spring boot·agent·spring ai·tool calling
张忠琳1 小时前
【Go 1.26.4】Golang Map 深度解析
开发语言·后端·golang
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第110题】【并发篇】第10题:CAS 存在哪些问题?
java·开发语言·面试
秋91 小时前
Python工程师面试常问提问和回答(AI工程化方向 · 2026版)
人工智能·python·面试