引用操作升级路径
Prisma ORM 版本 2.x 阻止在某些 Prisma 客户端函数中删除已连接的记录,并且不允许您在 Prisma 架构中配置引用操作来更改此行为。
Prisma ORM 版本 3.x 及更高版本允许您通过在模型的关系上显式设置引用操作来控制删除或更新记录时应发生的情况。升级后,Prisma 客户端将不再强制执行任何引用操作,任何写入数据库外键的操作将定义删除或更新记录时的行为。
Prisma Migrate 3.x 将使用 Prisma 客户端以前执行的操作作为写入数据库外键约束的新默认值。
Prisma ORM 2.x 行为
当使用 Prisma 客户端在必需关系上调用 delete()
或 deleteAll()
方法时,会执行运行时检查,如果记录引用了相关对象,则会阻止删除记录。这会阻止级联行为,无论外键如何定义。
在没有升级的情况下,Prisma ORM 2 中的行为根本不允许设置引用操作。 查看 Prisma ORM 2.x 默认引用操作
如果您需要实际使用数据库中配置的级联行为,您可以使用 raw
SQL 查询 删除多个引用的记录。这是因为 Prisma 客户端不会对原始查询执行运行时检查。
Prisma ORM 2.x 默认引用操作
以下是使用 Prisma Migrate 版本 2.x 时写入数据库外键的默认引用操作。
子句 | 可选关系 | 强制关系 |
---|---|---|
onDelete | SetNull | Cascade |
onUpdate | Cascade | Cascade |
在数据库引用操作之上,Prisma 客户端版本 2.x 中强制执行以下操作
子句 | 可选关系 | 强制关系 |
---|---|---|
onDelete | SetNull | Restrict |
onUpdate | Cascade | Cascade |
升级路径
在升级时,您可以采取几种路径,这将根据所需结果给出不同的结果。
如果您当前使用迁移工作流程,您可以运行 prisma db pull
来检查默认值在您的架构中如何反映。然后,如果需要,您可以手动更新数据库。
您也可以决定跳过检查默认值并运行迁移以使用新默认值更新数据库。
使用内省
如果您 内省您的数据库,数据库级别配置的引用操作将反映在您的 Prisma 架构中。如果您一直使用 Prisma Migrate 或 prisma db push
来管理数据库架构,那么这些很可能就是 <=2.25.0 默认值。
当您运行内省时,Prisma ORM 会将数据库中的所有外键与架构进行比较,如果 SQL 语句 ON DELETE
和 ON UPDATE
与默认值不匹配,则将在架构文件中显式设置它们。
内省后,您可以查看架构中非默认的子句。要查看的最重要的子句是 onDelete
,它在版本 2.25.0 及更早版本中默认设置为 Cascade
。
如果您使用的是 delete()
或 deleteAll()
方法,级联删除现在将执行,因为以前在运行时阻止级联删除的 Prisma 客户端中的安全网已删除。请务必检查您的代码并做出相应的调整。
确保您对架构中 onDelete: Cascade
的每个情况都满意。如果没有,则要么
- 修改您的 Prisma 架构并
db push
或dev migrate
来更改数据库,或者 - 如果您只在工作流程中使用
prisma db pull
,则手动更新基础数据库
以下示例会导致级联删除,这意味着如果删除了 User
,那么他们所有的 Post
也将被删除。
博客架构示例
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[]
}
使用迁移
当运行 迁移(或 prisma db push
命令)时,将 新默认值 应用于您的数据库。
与您第一次运行 prisma db pull
不同,新的引用操作子句和属性不会由 Prisma VSCode 扩展自动添加到您的 Prisma 架构中。如果您希望使用除新默认值以外的任何其他值,则必须手动添加它们。
在 Prisma 架构中显式定义引用操作是可选的。如果您没有为关系显式定义引用操作,则 Prisma ORM 使用 新默认值。
请注意,可以逐个添加引用操作。这意味着您可以将它们添加到一个关系,而将其他关系保留为默认值,而无需手动指定任何内容。
检查错误
在升级到版本 3.0.1(或版本 2.26.0 及更高版本,启用了 referentialActions
功能标志)之前,Prisma ORM 在使用 delete()
或 deleteMany()
时会阻止删除记录以保持引用完整性。Prisma 客户端将抛出一个自定义运行时错误,错误代码为 P2014
。
在升级后,Prisma ORM 不再执行运行时检查。相反,您可以指定自定义引用操作来保持关系之间的引用完整性。
当您使用 NoAction
或 Restrict
来阻止删除记录时,版本 3.0.1 及更高版本(或启用了 referentialActions
功能标志的 2.26.0)中的错误消息将与之前的版本不同。这是因为它们现在由数据库触发,而不是 Prisma 客户端。可以预期的新的错误代码是 P2003
,因此您应该检查您的代码以进行相应的调整。
捕获错误的示例
以下示例使用以下博客架构,其中 Post
和 User
之间存在 1-m 关系,并在 author
字段上设置了 Restrict
引用操作。
这意味着如果用户有帖子,则不能删除该用户(及其帖子)。
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
authorId String
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
在升级之前,当您尝试删除具有帖子的用户时,您会收到错误代码 P2014
,其消息为
"您尝试进行的更改将违反 {model_a_name} 和 {model_b_name} 模型之间所需的 '{relation_name}' 关系。"
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id',
},
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
console.log(error.message)
}
}
}
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
为了确保您在代码中检查了正确的错误,请修改您的检查以查找 P2003
,它将传递消息
"外键约束在字段:{field_name} 上失败"
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id'
}
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
if (error.code === 'P2003') {
console.log(error.message)
}
}
}
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})