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 UNCOMMITTEDyesyesyesnoyes
READ COMMITTEDnoyesyesnoyes
REPEATABLE READnonoyesnono
SERIALIZABLEnonononono

在Springboot中,我们先引入事务依赖包,加入了mybatis这个依赖后,springboot就已经自动注入了DataSourceTransactionManager,这样就可以直接使用@Transactional 注解进行事务使用,Transactional 既可以注解在方法上,也可以注解在类上,注解在类上代表该类的public方法都开启事务的,如果类和方法都开启了Transactional ,类级别的注解会重载方法级的注解。
在这里插入图片描述
我们紧接上一篇的代码,在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();
    }

运行之后,会直接抛出异常,数据库并没有增加记录,这样异常就生效了。
在这里插入图片描述
现在如果我们注释掉throw new RuntimeException();这行代码,那么记录就会插入数据库。
在这里插入图片描述
有时候有些异常没有被捕获到,而造成事务没有回滚,这是由于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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

机核动力

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

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

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

打赏作者

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

抵扣说明:

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

余额充值