Spring事务传播行为-实践向

前言

Spring事务传播行为定义了多个事务方法相互调用时,事务应该如何传播的规则。它是Spring事务管理的核心概念之一,决定了事务的边界范围。Spring事务传播行为总共有七种,如果没有理解的话很容易忘记,本文希望以实践验证的方式带大家了解常用的传播行为。

初始数据状态

下文会进行一系列验证,分别修改员工和资产表,此处记录初始状态,每轮测试如果成功修改了,下轮测试会手动重置成如下状态:
t_emp
t_emp
t_asset_info
t_asset_info

1、REQUIRED传播行为

REQUIRED:必须的,会将多个嵌套的事务合并成一个事务管理,能够保证多个事务同时成功或同时失败,为最常用的传播行为,适用于90%以上的场景。

1-1、演示使用REQUIRED传播行为,在外部事务中抛出异常

先更新员工信息,然后更新资产信息,在员工信息更新commit之前抛出自定义异常。

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        assetInfoService.updateAssetInfo();
        int i = 1 / 0; // 故意抛出异常
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
    }
}

测试后查看数据,两个事务都回滚了

t_emp
t_asset_info

1-2、演示使用REQUIRED传播行为,在内部事务中抛出异常

先更新员工信息,然后更新资产信息,在更新资产信息commit之前抛出自定义异常。

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        assetInfoService.updateAssetInfo();
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
        int i = 1 / 0; // 故意抛出异常
    }
}

测试后查看数据,两个事务都回滚了

t_emp
t_asset_info

2、REQUIRES_NEW传播行为

REQUIRES_NEW:需要新的 REQUIRES_NEW会从数据库拿一个独立的连接构成一个独立的事务,能够保证内外事务的独立性

2-1、演示使用REQUIRES_NEW传播行为,在外部事务中抛出异常

先更新员工信息,然后更新资产信息,在员工信息更新commit之前抛出自定义异常。
外部事务为REQUIRED传播行为,内部事务为REQUIRES_NEW传播行为

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        assetInfoService.updateAssetInfo();
        int i = 1 / 0; // 故意抛出异常
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
    }
}

测试后查看数据,外部事务回滚了,内部事务提交成功

t_emp
t_asset_info

2-2、演示使用REQUIRES_NEW传播行为,在内部事务中抛出异常【未手动捕获】

先更新员工信息,然后更新资产信息,在资产信息更新commit之前抛出自定义异常。
内部与外部事务均设置为REQUIRES_NEW传播行为

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        assetInfoService.updateAssetInfo();
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
        int i = 1 / 0; // 故意抛出异常
    }
}

测试后查看数据,两个事务都回滚了

t_emp
t_asset_info

2-3、演示使用REQUIRES_NEW传播行为,在内部事务中抛出异常【手动捕获】

先更新员工信息,然后更新资产信息,在资产信息更新commit之前抛出自定义异常。
内部与外部事务均设置为REQUIRES_NEW传播行为
手动捕获内部异常

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        try {
            assetInfoService.updateAssetInfo();
        } catch (Exception e) {
            log.error("updateAssetInfo error", e);
        }
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
        int i = 1 / 0; // 故意抛出异常
    }
}

测试后查看数据,外部事务正常提交,内部事务回滚了

t_emp
t_asset_info

3、NESTED传播行为

嵌套事务

3-1、演示使用NESTED传播行为,在内部事务中抛出异常【未手动捕获】

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        assetInfoService.updateAssetInfo();
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
        int i = 1 / 0; // 故意抛出异常
    }
}

测试后查看数据,两个事务都回滚了

t_emp
t_asset_info

2-2、演示使用NESTED传播行为,在内部事务中抛出异常【手动捕获】

注意事项:需要手动捕获内部事务异常

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements IEmpService {

    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private AssetInfoServiceImpl assetInfoService;
    @Override
    @Transactional
    public void updateEmpInfo() throws IOException {
        empMapper.update(new UpdateWrapper<>(new Emp())
                .set("emp_salary", 114.58)
                .set("emp_name", "一个名字")
                .eq("emp_id", 111));
        try {
            assetInfoService.updateAssetInfo();
        } catch (Exception e) {
            log.error("updateAssetInfo error", e);
        }
    }
}

@Service
public class AssetInfoServiceImpl extends ServiceImpl<AssetInfoMapper, AssetInfoDTO> implements IAssetInfoService {
    @Autowired
    private AssetInfoMapper assetInfoMapper;
    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void updateAssetInfo() throws IOException {
        assetInfoMapper.update(new UpdateWrapper<>(new AssetInfoDTO())
                .set("asset_status", 99)
                .eq("asset_number", "5277dd895001bbbdb444f174db644d75"));
        int i = 1 / 0; // 故意抛出异常
    }
}

测试后查看数据,外部事务正常提交,内部事务回滚了

t_emp
t_asset_info

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

松树戈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值