7.1 核心概念
- 事务(transaction)
- 一组要么全成、要么全败的数据库操作,保证 ACID。Prisma 提供两种:批处理数组式与交互式回调式。
- 批处理事务(sequential $transaction)
- 传入 Promise 数组,Prisma 顺序执行并包在同一事务里,任一失败全部回滚。适合无条件依赖的多写入。
- 交互式事务(interactive transaction)
- 传入
async (tx) => {}回调,在回调里用tx做查询并按结果分支决策。抛错则回滚。 - 隔离级别(isolation level)
- 控制并发事务可见性:ReadCommitted、RepeatableRead、Serializable 等,隔离越强并发越低、越安全。
- $queryRaw / $queryRawUnsafe
- 执行返回结果的原始 SQL。
$queryRaw用标签模板参数化(防注入),Unsafe变体拼字符串(需自行防注入)。 - $executeRaw
- 执行不返回行、只返回受影响行数的原始 SQL(INSERT/UPDATE/DELETE/DDL)。
- 乐观并发(optimistic concurrency)
- 不加锁,靠版本号/时间戳字段在更新时校验;若期间被他人改动则更新命中 0 行,据此重试或报冲突。
7.2 批处理事务
// 转账:两条更新要么都成功要么都回滚
const [from, to] = await prisma.$transaction([
prisma.account.update({ where: { id: 1 }, data: { balance: { decrement: 100 } } }),
prisma.account.update({ where: { id: 2 }, data: { balance: { increment: 100 } } }),
]);
7.3 交互式事务
await prisma.$transaction(async (tx) => {
const acc = await tx.account.findUnique({ where: { id: 1 } });
if (!acc || acc.balance < 100) {
throw new Error("余额不足"); // 抛错 → 整个事务回滚
}
await tx.account.update({ where: { id: 1 }, data: { balance: { decrement: 100 } } });
await tx.account.update({ where: { id: 2 }, data: { balance: { increment: 100 } } });
}, {
maxWait: 5000, // 等待连接的最长毫秒
timeout: 10000, // 事务最长执行毫秒
isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
});
交互式事务要短
事务期间会占用连接并可能持锁。回调里别做耗时的网络/文件 IO,也别把无关逻辑塞进去,否则拖长事务、加剧锁竞争与连接耗尽。
7.4 原始 SQL:$queryRaw
import { Prisma } from "@prisma/client";
// 参数化标签模板——自动转义,防 SQL 注入
const email = "bob@example.com";
const users = await prisma.$queryRaw<Array<{ id: number; email: string }>>`
SELECT id, email FROM "User" WHERE email = ${email}
`;
// 动态拼片段用 Prisma.sql / Prisma.join,仍保持参数化
const ids = [1, 2, 3];
const rows = await prisma.$queryRaw(
Prisma.sql`SELECT * FROM "Post" WHERE "authorId" IN (${Prisma.join(ids)})`
);
切勿字符串拼接 SQL
用 $queryRawUnsafe(`... ${userInput}`) 直接拼接用户输入 = SQL 注入。必须用标签模板 $queryRaw`...${v}` 或 Prisma.sql,让 Prisma 做参数绑定。
7.5 $executeRaw
// 返回受影响行数
const affected = await prisma.$executeRaw`
UPDATE "Post" SET "views" = "views" + 1 WHERE "id" = ${postId}
`;
7.6 乐观并发控制
// schema 加 version 字段;更新时以旧 version 为条件
const res = await prisma.post.updateMany({
where: { id: postId, version: currentVersion },
data: { title: newTitle, version: { increment: 1 } },
});
if (res.count === 0) {
throw new Error("记录已被他人修改,请刷新重试");
}
7.7 批量操作对比
| 方法 | 用途 | 是否原子 |
|---|---|---|
| createMany | 批量插入 | 单条 SQL,是 |
| updateMany / deleteMany | 按条件批量改删 | 单条 SQL,是 |
| $transaction([...]) | 多个不同操作打包 | 是 |
| 循环里逐条 update | 不推荐 | 否(除非包事务) |
7.8 小结与下一步
- 批处理事务传 Promise 数组;交互式事务用回调
tx做条件分支,抛错即回滚。 - 原始 SQL 用
$queryRaw(带返回)/$executeRaw(返回行数),务必用标签模板参数化。 - 并发用乐观锁(version 字段 + updateMany 条件校验 count)。
- 下一章讲 高级特性——$extends、软删除、JSON、全文搜索。