跳到主要內容

關係

關係是 Prisma schema 中兩個模型之間的*連線*。例如,UserPost 之間存在一對多關係,因為一個使用者可以擁有多篇部落格文章。

以下 Prisma schema 定義了 UserPost 模型之間的一對多關係。定義關係所涉及的欄位已突出顯示

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

model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)

title String
}

在 Prisma ORM 層面,User / Post 關係由以下部分組成:

  • 兩個關係欄位authorposts。關係欄位在 Prisma ORM 層面定義模型之間的連線,並且**不存在於資料庫中**。這些欄位用於生成 Prisma Client。
  • 標量 authorId 欄位,它由 @relation 屬性引用。此欄位**存在於資料庫中**——它是連線 PostUser 的外部索引鍵。

在 Prisma ORM 層面,兩個模型之間的連線**總是**透過關係**兩端**的關係欄位來表示。

資料庫中的關係

關係型資料庫

以下實體關係圖定義了**關係型資料庫**中 UserPost 表之間相同的一對多關係

A one-to-many relationship between a user and posts table.

在 SQL 中,您使用*外部索引鍵*在兩個表之間建立關係。外部索引鍵儲存在關係**一側**。我們的示例由以下部分組成:

  • Post 表中名為 authorId 的外部索引鍵列。
  • User 表中名為 id 的主鍵列。Post 表中的 authorId 列引用 User 表中的 id 列。

在 Prisma schema 中,外部索引鍵/主鍵關係由 author 欄位上的 @relation 屬性表示

author     User        @relation(fields: [authorId], references: [id])

注意:Prisma schema 中的關係表示資料庫中表之間存在的關聯。如果關係不存在於資料庫中,則它也不存在於 Prisma schema 中。

MongoDB

對於 MongoDB,Prisma ORM 當前使用規範化資料模型設計,這意味著文件之間透過 ID 互相引用,方式與關係型資料庫類似。

以下文件表示一個 User(在 User 集合中)

{ "_id": { "$oid": "60d5922d00581b8f0062e3a8" }, "name": "Ella" }

以下 Post 文件列表(在 Post 集合中)中的每個文件都有一個 authorId 欄位,該欄位引用同一個使用者

[
{
"_id": { "$oid": "60d5922e00581b8f0062e3a9" },
"title": "How to make sushi",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
},
{
"_id": { "$oid": "60d5922e00581b8f0062e3aa" },
"title": "How to re-install Windows",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
}
]

此資料結構表示一對多關係,因為多個 Post 文件引用同一個 User 文件。

ID 和關係標量欄位上的 @db.ObjectId

如果模型的 ID 是 ObjectId(由 String 欄位表示),則必須將 @db.ObjectId 新增到模型的 ID 以及關係另一側的關係標量欄位。

model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}

model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)

title String
}

Prisma Client 中的關係

Prisma Client 是從 Prisma schema 生成的。以下示例演示了當您使用 Prisma Client 獲取、建立和更新記錄時,關係如何體現。

建立記錄和巢狀記錄

以下查詢建立了一個 User 記錄和兩個連線的 Post 記錄

const userAndPosts = await prisma.user.create({
data: {
posts: {
create: [
{ title: 'Prisma Day 2020' }, // Populates authorId with user's id
{ title: 'How to write a Prisma schema' }, // Populates authorId with user's id
],
},
},
})

在底層資料庫中,此查詢

  1. 建立一個具有自動生成 id(例如,20)的 User
  2. 建立兩個新的 Post 記錄,並將兩個記錄的 authorId 設定為 20

以下查詢透過 id 檢索 User,幷包含任何相關的 Post 記錄

const getAuthor = await prisma.user.findUnique({
where: {
id: "20",
},
include: {
posts: true, // All posts where authorId == 20
},
});

在底層資料庫中,此查詢

  1. 檢索 id20User 記錄
  2. 檢索所有 authorId20Post 記錄

將現有記錄與另一個現有記錄關聯

以下查詢將現有 Post 記錄與現有 User 記錄關聯

const updateAuthor = await prisma.user.update({
where: {
id: 20,
},
data: {
posts: {
connect: {
id: 4,
},
},
},
})

在底層資料庫中,此查詢使用巢狀的 connect 查詢id 為 4 的帖子連結到 id 為 20 的使用者。該查詢透過以下步驟完成此操作:

  • 查詢首先查詢 id20 的使用者。
  • 然後,查詢將 authorID 外部索引鍵設定為 20。這將 id4 的帖子連結到 id20 的使用者。

在此查詢中,authorID 的當前值無關緊要。無論其當前值如何,查詢都會將 authorID 更改為 20

關係型別

Prisma ORM 中有三種不同型別(或基數)的關係:

以下 Prisma schema 包含每種型別的關係

  • 一對一:UserProfile
  • 一對多:UserPost
  • 多對多:PostCategory
model User {
id Int @id @default(autoincrement())
posts Post[]
profile Profile?
}

model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int @unique // relation scalar field (used in the `@relation` attribute above)
}

model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int // relation scalar field (used in the `@relation` attribute above)
categories Category[]
}

model Category {
id Int @id @default(autoincrement())
posts Post[]
}
資訊

此 schema 與示例資料模型相同,但已移除所有標量欄位(必需的關係標量欄位除外),因此您可以專注於關係欄位

