同一个 Bean 内部的方法中,方法之间调用的事务控制未生效,排查原因及解决方案

先上错误示例:

@Service
public class MyService {


    public void mainMethod() {
        // 主方法逻辑
        doMainBusiness();
        
        try {
            doSubBusiness();
            return true;
        } catch (Exception e) {
            log.error("子事务失败", e);
            return false;
        }
        
        // 主方法继续执行
    }

    private void doMainBusiness() {
        // 主方法的数据库操作
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void doSubBusiness() {
        // 子方法的数据库操作
    }
}

代码中,子方法 doSubBusiness 的数据库操作不会回滚。这是因为 Spring AOP 的代理机制无法拦截同一个 Bean 内的私有方法调用,导致 @Transactional 注解失效。

详细原因分析

  1. Spring AOP 代理机制
    Spring 的事务管理基于 AOP 代理,而 同一个 Bean 内部的方法调用不会触发代理。当 mainMethod() 调用 doSubBusiness() 时,实际上是通过 this 引用调用,绕过了 Spring 的事务代理。因此:

    • @Transactional 注解虽然在 public 方法上有效,但在同一 Bean 内部调用时不会生效。
    • 子方法的数据库操作会在主事务(如果存在)中执行,或默认不使用事务。
  2. 事务传播行为失效
    由于代理未生效,Propagation.REQUIRES_NEW 无法创建独立事务。若主方法没有事务,子方法的操作会直接提交到数据库;若主方法有事务,子方法会加入该事务

解决方案

方案 1:将子方法提取到独立 Service
@Service
public class MainService {
    @Autowired
    private SubService subService;

    public void mainMethod() {
        try {
            subService.doSubBusiness(); // 通过代理调用,事务生效
        } catch (Exception e) {
            log.error("子方法失败", e);
        }
    }
}

@Service
public class SubService {
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void doSubBusiness() {
        // 数据库操作
    }
}
方案 2:通过 ApplicationContext 获取代理对象
@Service
public class MyService {
    @Autowired
    private ApplicationContext context;
    private MyService selfProxy;

    @PostConstruct
    public void init() {
        selfProxy = context.getBean(MyService.class);
    }

    public void mainMethod() {
        try {
            selfProxy.doSubBusiness(); // 通过代理调用,事务生效
        } catch (Exception e) {
            log.error("子方法失败", e);
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void doSubBusiness() {
        // 数据库操作
    }
}
方案 3:使用 TransactionTemplate 手动管理事务
@Service
public class MyService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    public void mainMethod() {
        Boolean result = transactionTemplate.execute(status -> {
            try {
                doSubBusiness();
                return true;
            } catch (Exception e) {
                status.setRollbackOnly(); // 手动回滚
                log.error("子事务失败", e);
                return false;
            }
        });
    }

    private void doSubBusiness() {
        // 数据库操作
    }
}

总结

代码中,由于 Spring AOP 无法拦截同一 Bean 内的方法调用@Transactional 注解失效,子方法的数据库操作不会回滚。建议采用方案 1 或方案 3 解决此问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值