SpringBoot中级开发--事务配置管理(10)

事务在整个开发框架中是一个非常常用的功能,特别涉及到数据库操作。像Mysql,就有4个数据库级别:

(1) READ UNCOMMITTED(读未提交):允许读取未提交的数据。这种级别的事务可以读取到其他事务未提交的数据,可能会导致脏读、不可重复读和幻读等问题。

(2)READ COMMITTED(读已提交):只能读取已经提交的数据。这种级别的事务可以避免脏读,但可能会出现不可重复读和幻读等问题。

(3)REPEATABLE READ(可重复读):保证在同一个事务中多次读取同一份数据时,得到的结果是一致的。这种级别的事务可以避免脏读和不可重复读,但可能会出现幻读等问题。

(4)SERIALIZABLE(串行化):强制事务串行执行,避免了所有并发问题。这种级别的事务可以避免所有并发问题,但可能会导致性能问题。

事务级别从低到高,性能越来越差。通过不同的应用场景,选择不同的事务。
(1) READ UNCOMMITTED(读未提交):对数据一致性要求不高的,比如 统计分析

(2)READ COMMITTED(读已提交):对数据一致性要求较高,比如,金融计费系统

(3)REPEATABLE READ(可重复读):对数据一致性要求非常高,比如银行

(4)SERIALIZABLE(串行化):对数据一致性要求最高,这个也是银行应用比较多

接下来我们介绍,脏读,不可重复读,幻像读,第一类更新丢失,第二类更新丢失

脏读

时间序 转账事务甲 取款事务乙
1 开始事务
2 开始事务
3 查询到账户有100元
4 取出50,余额修改为50元
5 查询到余额为50元
6 撤销事务,余额改回100元
7 存入10元,余额为60元
8 事务提交

发生在第5步的就是脏读,由于甲读到乙未有提交的数据,造成脏读,后面乙又撤回了事务,把本该取的50元退了回去,但是甲由于读到了脏数据,并且根据脏数据存入了10元,这样就造成了账户白白丢失了50元。

不可重复读

时间序 取款事务甲 转账事务乙
1 开始事务
2 开始事务
3 查询到账户有100元
4 查询到账户有100元
5 取出10元,余额为90元
6 提交事务
7 查询余额为90元,与第4步的不一致

在同一个事务中,4,7时间序读出的余额都不一样。

幻像读

时间序 统计事务甲 转账事务乙
1 开始事务
2 开始事务
3 统计总账户有100元
4 现在增加一个账户,存入10元
5 提交事务
6 再次统计是110元

3,6查询出来的数据金额不一致。这样就出现了幻像读。幻像读和不可重复读感觉上好像一样,但是不可重复读是由于数据的更新造成的,要避免的话,直接加上行锁就可以。但是幻像读是由于数据新增造成的,对于这种问题,需要加上表锁。

第一类更新丢失

时间序 取款事务甲 转账事务乙
1 开始事务
2 开始事务
3 查询到账户有100元
4 查询到账户有100元
5 汇入10元,余额为110元
6 事务提交
7 取出10元,把余额设为90元
8 事务撤销
9 余额恢复为100元(丢失更新)

甲在撤销事务的时候,把乙存入的10元给抹掉了。

第二类更新丢失

时间序 转账事务甲 取款事务乙
1 开始事务
2 开始事务
3 查询到账户有100元
4 查询到账户有100元
5 取出10元,余额为90元
6 事务提交
7 存入10元
8 事务提交
9 把余额设为110元(丢失更新)

转账事务甲把取款业务乙的提交事务更新了,这样造成银行可能损失了10元。

Mysql的4个事务隔离级别能处理的业务并发

隔离级别 脏读 不可重复读 幻象读 第一类丢失更新 第二类丢失更新
READ UNCOMMITTED yes yes yes no yes
READ COMMITTED no yes yes no yes
REPEATABLE READ no no yes no no
SERIALIZABLE no no no no no

在Springboot中,我们先引入事务依赖包,加入了mybatis这个依赖后,springboot就已经自动注入了DataSourceTransactionManager,这样就可以直接使用@Transactional 注解进行事务使用,Transactional 既可以注解在方法上,也可以注解在类上,注解在类上代表该类的public方法都开启事务的,如果类和方法都开启了Transactional ,类级别的注解会重载方法级的注解。
SpringBoot中级开发--事务配置管理(10)_第1张图片
我们紧接上一篇的代码,在DAO层增加insert 代码

    @Insert("insert into welcome_info(info) values('transaction test')")
    @DS("test")
    public void  insertintoWelcomeinfo();

在Service层增加@Transactional注解,并且手动抛出异常

  @Transactional
    public void insertWelcomeInfo(){
        //插入一条记录
        welcomDao.insertintoWelcomeinfo();
        //手动抛出异常
        throw new RuntimeException();
    }

在Test用例代码中,增加测试用例方法,来测试上面的Service方法,看数据是否会回滚

    @Test
    void testTransactional(){
        welcomeService.insertWelcomeInfo();
    }

运行之后,会直接抛出异常,数据库并没有增加记录,这样异常就生效了。
SpringBoot中级开发--事务配置管理(10)_第2张图片
现在如果我们注释掉throw new RuntimeException();这行代码,那么记录就会插入数据库。
SpringBoot中级开发--事务配置管理(10)_第3张图片
有时候有些异常没有被捕获到,而造成事务没有回滚,这是由于Springboot的默认事务是遇到RuntimeException和程序错误Error造成事务回滚。如果要捕获这些通用的异常,只要在注解中增加rollbackfor选项。比如: @Transactional(rollbackFor = Exception.class)

我们来认识一下Transactional注解的主要配置选项

属性值 具体意义
propagationtiong 主要定义事务周期
isolation 设置事务隔离级别
timeout 指定事务过期时间,默认为数据库事务过期时间
readonly 指定事务是否是只读事务
rollbackFor 指定哪些异常可以引起事务回滚
noRollbackFor 指定哪些异常不引起事务回滚

propagationtiong选项:

REQUIRED,方法A调用时没有事务就新建,当调用另一个方法B时,方法B将使用与A相同的事务,如果B发生异常,这个事务都回滚回去。

REQUIRES_NEW,方法A和B,在方法调用的时候无论是否有开启一个新事务,如方法B有异常不会导致方法A的数据回滚。

NESTED 和上面的类似,但支持JDBC,不支持JPA或Hibernate

SUPPORT 方法调用时有事物就用事务,没事务就不用事务

NOT SUPPORT 强制不在事务中执行,在方法调用到结束阶段事务都会被挂起

NEVER 强制方法不在事务中执行,若有事务抛出异常

MANDATORY 强制方法在事务中,没有事务就抛出异常

Isolation选项

READ UNCOMMITTED

READ COMMITTED

REPEATABLE_READ

SERIALIZABLE

DEFAULT 使用数据库的默认是隔离级别,ORACLE,SQL SERVER是READ COMMITTED ,Mysql是REPEATABLE_READ

程序的源码在这里可以下载到链接:链接: https://pan.baidu.com/s/1jL04s4huVnTBOaQyg68QIA 提取码: vgut

你可能感兴趣的:(springboot,JAVA,spring,boot,后端,java,spring)