关系模式
在 Prisma 架构中,记录之间的关系通过 @relation
属性定义。例如,在以下架构中,User
和 Post
模型之间存在一对多关系
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 有两种关系模式,foreignKeys
和 prisma
,它们指定如何强制执行记录之间的关系。
如果你将 Prisma ORM 与关系型数据库一起使用,那么默认情况下,Prisma ORM 使用 foreignKeys
关系模式,该模式使用外键在数据库级别强制执行记录之间的关系。外键是在一个表中的一列或一组列,其值基于另一个表中的主键。外键允许你
- 设置约束,以防止你进行破坏引用的更改
- 设置 参照操作,定义如何处理记录的更改
这些约束和参照操作共同保证数据的参照完整性。
对于上面的示例架构,如果你使用 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 DELETE
和 ON UPDATE
参照操作指定了 CASCADE
选项,这将同时删除或更新属于该用户的所有帖子。
一些数据库,例如 MongoDB 或 PlanetScale,不支持外键。此外,在某些情况下,开发人员可能更愿意在通常支持外键的关系型数据库中不使用外键。对于这些情况,Prisma ORM 提供了 prisma
关系模式,它模拟了关系型数据库中关系的一些属性。当你将 Prisma 客户端与启用了 prisma
关系模式一起使用时,查询的行为是相同或类似的,但参照操作和一些约束由 Prisma 引擎处理,而不是在数据库中处理。
在 Prisma 客户端中模拟参照完整性和参照操作存在性能影响。在底层数据库支持外键的情况下,这通常是首选。
如何在 Prisma 架构中设置关系模式
要设置关系模式,请在 datasource
块中添加 relationMode
字段
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
在 Prisma ORM 版本 3.1.1 中,设置关系模式的功能作为 referentialIntegrity
预览功能的一部分引入,并在 Prisma ORM 版本 4.8.0 及更高版本中普遍可用。relationMode
字段在 Prisma ORM 版本 4.5.0 中已重命名,以前称为 referentialIntegrity
。
对于关系型数据库,可用的选项是
foreignKeys
:这使用外键在数据库中处理关系。这是所有关系型数据库连接器的默认选项,如果在datasource
块中没有显式设置relationMode
,则该选项处于活动状态。prisma
:这在 Prisma 客户端中模拟关系。如果你在将 MySQL 连接器与 PlanetScale 数据库一起使用时没有在你的 PlanetScale 数据库设置中启用本机外键约束,你也应该 启用此选项。
对于 MongoDB,唯一可用的选项是 prisma
关系模式。如果在 datasource
块中没有显式设置 relationMode
,则该模式也处于活动状态。
如果你在关系模式之间切换,Prisma ORM 将在下一次使用 Prisma Migrate 或 db push
将更改应用到架构时,向你的数据库添加或删除外键。有关更多信息,请参见 在关系模式之间切换。
使用 foreignKeys
关系模式在关系型数据库中处理关系
foreignKeys
关系模式使用外键在关系型数据库中处理关系。这是当你使用关系型数据库连接器(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)时的默认选项。
当你使用 MongoDB 连接器时,foreignKeys
关系模式不可用。一些关系型数据库,例如 PlanetScale,也禁止使用外键。在这些情况下,你应该改为 在 Prisma ORM 中使用 prisma
关系模式模拟关系。
参照完整性
foreignKeys
关系模式使用外键约束和参照操作在数据库级别维护参照完整性。
外键约束
当你创建或更新与另一条记录有关系的记录时,相关记录需要存在。外键约束在数据库中强制执行此行为。如果记录不存在,数据库将返回错误消息。
参照操作
当你更新或删除与另一条记录有关系的记录时,参照操作将在数据库中触发。为了在相关记录中维护参照完整性,参照操作会阻止导致破坏参照完整性的更改,将更改级联到相关记录,或将引用已更新或已删除记录的字段的值设置为 null
或默认值。
有关更多信息,请参见 参照操作 页面。
内省
当你使用 db pull
命令内省启用了 foreignKeys
关系模式的关系型数据库时,将在你的 Prisma 架构中添加一个 @relation
属性,用于存在外键的关系。
Prisma Migrate 和 db push
当你使用 Prisma Migrate 或 db push
将更改应用到启用了 foreignKeys
关系模式的 Prisma 架构时,将为架构中的所有 @relation
属性在你的数据库中创建外键。
使用 prisma
关系模式在 Prisma ORM 中模拟关系
prisma
关系模式使用一些额外的数据库查询和逻辑来模拟每个 Prisma 客户端查询的外键约束和参照操作,以维护参照完整性。
prisma
关系模式是 MongoDB 连接器的默认选项。如果你使用不支持外键的关系型数据库,也应该设置它。例如,如果你使用 PlanetScale 但没有外键约束,你应该使用 prisma
关系模式。
在 Prisma 客户端中模拟参照完整性存在性能影响,因为它使用额外的数据库查询来维护参照完整性。在底层数据库可以使用外键处理参照完整性的情况下,这通常是首选。
模拟关系仅适用于 Prisma 客户端查询,不适用于原始查询。
模拟哪些外键约束?
当你更新记录时,Prisma ORM 将模拟外键约束。这意味着,当你更新与另一条记录有关系的记录时,相关记录需要存在。如果记录不存在,Prisma 客户端将返回错误消息。
但是,当你创建记录时,Prisma ORM 不会模拟任何外键约束。你将能够创建无效数据。
模拟哪些参照操作?
当您更新或删除具有关联记录的记录时,Prisma ORM 将模拟参照操作。
下表显示了每个数据库连接器可用的模拟参照操作。
数据库 | 级联 | 限制 | 无操作 | 设为 null | 设为默认值 |
---|---|---|---|---|---|
PostgreSQL | ✔️ | ✔️ | ❌‡ | ✔️ | ❌† |
MySQL | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
SQLite | ✔️ | ✔️ | ❌‡ | ✔️ | ❌† |
SQL Server | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
CockroachDB | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
MongoDB | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
- † 在
prisma
关系模式中不支持SetDefault
参照操作。 - ‡ 在
prisma
关系模式中,PostgreSQL 和 SQLite 不支持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
关系模式内省数据库时,关系不会自动添加到您的模式中。您需要使用 @relation
属性手动添加所有关系。这只需要做一次 - 下次您内省数据库时,Prisma ORM 将保留您添加的 @relation
属性。
Prisma Migrate 和 db push
当您使用 Prisma Migrate 或 db push
并启用 prisma
关系模式将更改应用于 Prisma 模式时,Prisma ORM 不会在您的数据库中使用外键。
索引
在使用外键约束的关系数据库中,数据库通常还会隐式为外键列创建索引。例如,MySQL 将在所有外键列上创建索引。这是为了让外键检查能够快速运行,而不需要进行表扫描。
prisma
关系模式不使用外键,因此当您使用 Prisma Migrate 或 db push
将更改应用于数据库时,不会创建任何索引。您需要使用 @@index
属性(或 @unique
、@@unique
或 @@id
属性,如果适用)手动在关系标量字段上添加索引。
索引验证
如果您没有手动添加索引,查询可能需要进行全表扫描。这可能会很慢,并且在按访问行计费的数据库提供商上也很昂贵。为了避免这种情况,Prisma ORM 会在您的模式中包含在 @relation
中使用但没有定义索引的字段时向您发出警告。例如,请考虑以下模式,其中 User
和 Post
模型之间存在关系
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 format
或 prisma 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
模型中添加索引
model Post {
id Int @id
userId Int
user User @relation(fields: [userId], references: [id])
@@index([userId])
}
如果您使用 Prisma VS Code 扩展(或我们 在其他编辑器中的语言服务器),该警告将添加一个快速修复,为您添加所需的索引。
在关系模式之间切换
只有在使用关系数据库连接器(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)时才能在关系模式之间切换。
从 foreignKeys
切换到 prisma
如果您使用关系数据库并且没有在 datasource
块中包含 relationMode
字段,则默认关系模式为 foreignKeys
。要切换到 prisma
关系模式,请添加值为 prisma
的 relationMode
字段,或者如果 relationMode
字段已存在,则将其值更新为 prisma
。
当您将关系模式从 foreignKeys
切换到 prisma
后,在您第一次使用 Prisma Migrate 或 db push
将更改应用于模式后,Prisma ORM 将在下次迁移中删除所有之前创建的外键。
如果您保留相同的数据库,那么您可以继续正常工作。如果您切换到根本不支持外键的数据库,则您现有的迁移历史记录包含创建外键的 SQL DDL,如果需要重新运行这些迁移,这可能会引发错误。在这种情况下,我们建议您删除 migrations
目录。(如果您使用 PlanetScale(不支持外键),我们通常建议您 使用 db push
而不是 Prisma Migrate。)
从 prisma
切换到 foreignKeys
要从 prisma
关系模式切换到 foreignKeys
关系模式,请将 relationMode
字段的值从 prisma
更新为 foreignKeys
。为此,数据库必须支持外键。当您在切换关系模式后第一次使用 Prisma Migrate 或 db push
将更改应用于模式时,Prisma ORM 将在下次迁移中为所有关系创建外键。