什么是事务
事务是指程序中一系列严密的逻辑操作,而且所有的操作必须全部完成成功,否则每个操作中的所做的所有更新都必须撤销。
通俗来讲,就是把多件事情当作一件事情去处理。我们拿学生表和班级表来举个简单的例子
- 需求:我们想要在学生表中添加学生时,也同样在班级表中把班级添加进去
首先想到的可能是这种方式实现:
在 student.service 中调用一次 class.service 中的添加方法,大概是这样的
1 | await this.studentModel.save(student); |
但是,我们看执行的结果,虽然数据都添加到了数据库,但是,执行了两次 COMMIT (如下图) ,
而且,假如我们在执行完 student 的添加之后,抛出异常,那么此时 student 的数据是已经添加成功了的,class 里的数据却没有添加进去,这显然不符合预期。
所以,我们就需要去了解 QueryRunner ,来将两个 INSERT 放到同一个事务中。
QueryRunner
TypeORM 给出了三种应用事务的方式:
- 手动创建和使用事务
- 使用事务装饰器
- 使用QueryRunner
这里,我们先介绍使用 QueryRunner 创建事务。
步骤:
- 获取连接并创建新的 QueryRunner
1
const queryRunner = getConnection().createQueryRunner();
- 使用我们的新 QueryRunner 建立真正的数据库连
1
await queryRunner.connect();
- 开始事务
1
await queryRunner.startTransaction();
- 对此事务执行一些操作
这里就是我们要写的添加数据到学生表和班级表 - 提交事务
1
await queryRunner.commitTransaction();
- 有错误做出回滚更改完整代码:
1
await queryRunner.rollbackTransaction();
这样,我们在添加学生的时候,班级表里也会添加数据了,如果抛出异常的情况下,也会进行回滚,这样一个完整的事务就完成了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53// src/service/student.service.ts
import { Inject, Provide } from '@midwayjs/decorator';
import { InjectEntityModel } from '@midwayjs/orm';
import { StudentEntity } from '../entity/student.entity';
import { Repository } from 'typeorm';
import { ILogger } from '@midwayjs/logger';
import { getConnection } from 'typeorm';
import { ClassEntity } from '../entity/class.entity';
'') (
export class StudentService {
()
logger: ILogger;
(StudentEntity)
studentModel: Repository<StudentEntity>;
(ClassEntity)
clazzModel: Repository<ClassEntity>;
// save
async saveStudent(param) {
// create a entity object
const student = new StudentEntity();
student.student_name = param.name;
student.student_id = param.id;
student.class = param.clazzName;
const clazz = new ClassEntity();
clazz.class_name = '三年级2班';
// 获取连接并创建新的 QueryRunner
const queryRunner = getConnection().createQueryRunner();
// 使用我们的新 QueryRunner 建立真正的数据库连
await queryRunner.connect();
// 开始事务:
await queryRunner.startTransaction();
try {
// 对此事务执行一些操作:
await queryRunner.manager.save(student);
await queryRunner.manager.save(clazz);
// 提交事务:
await queryRunner.commitTransaction();
} catch (err) {
// 有错误做出回滚更改
await queryRunner.rollbackTransaction();
}
}
}
如下图,我们看到这次的日志中,执行了两次 INSERT 之后,统一进行的 COMMIT ,而不再是像最开始的那样,执行两次 COMMIT 了。 完整的代码,可以从我的 【 GitHub 】 查看。