引用操作:级联删除与约束行为
本教程共 54 篇 · 第 14 篇 · 更新于 2026-08-11 · 约 4 分钟阅读
本节目标:掌握 onDelete/onUpdate 五种动作与默认值,避开循环引用陷阱,理解 relationMode 的两种模式。
删掉一个用户,他的文章怎么办?一起删、保留但置空作者、还是干脆拒绝删除?答案由引用操作(referential actions)决定。
引用操作写在 @relation 属性里,最终映射为数据库的外键约束:
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
onDelete 管删除父记录,onUpdate 管父记录主键值变化。Prisma 提供五种动作。
五种动作
| 动作 | 删除父记录时 | 适用场景 |
|---|---|---|
| Cascade | 子记录一并删除 | 文章与评论 |
| Restrict | 有子记录就拒绝删除 | 保护历史数据 |
| NoAction | 基本同 Restrict,PG 可延迟到事务末尾检查 | SQL Server 兼容 |
| SetNull | 外键置空 | 作者注销但保留文章 |
| SetDefault | 外键恢复默认值 | 匿名作者 |
写法就是在 @relation 里加参数:
// 作者注销后,文章保留,authorId 置空
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull)
authorId Int?
SetNull 要求外键可选;SetDefault 要求外键有 @default。组合用错,迁移或运行时会报错。
Note隐式多对多不支持引用操作。想级联清理连接记录,必须转成显式多对多,把动作写在连接表模型上。
默认值:不写也有行为
什么都不写时,Prisma 按关系是否必填套默认值:
- onDelete:必填关系用 Restrict,可选关系用 SetNull
- onUpdate:一律 Cascade
口诀:删必填要拦,删可选可空,改主键跟着走。
各家数据库还有自己的脾气:
- MySQL/MariaDB 不支持 SetDefault(8.0+ 把它当 NoAction 别名)
- PostgreSQL 允许对非空字段声明 SetNull,但触发时会报错,Prisma 会警告你改成可选
- SQL Server 没有 Restrict,用 NoAction 代替
循环引用陷阱
默认的 onUpdate: Cascade 在自关系里会形成循环。员工与经理的关系就是这样:
model Employee {
id Int @id @default(autoincrement())
manager Employee? @relation("management", fields: [managerId], references: [id], onUpdate: NoAction, onDelete: NoAction)
managees Employee[] @relation("management")
managerId Int?
}
在 SQL Server 和 MongoDB 上,自关系、三表循环、多条级联路径都会触发校验错误,提示你把其中一侧的 onDelete/onUpdate 改成 NoAction 来断环。Prisma 在生成 SQL 前就会检查出来,不用等数据库报错。
Tip环路的本质是「级联追着自己跑」。任选一条边改成 NoAction,环就断了。
relationMode:外键还是模拟
关系模式由 datasource 块里的 relationMode 控制:
datasource db {
provider = "mysql"
relationMode = "prisma"
}
两种模式:
- foreignKeys(默认):外键约束和引用操作都在数据库执行,Prisma Migrate 会生成对应的 ADD CONSTRAINT SQL
- prisma:Prisma Client 用额外查询模拟引用完整性,给不支持外键的数据库用,比如 PlanetScale 和 MongoDB
模拟模式有代价:
- 每个相关操作多跑几条查询,性能有损耗
- 不自动创建外键索引,要在关系标量字段上手动加
@@index,否则查关联会全表扫描 - NoAction(PG/SQLite)、SetDefault 在模拟模式下不支持
错误消息也不一样:foreignKeys 报「外键约束失败」,prisma 模式报「违反必填关系」。
Tip只要数据库支持外键,就用默认的 foreignKeys。模拟模式是给 PlanetScale 这类特殊场景准备的。
MongoDB 只有 prisma 模式可用,但 v7 不支持 MongoDB,相关讨论留到 v6.19 教程。
参考来源
- Prisma 官方文档:Referential actions
- Prisma 官方文档:Relation mode
- Mapagam:Understanding Referential Actions