跳到主要内容

关系模式

在 Prisma schema 中,记录间的关系通过 @relation 属性定义。例如,在以下 schema 中,UserPost 模型之间存在一对多关系

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade)
authorId Int
}

model User {
id Int @id @default(autoincrement())
posts Post[]
}

Prisma ORM 拥有两种关系模式foreignKeysprisma,它们指定了记录间关系的强制执行方式。

如果您将 Prisma ORM 与关系型数据库一起使用,则默认情况下,Prisma ORM 使用foreignKeys 关系模式,该模式在数据库级别通过外键强制执行记录间的关系。外键是表中的一列或一组列,其值基于另一个表中的主键。外键允许您

  • 设置约束以防止您进行破坏引用的更改
  • 设置引用操作,以定义如何处理对记录的更改

这些约束和引用操作共同保证了数据的引用完整性

对于上述示例 schema,如果您使用 PostgreSQL 连接器,Prisma Migrate 将默认生成以下 SQL


-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"authorId" INTEGER NOT NULL,

CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
//highlight-start
ALTER TABLE "Post"
ADD CONSTRAINT "Post_authorId_fkey"
FOREIGN KEY ("authorId")
REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
//highlight-end

在这种情况下,Post 表的 authorId 列上的外键约束引用了 User 表的 id 列,并保证帖子必须拥有一个存在的作者。如果您更新或删除用户,则 ON DELETEON UPDATE 引用操作指定了 CASCADE 选项,该选项也将删除或更新属于该用户的所有帖子。

某些数据库,例如 MongoDB 或 PlanetScale,不支持外键。此外,在某些情况下,开发人员可能更倾向于不在通常支持外键的关系型数据库中使用外键。对于这些情况,Prisma ORM 提供了prisma 关系模式,该模式模拟了关系型数据库中关系的一些属性。当您启用 prisma 关系模式并使用 Prisma Client 时,查询行为相同或相似,但引用操作和一些约束由 Prisma 引擎而不是数据库处理。

警告

在 Prisma Client 中模拟引用完整性和引用操作会带来性能影响。在底层数据库支持外键的情况下,通常首选外键。

如何在您的 Prisma schema 中设置关系模式

要设置关系模式,请在 datasource 块中添加 relationMode 字段

schema.prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
信息

设置关系模式的功能作为 referentialIntegrity 预览功能的一部分在 Prisma ORM 3.1.1 版本中引入,并在 Prisma ORM 4.8.0 及更高版本中普遍可用。

relationMode 字段在 Prisma ORM 4.5.0 版本中已重命名,之前名为 referentialIntegrity

对于关系型数据库,可用选项为

  • foreignKeys:通过外键处理数据库中的关系。这是所有关系型数据库连接器的默认选项,如果 datasource 块中未明确设置 relationMode,则此选项处于活动状态。
  • prisma:在 Prisma Client 中模拟关系。当您使用 MySQL 连接器与 PlanetScale 数据库,并且在 PlanetScale 数据库设置中未启用原生外键约束时,您也应该启用此选项

对于 MongoDB,唯一可用的选项是 prisma 关系模式。如果 datasource 块中未明确设置 relationMode,此模式也处于活动状态。

警告

如果您在关系模式之间切换,Prisma ORM 将在下次使用 Prisma Migrate 或 db push 应用 schema 更改时,向数据库添加或删除外键。有关更多信息,请参阅在关系模式之间切换

使用 foreignKeys 关系模式处理关系型数据库中的关系

foreignKeys 关系模式通过外键处理关系型数据库中的关系。这是您使用关系型数据库连接器(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)时的默认选项。

使用 MongoDB 连接器时,foreignKeys 关系模式不可用。一些关系型数据库,例如 PlanetScale,也禁止使用外键。在这些情况下,您应该改为使用 prisma 关系模式在 Prisma ORM 中模拟关系

引用完整性

foreignKeys 关系模式在数据库级别通过外键约束和引用操作维护引用完整性。

外键约束

当您创建更新一个与另一条记录存在关系的记录时,相关记录必须存在。外键约束在数据库中强制执行此行为。如果记录不存在,数据库将返回错误消息。

引用操作

当您更新删除一个与另一条记录存在关系的记录时,数据库中会触发引用操作。为了维护相关记录的引用完整性,引用操作会阻止破坏引用完整性的更改,将更改级联到相关记录,或者将引用已更新或已删除记录的字段值设置为 null 或默认值。

有关更多信息,请参阅引用操作页面。

内省

当您使用 db pull 命令并在启用 foreignKeys 关系模式的情况下内省关系型数据库时,对于存在外键的关系,您的 Prisma schema 中将添加一个 @relation 属性。

Prisma Migrate 和 db push

当您使用 Prisma Migrate 或 db push 并在启用 foreignKeys 关系模式的情况下将更改应用于您的 Prisma schema 时,数据库中将为 schema 中的所有 @relation 属性创建外键。

使用 prisma 关系模式在 Prisma ORM 中模拟关系

prisma 关系模式通过一些额外的数据库查询和逻辑,为每个 Prisma Client 查询模拟某些外键约束和引用操作,以维护引用完整性。

prisma 关系模式是 MongoDB 连接器的默认选项。如果您使用的关系型数据库不支持外键,也应设置此模式。例如,如果您使用 PlanetScale 且没有外键约束,则应使用 prisma 关系模式。

警告

