跳至主要内容

引用操作升级路径

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 时写入数据库外键的默认引用操作。

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

在数据库引用操作之上,Prisma 客户端版本 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

danger

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

确保您对架构中 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 命令)时,将 新默认值 应用于您的数据库。

info

与您第一次运行 prisma db pull 不同,新的引用操作子句和属性不会由 Prisma VSCode 扩展自动添加到您的 Prisma 架构中。如果您希望使用除新默认值以外的任何其他值,则必须手动添加它们。

在 Prisma 架构中显式定义引用操作是可选的。如果您没有为关系显式定义引用操作,则 Prisma ORM 使用 新默认值

请注意,可以逐个添加引用操作。这意味着您可以将它们添加到一个关系,而将其他关系保留为默认值,而无需手动指定任何内容。

检查错误

升级到版本 3.0.1(或版本 2.26.0 及更高版本,启用了 referentialActions 功能标志)之前,Prisma ORM 在使用 delete()deleteMany() 时会阻止删除记录以保持引用完整性。Prisma 客户端将抛出一个自定义运行时错误,错误代码为 P2014

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

当您使用 NoActionRestrict 来阻止删除记录时,版本 3.0.1 及更高版本(或启用了 referentialActions 功能标志的 2.26.0)中的错误消息将与之前的版本不同。这是因为它们现在由数据库触发,而不是 Prisma 客户端。可以预期的新的错误代码是 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)
})