跳到主要內容

關聯

關聯是 Prisma 結構描述中兩個模型之間的連接。例如,UserPost 之間存在一對多關聯,因為一個使用者可以擁有多個部落格文章。

以下 Prisma 結構描述定義了 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 結構描述中,外鍵/主鍵關係由 author 欄位上的 @relation 屬性表示

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

注意:Prisma 結構描述中的關聯代表資料庫中表格之間存在的關係。如果資料庫中不存在關係,則 Prisma 結構描述中也不存在關係。

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 結構描述產生的。以下範例示範當您使用 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. 建立一個具有自動產生 idUser(例如,20
  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 結構描述包含每種關聯類型

  • 一對一: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[]
}
資訊

此結構描述與範例資料模型相同,但已移除所有純量欄位(除了必要的關聯純量欄位),以便您可以專注於關聯欄位

資訊

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

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

對於關聯式資料庫,以下實體關聯圖代表與範例 Prisma 結構描述對應的資料庫

The sample schema as an entity relationship diagram

對於 MongoDB,Prisma ORM 使用標準化的資料模型設計,這表示文件透過 ID 互相參考,方式與關聯式資料庫類似。請參閱MongoDB 章節以取得更多詳細資訊。

隱含和顯式多對多關聯

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

  • 顯式多對多關聯,其中關聯表格在您的 Prisma 結構描述中表示為顯式模型
  • 隱含多對多關聯,其中 Prisma ORM 管理關聯表格,且該表格不會出現在 Prisma 結構描述中。

隱含多對多關聯要求兩個模型都具有單一 @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
}