事务管理:确保素材一致性的关键
事务
事务是一组操作的集合,是一个不可分割的操作
事务会把所有的操作作为一个整体,一起向数据库提交或者是撤销操作请求,所以这组操作要么同时成功要么同时失效
事务应用
比如转账操作:
1.账户 A 给 账户 B 转了 100元
2.账户 B 的账户 +100元
这是如果没有事务的话,就可能会出现第一步实现了,但是第二步没有实现,导致账户 A 的钱减少了,但是账户 B 却未到账的问题。
虽然事务没有办法去解决第二步为啥没有实现的这个问题,但是可是使得 B 要是没有收到钱的话,A 的账户就不会减少
要么一起成功,要么一起失败
事务的操作
事务的操作主要分为三步:
1.开启事务: start transaction/begin (一组操作前开启事务)
2.提交事务:commit
3. 回滚事务: roolback
Spring 中事务的实现
Spring 中的事务操作分为两类:
1.编程式事务
需求:用户注册时在日志表中插入一条操作记录
数据准备:
-- 创建数据库
DROP DATABASE IF EXISTS trans_test;
CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;
-- 切换到目标数据库
USE trans_test;
-- 用户表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
`id` INT NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR (128) NOT NULL,
`password` VARCHAR (128) NOT NULL,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARACTER SET = utf8mb4 COMMENT = '用户表';
-- 操作日志表
DROP TABLE IF EXISTS log_info;
CREATE TABLE log_info (
`id` INT PRIMARY KEY auto_increment,
`user_name` VARCHAR ( 128 ) NOT NULL,
`op` VARCHAR ( 256 ) NOT NULL,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now()
) DEFAULT charset 'utf8mb4';
代码准备:
配置文件

实体类


Mapper


Service


Controller

Spring 编程式事务
Spring 手动操作事务 和 Mysql 操作事务类似,有 3 个重要操作步骤:
开启事务
提交事务
回滚事务



数据库插入成功
我们打开回滚来看看


虽然 注册成功了,但是数据库并没有新增数据
2.声明式事务 @Transactional
声明式事务实现只需在事务的方法上添加 @Transactional 注解就可以实现了
无需手动开启事务和提交事务,进行方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没处理的异常会自动回滚事务



我们试一下异常会咋样



@Transactional 作用
它可以用来修饰方法 和 类:
修饰方法时:只有修饰 public 方法时才能生效
修饰类时:对 @Transactional 修饰的类中所有的 public 方法都生效
需要注意的是我们前面说出现异常没有处理时,就会回滚,但要是将异常给捕获了的话,方法会被认为成功执行,依然会提交事务
我们试着捕获一下:


如果我们要事务进行回滚有两种方法:
1.重新抛出异常

2.手动回滚事务

使用
TransactionAspectSupport.currentTransactionStatus()
来获取事务,并且设置回滚
@Transactional
@Transactional 中常见的三个属性:
1.rollbackFor
异常回滚属性,指定能够出发事务回滚的异常类型
@Transactional 默认只在遇到运行时异常和Error时才会回滚,非运行时异常不会滚,既 Exception 的子类中,除了 RuntimeException 及其子类



出现了异常,但是并没有出发回滚
我们需要所有的异常都进行回滚,通常需要配置 @Transactional,rollbackFor 指定出现何种异常类型时事务进行回滚


现在在出发这种类型的异常就进行了回滚了
2.Isolation
事务隔离级别
读未提交
读提交
可重复读
串行化

这在 MySQL 那里已经做过详细的介绍
Spring 事务隔离级别
| 序号 | 隔离级别常量 | 说明 | 对应 SQL 标准隔离级别 |
|---|---|---|---|
| 1 | Isolation.DEFAULT | 以连接的数据库的事务隔离级别为主 | - |
| 2 | Isolation.READ_UNCOMMITTED | 读未提交 | READ UNCOMMITTED |
| 3 | Isolation.READ_COMMITTED | 读已提交 | READ COMMITTED |
| 4 | Isolation.REPEATABLE_READ | 可重复读 | REPEATABLE READ |
| 5 | Isolation.SERIALIZABLE | 串行化 | SERIALIZABLE |
Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置
3.propagation
事务的传播机制就是:多个事务方法存在调用关系中,事务是如何在这些方法间进行传播的
比如有两个方法 A 和 B 都被 @Transactional 修饰,A 方法调用了 B 方法
A 方法运行时开启了事务,调用 B方法 的时候,B 本身也有事务,这个时候是加入 方法A 的事务 还是创建一个新的事务,这就涉及到了 事务的传播机制
事务隔离级别是解决多个事务同时调用一个数据库的问题
事务传播机制解决的是一个事务在多个节点中传递的问题
事务的传播机制有哪些
Spring 中事务传播机制可以通过 @Transactional 中的 propagation 属性来制定传播行为
Spring 中事务传播机制有 7 种:
| 序号 | 事务传播行为 | 说明 |
|---|---|---|
| 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。 |
| 对比项 | Propagation.REQUIRED | Propagation.NESTED |
|---|---|---|
| 执行成功时 | 结果与 NESTED 一致 | 结果与 REQUIRED 一致 |
| 部分执行成功时 | 会导致整个事务全部回滚 | 可实现局部回滚,不影响上一个方法的执行结果 |

浙公网安备 33010602011771号