事务的传播属性(有坑点)自调用失效学习笔记

本文介绍了事务的七种传播属性,重点讨论了PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW和PROPAGATION_NESTED的常见应用场景。提到了一个坑点,即在Spring中,服务类内部方法的自调用会导致事务失效,原因是AOP的动态代理机制。为了解决这个问题,可以通过Spring容器获取服务的代理对象进行调用来启用事务。建议阅读《深入浅出Spring Boot 2.x》了解更多详情。

事务传播性:

事务的传播属性一共有七种,如下。

1、PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

2、PROPAGATION_SUPPORTS:自身不会开启事务,在事务范围内则使用相同事务,否则不使用事务。‘

3、PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。

4、PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。

5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

而在实际的应用总,只有PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED这三种是常用的。而第二三种很类似,PROPAGATION_REQUIRES_NEW是无论如何,子方法都会创建一个事务来管理。而PROPAGATION_NESTED则是应用了数据库的保存点技术,和调用它的方法共用同一个事务管理器。

这里有一个坑点,外部经过spring容器调用service的方法事务才生效,service类内部方法间相互调用事务不生效,也就是传说中的自调用失效问题。主要原因是 Spring数据库事务的约定,其实现原理是AOP,而AOP的原理是动态代理,在自调用的过程中,是类自身的调用,而不是代理对象去调用,那么就不会产生AOP,这样 Spring就不能把你的代码织入到约定的流程中,于是就产生了现在看到的失败场景。为了克服这个问题,我们可以用一个 Service去调用另一个 Service,这样就是代理对象的调用, Spring才会将你的代码织入事务流程。当然也可以从 Spring loc容器中获取代理对象去启用AOP。下面我粘了一段代码,通过这种方式,再ioc容器中获取代理对象就可以解决事务传播失效的问题。(具体对于异常是否要捕获,是否要继续抛出,则完全看你业务的需求。)

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author alin
 * @since 2018-12-29
 */
public interface ICityService extends IService<City> {

    public void insertAlin01(City city) throws Exception;
    public void insertAlin02() throws Exception;
	
}
@Service
public class CityServiceImpl extends ServiceImpl<CityMapper, City> implements ICityService {

    @Autowired
    private CityMapper cityMapper;
    @Autowired
    private IUserService userService;
    @Autowired
    private ICityService cityService;

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED)
    public void insertAlin01(City city) throws Exception {
        cityMapper.insert(city);
        User user = new User();
        user.setName("new");
        user.setGender(1);
        try {
            cityService.insertAlin02();
        } catch (Exception e) {

        }
    }

    @Override
    @Transactional(isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRES_NEW)
    public void insertAlin02() throws Exception {
        City city = new City();
        city.setName("method2");
        city.setAge(2);
        city.setState(DateUtil.getStringDate());
        cityMapper.insert(city);
    }

}

这里只是粗略写了点个人想法,想深入了解一点建议看《深入浅出Spring Boot 2.x》第六章6.4节传播行为的相关讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值