在 Prisma Client 中模拟引用完整性会带来性能影响,因为它会使用额外的数据库查询来维护引用完整性。在底层数据库可以使用外键处理引用完整性的情况下,通常首选外键。

关系的模拟仅适用于 Prisma Client 查询,不适用于原生查询。

哪些外键约束被模拟?

当您更新记录时,Prisma ORM 将模拟外键约束。这意味着当您更新一条与另一条记录存在关系的记录时,相关记录必须存在。如果记录不存在,Prisma Client 将返回错误消息。

然而,当您创建记录时,Prisma ORM 不会模拟任何外键约束。您将能够创建无效数据。

哪些引用操作被模拟?

当您更新删除一条带有相关记录的记录时,Prisma ORM 将模拟引用操作。

下表显示了每个数据库连接器可用的模拟引用操作

数据库级联限制无动作设为空设为默认
PostgreSQL✔️✔️✔️
MySQL✔️✔️✔️✔️
SQLite✔️✔️✔️
SQL Server✔️✔️✔️✔️
CockroachDB✔️✔️✔️✔️
MongoDB✔️✔️✔️✔️
  • prisma 关系模式不支持 SetDefault 引用操作。
  • ‡ PostgreSQL 和 SQLite 的 prisma 关系模式不支持 NoAction 引用操作。请改为使用 Restrict 操作。

错误消息

prisma 关系模式中由模拟约束和引用操作返回的错误消息由 Prisma Client 生成,与 foreignKeys 关系模式中的错误消息略有不同

Example:
// foreignKeys:
... Foreign key constraint failed on the field: `ProfileOneToOne_userId_fkey (index)`
// prisma:
... The change you are trying to make would violate the required relation 'ProfileOneToOneToUserOneToOne' between the `ProfileOneToOne` and `UserOneToOne` models.

内省

当您使用 db pull 命令并在启用 prisma 关系模式的情况下内省数据库时,关系将不会自动添加到您的 schema 中。您将需要手动使用 @relation 属性添加任何关系。这只需要做一次——下次您内省数据库时,Prisma ORM 将保留您添加的 @relation 属性。

Prisma Migrate 和 db push

当您使用 Prisma Migrate 或 db push 并在启用 prisma 关系模式的情况下将更改应用于您的 Prisma schema 时,Prisma ORM 将不会在您的数据库中使用外键。

索引

在使用外键约束的关系型数据库中,数据库通常也会隐式地为外键列创建索引。例如,MySQL 将在外键列上创建索引。这是为了让外键检查能够快速运行,且不需要进行全表扫描。

prisma 关系模式不使用外键,因此当您使用 Prisma Migrate 或 db push 将更改应用于数据库时,不会创建索引。您需要手动使用 @@index 属性(或在适用情况下使用 @unique@@unique@@id 属性)在您的关系标量字段上添加索引。

索引验证

如果您不手动添加索引,查询可能需要进行全表扫描。这可能会很慢,并且对于按访问行数计费的数据库提供商来说也可能很昂贵。为了帮助避免这种情况,当您的 schema 包含用于 @relation 但未定义索引的字段时,Prisma ORM 会发出警告。例如,以下 schema 中包含 UserPost 模型之间的关系

schema.prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}

model User {
id Int @id
posts Post[]
}

model Post {
id Int @id
userId Int
user User @relation(fields: [userId], references: [id])
}

当您运行 prisma formatprisma validate 时,Prisma ORM 会显示以下警告

With `relationMode = "prisma"`, no foreign keys are used, so relation fields will not benefit from the index usually created by the relational database under the hood. This can lead to poor performance when querying these fields. We recommend adding an index manually.

要解决此问题,请在您的 Post 模型中添加索引

schema.prisma
model Post {
id Int @id
userId Int
user User @relation(fields: [userId], references: [id])

@@index([userId])
}

如果您使用 Prisma VS Code 扩展(或我们在其他编辑器中的语言服务器),该警告会通过一个快速修复来增强,为您添加所需的索引。

The Quick Fix pop-up for adding an index on a relation scalar field in VS Code

在关系模式之间切换

只有在使用关系型数据库连接器(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)时,才可以在关系模式之间切换。

foreignKeys 切换到 prisma

如果您使用关系型数据库且未在 datasource 块中包含 relationMode 字段,则默认关系模式为 foreignKeys。要切换到 prisma 关系模式,请添加值为 prismarelationMode 字段,如果该字段已存在,则将其值更新为 prisma

当您将关系模式从 foreignKeys 切换到 prisma 后,首次使用 Prisma Migrate 或 db push 应用 schema 更改时,Prisma ORM 将在下一次迁移中删除所有先前创建的外键。

如果您保留相同的数据库,则可以正常继续工作。如果您切换到根本不支持外键的数据库,您现有的迁移历史将包含创建外键的 SQL DDL,如果您必须重新运行这些迁移,这可能会触发错误。在这种情况下,我们建议您删除 migrations 目录。(如果您使用 PlanetScale,它不支持外键,我们通常建议您使用 db push 而非 Prisma Migrate。)

prisma 切换到 foreignKeys

要从 prisma 关系模式切换到 foreignKeys 关系模式,请将 relationMode 字段值从 prisma 更新为 foreignKeys。为此,数据库必须支持外键。当您在切换关系模式后首次使用 Prisma Migrate 或 db push 应用 schema 更改时,Prisma ORM 将在下一次迁移中为所有关系创建外键。

© . All rights reserved.