事务 - 失效的场景

1. 没有使用代理

场景: 如果你在一个类内部调用同一个类中的另一个方法,Spring 事务管理无法生效。

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 当在 controller 调用 executeTask 时事务会失效
 * 1,因为标注了 @Transaction 注解,spring 会为 MyService 创建一个代理对象
 * 2,controller 调用 executeTask 方法时,调用的是真实对象的方法,因为这个方法没有标注事务注解
 * 3,executeTask 再调用 performTask 时,其实是 this.performTask,这里的 this 是普通对象,而不是代理对象
 */
@Service
public class MyService {

    @Transactional
    public void performTask() {
        // 模拟数据库操作
        System.out.println("Performing task...");
        // 这里可以添加更多数据库操作代码
        // 例如:userRepository.save(new User());
    }

    public void executeTask() {
        performTask();  // 直接调用同一类中的方法
    }
}

原因: 这是因为 Spring 事务管理是基于代理的。当你在一个类中调用另一个方法时,实际上是在同一个对象的上下文中执行的,这样事务注解不会被代理拦截,导致事务失效。

解决方法: 1,将方法提取到不同的类中,2,在同一个类中使用 self 注入。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MyService {

    @Autowired
    private MyService self;  // 自我注入

    @Transactional
    public void performTask() {
        System.out.println("Performing task...");
        // 数据库操作
    }

    public void executeTask() {
        self.performTask();  // 使用自我注入调用,因为此时 self 已经是代理对象了,所以会走 AOP 的通知
    }
}

2. 非运行时异常

场景: Spring 默认只对运行时异常(RuntimeException)进行回滚,如果抛出的是检查性异常(如 IOException),则不会回滚事务。

@Transactional
public void test(){
	userDao.save(user);
   	new File("D:\\不存在的文件.jpg")
}

原因: 这是 Spring 事务管理的默认行为,非运行时异常不会触发事务回滚。

解决方法: 可以在 @Transactional 注解中指定回滚的异常类型,例如:

@Transactional(rollbackFor = Exception.class)
public void test(){
	userDao.save(user);
   	new File("D:\\不存在的文件.jpg")
}

3. 事务传播行为设置不当

场景: 在多个事务之间的交互时,传播行为设置不当可能导致事务失效。

// 订单 service
@Service
public class OrderService {
    
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    @Transactional
    public void createOrder() {
        // 订单创建逻辑
        System.out.println("Creating order...");
        
        // 调用 PaymentService 处理支付
        paymentService.processPayment();
        
        // 其他订单处理逻辑
    }
}

// 支付 service
@Service
public class PaymentService {

    // 这个方法使用的是 REQUIRES_NEW,会新开一个事务,出现异常只是当前事务会回滚,不会影响上层的事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment() {
        // 支付处理逻辑
        System.out.println("Processing payment...");
        // 模拟异常
        if (true) {
            throw new RuntimeException("Payment failed!");
        }
    }
}

4. 隔离级别不合适

场景: 在高并发情况下,使用不合适的隔离级别可能导致事务表现不符合预期。

# 1,假设隔离级别是读未提交
# 2,A 事务先新增一条数据
# 3,此时 B 事务读到这条数据了,做业务的途中发生异常,要回滚数据(将要回滚还没有回滚的时候,A RollBack 了)
# 4,B 事务咋回滚,要回滚的数据都没了

5. Spring Boot 和 Spring 版本不兼容

场景: 在使用 Spring Boot 的时候,某些 Spring 版本之间可能存在兼容性问题。

原因: 在不同版本的 Spring 或 Spring Boot 中,事务管理的实现可能会有所不同。

解决方法: 确保使用兼容的 Spring 和 Spring Boot 版本,并查阅相应的文档。

posted @   CyrusHuang  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示