跳至主要内容

引用操作升级路径

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

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 模式中。如果您一直使用 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-m 关系,并在 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)
})