淘先锋技术网

首页 1 2 3 4 5 6 7

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 异常不回滚几种原因

  1. @Transactional是基于AOP动态代理做的,所以方法必须是public;其他的修饰符不生效。
  2. 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中七种事务传播行为

序号事务传播行为类型说明
1PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
2PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行
3PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
4PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
5PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
7PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

可以看出1、2、3为一组,都表现对当前事务的支持,不同的在于当前不存在事务的处理方式;4、5、6为一组,都表现对当前事务的不支持,不同的在于当前有事务的处理方式。然后7单独一组。

举个小栗子,比如有一个service中有方法A,调用了方法B,方法B的传播行为是PROPAGATION_REQUIRED,那么如果方法A的传播行为也是PROPAGATION_REQUIRED,方法A运行的时候,开启了一个事务,方法A中执行到方法B的时候,察觉到当前已经有事务了,方法B就不会再创建新的事务,直接包含在方法A的事务当中。