SpringBoot 事务(@Transactional)整合
Spring boot 事务整合
spring boot 事务是怎么控制的呢?就是我们熟知的@Transactional
注解了
业务分析
让代码更新信息后抛出异常,看看数据会不会回滚
实战编码
修改更新用户信息接口抛出一个异常
@PutMapping("/user")
@Transactional(rollbackFor = Exception.class)
public String updateUser(@RequestBody ApiUserVO vo) {
int flag = userService.updateUser(vo);
int i =1/0;
if (flag <= 0) {
return "修改失败";
}
return "修改成功";
}
抛出异常后数据库并未进行修改;
@Transactional 异常不回滚几种原因
- @Transactional是基于AOP动态代理做的,所以方法必须是public;其他的修饰符不生效。
- Spring事务控制器在catch里面做了判断只有RuntimeException或者错误信息的时候才会回滚,故不是RuntimeException的异常不会回滚。
Transactional的异常控制,默认是Check Exception 不回滚,unCheck Exception回滚 如果配置了rollbackFor 和 noRollbackFor 且两个都是用同样的异常,那么遇到该异常,还是回滚 rollbackFor 和noRollbackFor 配置也许不会含盖所有异常,对于遗漏的按照Check Exception 不回滚,unCheck Exception回滚。
数据库的四种隔离级别
数据库一共有如下四种隔离级别:
Read uncommitted
读未提交
在该级别下,一个事务对一行数据修改的过程中,不允许另一个事务对该行数据进行修改,但允许另一个事务对该行数据读。
因此本级别下,不会出现更新丢失,但会出现脏读、不可重复读。Read committed
读已提交
在该级别下,未提交的写事务不允许其他事务访问该行,因此不会出现脏读;但是读取数据的事务允许其他事务的访问该行数据,因此会出现不可重复读的情况。Repeatable read
可重复读
在该级别下,读事务禁止写事务,但允许读事务,因此不会出现同一事务两次读到不同的数据的情况(不可重复读),且写事务禁止其他一切事务。Serializable 序列化
该级别要求所有事务都必须串行执行,因此能避免一切因并发引起的问题,但效率很低。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | √ | √ | √ |
读已提交(Read committed) | × | √ | √ |
可重复读(Repeatable read) | × | × | √ |
可串行化(Serializable ) | × | × | × |
Spring中七种事务传播行为
序号 | 事务传播行为类型 | 说明 |
---|---|---|
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类似的操作。 |
可以看出1、2、3为一组,都表现对当前事务的支持,不同的在于当前不存在事务的处理方式;4、5、6为一组,都表现对当前事务的不支持,不同的在于当前有事务的处理方式。然后7单独一组。
举个小栗子,比如有一个service中有方法A,调用了方法B,方法B的传播行为是PROPAGATION_REQUIRED,那么如果方法A的传播行为也是PROPAGATION_REQUIRED,方法A运行的时候,开启了一个事务,方法A中执行到方法B的时候,察觉到当前已经有事务了,方法B就不会再创建新的事务,直接包含在方法A的事务当中。