事务
数据库事务是一系列读/写操作,这些操作保证要么全部成功,要么全部失败。在本章中,我们将介绍
交互式事务
使用 $transaction
方法在事务中执行闭包。如果闭包中的任何操作失败,整个事务将回滚
dart
/// Transfer views from one post to another
transfer(int from, int to, int views) async {
await prisma.$transaction((tx) async {
// 1. Decrement views from the source post
await tx.post.update(
where: PostWhereUniqueInput(id: from),
data: PrismaUnion.$1(
PostUpdateInput(
views: PrismaUnion.$2(
IntFieldUpdateOperationsInput(decrement: views),
),
),
),
);
// 2. Increment views from the destination post
await tx.post.update(
where: PostWhereUniqueInput(id: to),
data: PrismaUnion.$1(
PostUpdateInput(
views: PrismaUnion.$2(
IntFieldUpdateOperationsInput(increment: views),
),
),
),
);
});
}
await transfer(1, 2, 10); // Transfer 10 views from post 1 to post 2
await transfer(2, 1, 10); // Transfer 10 views from post 2 to post 1
在上面的示例中,您可以尝试传递无效数据到 transfer
,因为任何抛出的异常都将导致事务回滚,而不会影响数据。
捕获异常/错误
您有时可能希望在事务中捕获异常或错误。您可以使用 try/catch
语句来实现
dart
try {
await prisma.$transaction((tx) async {
// Code running in a transaction...
});
} catch (e) {
// Handle the rollback...
}
隔离级别
Prisma Dart 客户端事务支持隔离级别(如果数据库支持)。默认情况下,事务使用数据库的默认隔离级别。要更改隔离级别,请使用 isolationLevel
参数
dart
await prisma.$transaction(
isolationLevel: TransactionIsolationLevel.serializable,
(tx) async {
// Code running in a transaction...
},
);
支持的数据库隔离级别矩阵
数据库 | 未提交读 | 已提交读 | 可重复读 | 序列化 | 快照 |
---|---|---|---|---|---|
PostgreSQL | ✅ | ✅ | ✅ | ✅ | ❌ |
MySQL | ✅ | ✅ | ✅ | ✅ | ❌ |
SQL Server | ✅ | ✅ | ✅ | ✅ | ✅ |
CockroachDB | ❌ | ❌ | ❌ | ✅ | ❌ |
SQLite | ❌ | ❌ | ❌ | ✅ | ❌ |
默认隔离级别
PostgresSQL | MySQL | SQL Server | CockroachDB | SQLite |
---|---|---|---|---|
已提交读 | 可重复读 | 已提交读 | 序列化 | 序列化 |
警告
TransactionIsolationLevel
枚举公开了 Prisma Dart 客户端支持的所有隔离级别。但是,并非所有数据库都支持所有隔离级别。例如,SQLite 仅支持 Serializable
隔离级别。
超时
当您使用交互式事务时,为了避免长时间等待,您可以通过以下两个参数设置事务运行时间
maxWait
- 客户端等待数据库事务的最长时间,默认为2
秒timeout
- 交互式事务在被取消或回滚之前可以运行的最长时间,默认为5
秒
dart
await prisma.$transaction(
(tx) async {
// Code running in a transaction...
},
maxWait: 5000, // Default is 2000
timeout: 10000, // Default is 5000
);
警告
您应该谨慎使用 timeout 参数,长时间保持事务打开会损害数据库性能,甚至可能导致死锁。尝试避免在事务函数中执行网络请求和性能较慢的查询。我们建议尽快进入和退出!
手动事务
Prisma Dart 客户端支持手动事务,这意味着您可以在事务中执行任意数量的操作,然后决定是提交还是回滚事务。
dart
final tx = await prisma.$transaction.start();
try {
// Delete all posts
await tx.post.deleteMany();
// Delete all users
await tx.user.deleteMany();
// Commit the transaction
await tx.$transaction.commit();
} catch (e) {
// Rollback the transaction
await tx.$transaction.rollback();
}
使用 $transaction.start()
将返回一个新的 PrismaClient
实例。事务中的所有操作都应在此实例上执行。当您决定提交或回滚事务时,请调用 $transaction.commit()
或 $transaction.rollback()
。