【Spring】事务传播机制使用各种场景演示

Spring 事务传播机制使用和各种场景演示

对于以上事务传播机制,我们重点关注以下两个就可以了:

  1. REQUIRED (默认值)
  2. REQUIRES_NEW

REQUIRED(加入事务)

看下面代码实现:

  1. 用户注册,插入一条数据
  2. 记录操作日志,插入一条数据(出现异常)
    观察 propagation = Propagation.REQUIRED 的执行结果
@RequestMapping("/propaga")  
@RestController  
public class PropagationController {  
    @Autowired  
    private UserService userService;  
  
    @Autowired  
    private LogService logService;  
  
    public String r3(String name, String password) {  
        // 用户注册  
        userService.registryUser(name, password);  
        // 记录操作日志  
        logService.insertLog(name, "用户注册");  
        return "r3";  
    }  
}

对应的 UserServiceLogService 都添加上 @Transactional(propagation = Propagation.REQUIRED)

@Slf4j  
@Service  
public class UserService {  
    @Autowired  
    private UserInfoMapper userInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRED)  
    public void registryUser(String name, String password) {  
        // 插入用户信息  
        userInfoMapper.insert(name, password);  
    }  
}
@Slf4j  
@Service  
public class LogService {  
    @Autowired  
    private LogInfoMapper logInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRED)  
    public void insertLog(String name, String op) {  
    	int a = 10 / 0;
        // 记录用户操作  
        logInfoMapper.insertLog(name, "用户注册");  
    }  
}

运行程序:image.png|390

但发现数据库没有插入任何数据image.png|474


流程描述:

  1. p1 方法开启事务
  2. 用户注册,插入一条数据(执行成功)(和 p1 使用同一个事务)
  3. 记录操作日志,插入一条数据(出现异常,执行失败)(和 p1 使用同一个事务)
  4. 因为步骤 3 出现一次,事务回滚。步骤 2 和 3 使用同一个事务,所以步骤 2 的数据也回滚了

REQUIRES_NEW(新建事务)

将上述 UserServiceLogService 中相关方法事务传播机制改为 Propagation.REQUIRES_NEW

@Slf4j  
@Service  
public class UserService {  
    @Autowired  
    private UserInfoMapper userInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void registryUser(String name, String password) {  
        // 插入用户信息  
        userInfoMapper.insert(name, password);  
    }  
}
@Slf4j  
@Service  
public class LogService {  
    @Autowired  
    private LogInfoMapper logInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRES_NEW)  
    public void insertLog(String name, String op) {  
    	int a = 10 / 0;
        // 记录用户操作  
        logInfoMapper.insertLog(name, "用户注册");  
    }  
}

运行程序,发现用户数据插入成功了,日志表数据插入失败image.png

  • LogService 方法中的事务不影响 UserService 中的事务
  • 当我们不希望事务之间相互影响时,可以使用该传播行为

NEVER(不支持当前事务,抛异常)

修改 UserService 中对应方法的事务传播机制为 Propagation.NEVER

@Slf4j  
@Service  
public class UserService {  
    @Autowired  
    private UserInfoMapper userInfoMapper;  
  
    @Transactional(propagation = Propagation.NEVER)  
    public void registryUser(String name, String password) {  
        // 插入用户信息  
        userInfoMapper.insert(name, password);  
    }  
}

程序执行报错,没有数据插入image.png

NESTED(嵌套事务)

将上述 UserServiceLogService 中相关方法事务传播机制改为 Propagation.NESTED

@Slf4j  
@Service  
public class UserService {  
    @Autowired  
    private UserInfoMapper userInfoMapper;  
  
    @Transactional(propagation = Propagation.NESTED)  
    public void registryUser(String name, String password) {  
        // 插入用户信息  
        userInfoMapper.insert(name, password);  
    }  
}
@Slf4j  
@Service  
public class LogService {  
    @Autowired  
    private LogInfoMapper logInfoMapper;  
  
    @Transactional(propagation = Propagation.NESTED)  
    public void insertLog(String name, String op) {  
    	int a = 10 / 0;
        // 记录用户操作  
        logInfoMapper.insertLog(name, "用户注册");  
    }  
}

运行程序,发现没有任何数据插入
流程描述:

  1. Controllerp1 方法开始事务
  2. UserService 用户注册,插入一条数据(嵌套 p1 事务)
  3. LogService 记录操作日志,插入一条数据(出现一次,执行失败)(嵌套 p1 事务,回滚当前事务,数据添加失败)
  4. 由于是嵌套事务,LogService 出现异常之后,往上找调用它的方法和事务,所以用户注册也失败了
  5. 最终结果是两个数据都没有添加
  • p1 事务可以认为是父事务,嵌套事务是子事务
  • 父事务出现异常,子事务也会回滚
  • 子事务出现异常,如果不进行处理,也会导致父事务回滚

NESTED 和 REQUIRED 有什么区别

我们在 LogService 进行当前事务回滚,修改 LogService,代码如下:

@Slf4j  
@Service  
public class LogService {  
    @Autowired  
    private LogInfoMapper logInfoMapper;  
  
    @Transactional(propagation = Propagation.NESTED)  
    public void insertLog(String name, String op) {  
        try {  
            int a = 10 / 0;  
        }catch (Exception e) {  
            // 回滚当前事务  
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  
        }  
        // 记录用户操作  
        logInfoMapper.insertLog(name, "用户注册");  
    }  
}

重新运行程序,发现用户表数据添加成功,日志表添加失败

  • LogService 中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,也就是说嵌套事务可以实现部分事务回滚

对比 REQUIRED

NESTED 传播机制改为 REQUIRED,修改代码如下:

@Slf4j  
@Service  
public class UserService {  
    @Autowired  
    private UserInfoMapper userInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRED)  
    public void registryUser(String name, String password) {  
        // 插入用户信息  
        userInfoMapper.insert(name, password);  
    }  
}
@Slf4j  
@Service  
public class LogService {  
    @Autowired  
    private LogInfoMapper logInfoMapper;  
  
    @Transactional(propagation = Propagation.REQUIRED)  
    public void insertLog(String name, String op) {  
        try {  
            int a = 10 / 0;  
        }catch (Exception e) {  
            // 回滚当前事务  
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  
        }  
        // 记录用户操作  
        logInfoMapper.insertLog(name, "用户注册");  
    }  
}

重新运行程序,发现用户表和日志表的数据都添加失败了

  • REQUIRED 如果回滚就是回滚所有事务,不能实现部分事务的回滚(因为属于同一个事务)

NESTED 和 REQUIRED 区别

  • 整个事务如果全部执行成功,二者的结果是一样的
  • 如果事务一部分执行成功,REQUIRED 加入事务会导致整个事务全部回滚。NESTED 嵌套事务可以实现局部回滚,不会影响上一个方法中执行的结果

嵌套事务之所以能够实现部分事务的回滚,是因为事务中有一个保存点(savepoint)的概念,其那套事务进入之后相当于新建了一个保存点,而回滚时只回滚到当前保存点

总结

  1. Spring 中使用事务,有两种方式:编程式事务(手动操作)和声明式事务。其中声明式事务使用较多,在方法上添加 @Transactional 即可实现
  2. 通过 @Transactional(isolation = Isolation.SERIALIZABLE) 设置事务的隔离级别。Spring 中的事务隔离级别有 5 种
  3. 通过 @Transactional(propagation = Propagation.REQUIRED 设置事务的传播机制,Spring 中的事务传播级别有 7 种,重点关注 REQUIRED(默认值)和 REQUIRES_NEW
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值