跳至主要内容

引用操作升级路径

Prisma ORM 2.x 版本在某些 Prisma Client 函数中阻止删除关联记录,并且不允许您在 Prisma Schema 中配置引用操作来更改此行为。

Prisma ORM 3.x 及更高版本允许您通过在模型的关系上显式设置引用操作来控制删除或更新记录时应发生的情况。升级后,Prisma Client 将不再强制执行任何引用操作,并且写入数据库外键的任何操作都将定义删除或更新记录时的行为。

Prisma Migrate 3.x 在将外键约束写入数据库时,将使用 Prisma Client 之前执行的操作作为新默认值。

Prisma ORM 2.x 行为

当使用 Prisma Client 在必要关系上调用 delete()deleteAll() 方法时,会执行运行时检查,如果记录引用了相关对象,则会阻止删除记录。这会阻止级联行为,无论外键如何定义

在不升级的情况下,Prisma ORM 2 的行为根本不允许设置引用操作。请参阅 Prisma ORM 2.x 默认引用操作

如果您需要实际使用数据库中配置的级联行为,您可以使用 raw SQL 查询来 删除多个引用记录。这是因为 Prisma Client 将对原始查询执行运行时检查。

Prisma ORM 2.x 默认引用操作

以下是使用 Prisma Migrate 2.x 版本时写入数据库外键的默认引用操作

子句可选关系强制关系
onDeleteSetNullCascade
onUpdateCascadeCascade

除了数据库引用操作之外,Prisma Client 2.x 版本还强制执行以下操作

子句可选关系强制关系
onDeleteSetNullRestrict
onUpdateCascadeCascade

升级路径

升级时有几种路径可供选择,根据所需的结果会产生不同的结果。

如果您目前使用迁移工作流程,您可以运行 prisma db pull 来检查默认值如何在您的模式中反映。然后,如果需要,您可以手动更新您的数据库。

您也可以决定跳过检查默认值并运行迁移以使用新默认值更新您的数据库。

使用内省

如果您内省您的数据库,数据库级别配置的引用操作将反映在您的 Prisma Schema 中。如果您一直使用 Prisma Migrate 或 prisma db push 来管理数据库模式,这些很可能是 <=2.25.0 默认值

当您运行内省时,Prisma ORM 会将数据库中的所有外键与模式进行比较,如果 SQL 语句 ON DELETEON UPDATE 与默认值匹配,它们将被显式设置在模式文件中。

内省后,您可以在模式中查看非默认子句。最重要的要审查的子句是 onDelete,它在 2.25.0 及更早版本中默认为 Cascade

危险

如果您正在使用 delete()deleteAll() 方法,现在将执行级联删除,因为 Prisma Client 中以前在运行时阻止级联删除的安全网已被移除。请务必检查您的代码并进行相应调整。

确保您对模式中所有 onDelete: Cascade 的情况都满意。如果不满意,请执行以下操作:

  • 修改您的 Prisma 模式并 db pushdev 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 版本(或启用了 referentialActions 功能标志的 2.26.0 及更高版本)之前,Prisma ORM 在使用 delete()deleteMany() 时会阻止记录删除以保留引用完整性。Prisma Client 会抛出一个自定义运行时错误,错误代码为 P2014

升级后,Prisma ORM 不再执行运行时检查。您可以通过指定自定义引用操作来保留关系之间的引用完整性。

当您使用 NoActionRestrict 阻止记录删除时,3.0.1 及更高版本(或启用了 referentialActions 功能标志的 2.26.0)中的错误消息将与之前版本中的错误消息不同。这是因为它们现在由数据库触发,而不是 Prisma Client。预期的新错误代码是 P2003,因此您应该检查您的代码以进行相应调整。

捕获错误的示例

以下示例使用下面的博客模式,其中 PostUser 之间存在 1 对多关系,并在 author 字段上设置了 Restrict 引用操作。

这意味着如果用户有帖子,则该用户(及其帖子)无法被删除。

schema.prisma
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)
})
© . This site is unofficial and not affiliated with Prisma Data, Inc.