spring 事务传播机制

 

spring事务有七中传播级别,分别是:

1、PROPERGATION_MANDATORY: 方法必须运行在一个事务中,不存在事务则抛出异常

2、PROPERGATION_NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。

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

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

5、PROPERGATION_REQUIRED: spring默认的传播级别,如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。

6、PROPERGATION_REQUIRES_NEW:  新建一个自己的事务,不论当前是否存在事务

7、PROPERGATION_SUPPORT: 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。

 

接下来我们就验证下各个事务传播级别的影响

首先测试下PROPERGATION_REQUIRES_NEW事务的影响,代码如下:

 

首先写一个接口,其中主方法事务级别为默认PROPERGATION_REQUIRED,然后在写两个方法,传播级别分别设置为PROPERGATION_REQUIRED,和PROPERGATION_REQUIRES_NEW,外部抛出异常,看看事务会不会回滚

public interface TransactionalService {

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    void transaction();
}
@Service
public class TransactionalServiceImpl implements TransactionalService {

    Logger logger = LoggerFactory.getLogger(TransactionalServiceImpl.class);

    @Autowired
    private EntityOneService entityOneService;

    @Override
    public void transaction() {
        logger.info("====================== 开始运行transaction方法 ======");
        entityOneService.add1();
        entityOneService.add2();
        throw new RuntimeException("RuntimeException");
    }
}

 

public interface EntityOneService {

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    void add1();

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    void add2();
}
@Service
public class EntityOneServiceImpl implements EntityOneService {

    @Autowired
    private EntityOneDao entityOneDao;

    @Override
    public void add1() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr1");
        entityOne.setAttr2("attr2");
        entityOneDao.save(entityOne);
    }

    @Override
    public void add2() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr7");
        entityOne.setAttr2("attr8");
        entityOneDao.save(entityOne);
    }
}

 

执行代码后发现,add2方法中向数据库添加数据成功,add1添加失败,由此看来外部事务回滚不会造成PROPERGATION_REQUIRES_NEW回滚,PROPERGATION_REQUIRES_NEW事务的数据一样可以存储。

现在修改代码把add2中抛出异常,外层不抛出异常:

@Service
public class TransactionalServiceImpl implements TransactionalService {

    Logger logger = LoggerFactory.getLogger(TransactionalServiceImpl.class);

    @Autowired
    private EntityOneService entityOneService;

    @Override
    public void transaction() {
        logger.info("====================== 开始运行transaction方法 ======");
        entityOneService.add1();
        entityOneService.add2();
    }
}
@Service
public class EntityOneServiceImpl implements EntityOneService {

    @Autowired
    private EntityOneDao entityOneDao;

    @Override
    public void add1() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr1");
        entityOne.setAttr2("attr2");
        entityOneDao.save(entityOne);
    }

    @Override
    public void add2() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr7");
        entityOne.setAttr2("attr8");
        entityOneDao.save(entityOne);
        throw new RuntimeException("抛出异常");
    }
}

运行结果发现,内部事务PROPERGATION_REQUIRES_NEW抛出异常,外部事务也同样回滚,add1和add2方法都执行失败。

如果修改代码在主方法中捕获异常则可以add1执行成功。

@Service
public class TransactionalServiceImpl implements TransactionalService {

    Logger logger = LoggerFactory.getLogger(TransactionalServiceImpl.class);

    @Autowired
    private EntityOneService entityOneService;

    @Override
    public void transaction() {
        logger.info("====================== 开始运行transaction方法 ======");
        entityOneService.add1();
        try{
            entityOneService.add2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

但是如果把add2中的隔离级别改成PROPERGATION_REQUIRED,则两个方法都是执行失败,因为是在同一个事务中,即使被捕获了异常,同样是事务回滚,所以同一个事务的其他方法,也同样回滚。

 

由此得出结论,外层PROPERGATION_REQUIRED事务,其调用的方法如果也用的是此传播机制,则加如此事务,如果有一个异常,所有都回滚。如果内部方法隔离级别是PROPERGATION_REQUIRES_NEW,外部事务回滚不会影响此事务,但是此事务异常如果没有被捕捉,会影响外部事务。

由于JPA不支持嵌套事务PROPERGATION_NESTED,可以使用jdbcTemplate测试,暂时就没做测试。

 

// 2020年7月29补充:

在新做了一个测试,在同一个类中,add1方法传播机制是 REQUIRED,add2方法的传播机制是 REQUIRES_NEW,

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void add1() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr1");
        entityOne.setAttr2("attr2");
        entityOneDao.save(entityOne);
        add2();
        throw new RuntimeException("ccccc");
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void add2() {
        EntityOne entityOne = new EntityOne();
        entityOne.setId(UUID.randomUUID().toString().replaceAll("-",""));
        entityOne.setAttr1("attr7");
        entityOne.setAttr2("attr8");
        entityOneDao.save(entityOne);
    }

但是在调用add1方法后,按照分析,add2是新的事务,和add1的事务不同,所以add1报错,add1应该会回滚,add2会成功。但是结果却显示add1和add2都失败了。

最后查资料才明白Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。所以内部方法调用不会被拦截,所以add2上就相当于加入到了add1的这个事务。所以出错之后两个都失败了,因为是同一个事务里。

总结:对于spring的事务,同类的调用一般只要写在最外层的方法就行了,这样内部方法就同样加入了这个事务。

对于不同事务之间的调用,最好分布在多个类之间,这样才能有效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值