資訊

此示例使用隱式多對多關係。除非您需要消除關係歧義,否則這些關係不需要 @relation 屬性。

請注意,關係型資料庫和 MongoDB 之間的語法略有不同——特別是對於多對多關係

對於關係型資料庫,以下實體關係圖表示與示例 Prisma schema 對應的資料庫

The sample schema as an entity relationship diagram

對於 MongoDB,Prisma ORM 使用規範化資料模型設計,這意味著文件之間透過 ID 互相引用,方式與關係型資料庫類似。有關更多詳細資訊,請參閱MongoDB 部分

隱式和顯式多對多關係

關係型資料庫中的多對多關係可以透過兩種方式建模

隱式多對多關係要求兩個模型都具有單個 @id。請注意以下事項:

  • 您不能使用多欄位 ID
  • 您不能使用 @unique 代替 @id

要使用這些功能中的任何一個,您必須設定顯式多對多關係。

隱式多對多關係仍然在底層資料庫中體現為關係表。但是,Prisma ORM 管理此關係表。

如果您使用隱式多對多關係而不是顯式關係,它會使Prisma Client API 更簡單(例如,您在巢狀寫入中的巢狀級別會減少一級)。

如果您不使用 Prisma Migrate,而是從內省獲取資料模型,您仍然可以遵循 Prisma ORM 的關係表約定來使用隱式多對多關係。

關係欄位

關係欄位是 Prisma 模型上的欄位,其型別*不是* 標量型別。相反,它們的型別是另一個模型。

每個關係必須正好有兩個關係欄位,每個模型上一個。在一對一和一對多關係的情況下,需要一個額外的*關係標量欄位*,它透過 @relation 屬性中的兩個關係欄位之一進行連結。此關係標量欄位是底層資料庫中*外部索引鍵*的直接表示。

model User {
id Int @id @default(autoincrement())
email String @unique
role Role @default(USER)
posts Post[] // relation field (defined only at the Prisma ORM level)
}

model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id]) // relation field (uses the relation scalar field `authorId` below)
authorId Int // relation scalar field (used in the `@relation` attribute above)
}

postsauthor 都是關係欄位,因為它們的型別不是標量型別,而是其他模型。

另請注意,帶註解的關係欄位 author 需要在 @relation 屬性中連結 Post 模型上的關係標量欄位 authorId。關係標量欄位表示底層資料庫中的外部索引鍵。

兩個關係欄位(即 postsauthor)都純粹在 Prisma ORM 層面定義,它們不會在資料庫中體現。

帶註解的關係欄位

需要關係的一側用 @relation 屬性*註解*的關係被稱為*帶註解的關係欄位*。這包括:

  • 一對一關係
  • 一對多關係
  • 僅適用於 MongoDB 的多對多關係

@relation 屬性註解的關係側表示**在底層資料庫中儲存外部索引鍵**的一側。代表外部索引鍵的“實際”欄位在該關係側也需要,它被稱為*關係標量欄位*,並在 @relation 屬性內部被引用。

author     User    @relation(fields: [authorId], references: [id])
authorId Int

當標量欄位在 @relation 屬性的 fields 中使用時,它*成為*關係標量欄位。

關係標量欄位

關係標量欄位命名約定

因為關係標量欄位始終*屬於*一個關係欄位,所以以下命名約定很常見:

  • 關係欄位:author
  • 關係標量欄位:authorId(關係欄位名 + Id

@relation 屬性

@relation 屬性只能應用於關係欄位,不能應用於標量欄位

在以下情況下需要 @relation 屬性:

  • 您定義一對一或一對多關係時,它在關係的*一側*是必需的(帶有相應的關係標量欄位)
  • 您需要消除關係歧義時(例如,當兩個模型之間存在兩個關係時)
  • 您定義自關係
  • 您定義適用於 MongoDB 的多對多關係
  • 您需要控制關係表在底層資料庫中的表示方式時(例如,為關係表使用特定名稱)

注意:關係型資料庫中的隱式多對多關係不需要 @relation 屬性。

消除關係歧義

當您在相同的兩個模型之間定義兩個關係時,您需要在 @relation 屬性中新增 name 引數來消除它們的歧義。例如,考慮以下模型,以說明為什麼需要這樣做:

// NOTE: This schema is intentionally incorrect. See below for a working solution.

model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[]
pinnedPost Post?
}

model Post {
id Int @id @default(autoincrement())
title String?
author User @relation(fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation(fields: [pinnedById], references: [id])
pinnedById Int?
}

在這種情況下,關係是模糊的,有四種不同的解釋方式:

  • User.writtenPostsPost.author + Post.authorId
  • User.writtenPostsPost.pinnedBy + Post.pinnedById
  • User.pinnedPostPost.author + Post.authorId
  • User.pinnedPostPost.pinnedBy + Post.pinnedById

為了消除這些關係的歧義,您需要使用 @relation 屬性註解關係欄位並提供 name 引數。您可以設定任何 name(空字串 "" 除外),但它在關係的兩側必須相同。

model User {
id Int @id @default(autoincrement())
name String?
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}

model Post {
id Int @id @default(autoincrement())
title String?
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById Int? @unique
}
© . This site is unofficial and not affiliated with Prisma Data, Inc.