事务@Transactional三种失效场景

本文详细介绍了Spring中@Transactional注解可能导致事务失效的六大场景,包括非public方法、内部方法调用、异常处理不当等,并提供了相应的解决策略。了解这些常见问题有助于避免事务管理失效,确保Spring应用的事务正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring中事务@Transactional失效场景(6大种)

一、@Transactional注解方法为非public

@Service
public class TestServiceImpl implements TestService {
    @Resource
    TestMapper mapper;
    
    @Transactional
    void insertTest() {
        mapper.insert(test1);
        mapper.insert(test2);
    }
}

原因:@Transactional是基于动态代理实现的,在bean初始化过程中,Spring扫描@Transactional注解信息,对含有@Transactional标注的bean实例创建代理对象。
Spring加载时拦截bean的创建,创建一个aop切点BeanFactoryTransactionAttributeSourceAdvisor,遍历当前bean的class对象方法,判断方法上面的注解信息是否包含@Transactional,如果bean任何一个方法包含@Transactional注解,那么就是适配这个切点,就需要创建代理对象,然后代理逻辑为我们管理事务开闭逻辑。
类所有方法上的修饰符都是非public时,将不会创建代理对象。public修饰符的类经过cglib创建代理对象。
类中多个方法,其中一个为public修饰方法,将会创建代理对象,但非public修饰的方法仍然不会开启事务。原因是在动态代理对象进行代理逻辑调用时,会获取当前被代理对象的需要执行的method适配的aop逻辑,非public方法不适配BeanFactoryTransactionAttributeSourceAdvisor,就不执行代理@Transactional对应的代理逻辑,直接执行方法,就无法开启事务。

AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
AbstractFallbackTransactionAttributeSource#getTransactionAttribute
AbstractFallbackTransactionAttributeSource#computeTransactionAttribute

二、类内部调用当前类内部@Transactional标注的方法

@Service
public class TestServiceImpl implements TestService {
    @Resource
    TestMapper mapper;
    
    @Transactional
    public void insertTest() {
        mapper.insert(test1);
        mapper.insert(test2);
    }

    public void test(){
        insertTest(); //类内部调用@Transactional标注的方法
    }
}

原因:事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过代理对象,肯定就是没有代理逻辑了。

解决:在类的内部注入当前类自己,通过注入的类对象调用类内其他@Transactional方法,这样就是使用了代理对象进行事务调用,所以能够开启事务管理。实际操作中不会这样去做。

三、@Transactional标注的方法内部捕获了异常

@Service
public class TestServiceImpl implements TestService {
    @Resource
    TestMapper mapper;
    
    @Transactional
    public void insertTest() {
        try {
            mapper.insert(test1);
            mapper.insert(test2);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

  原因:代理逻辑管理事务,是在开启事务后,通过反射调用业务方法,发生异常时try-catch捕获,在catch逻辑中回滚事务。如果业务方法内部捕获了异常,代理逻辑中就捕获不到异常,事务就不会回滚了。

  TransactionAspectSupport#invokeWithinTransaction

  针对这种情况,在catch中添加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

@Service
public class TestServiceImpl implements TestService {
    @Resource
    TestMapper mapper;
    
    @Transactional
    public void insertTest() {
        try {
            mapper.insert(test1);
            mapper.insert(test2);
        } catch (Exception e) {
            System.out.println(e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值