關聯
關聯是 Prisma 結構描述中兩個模型之間的連接。例如,User
和 Post
之間存在一對多關聯,因為一個使用者可以擁有多個部落格文章。
以下 Prisma 結構描述定義了 User
和 Post
模型之間的一對多關聯。定義關聯所涉及的欄位已突出顯示
- 關聯式資料庫
- MongoDB
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
}
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 ORM 層級,User
/ Post
關聯由以下組成
- 兩個關聯欄位:
author
和posts
。關聯欄位定義 Prisma ORM 層級模型之間的連接,且不存在於資料庫中。這些欄位用於產生 Prisma Client。 - 純量
authorId
欄位,由@relation
屬性參考。此欄位確實存在於資料庫中 - 它是連接Post
和User
的外鍵。
在 Prisma ORM 層級,兩個模型之間的連接始終由關聯的每一側上的關聯欄位表示。
資料庫中的關聯
關聯式資料庫
以下實體關聯圖定義了 關聯式資料庫中 User
和 Post
表格之間相同的多對一關聯
在 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
],
},
},
})
在底層資料庫中,此查詢
- 建立一個具有自動產生
id
的User
(例如,20
) - 建立兩個新的
Post
記錄,並將兩個記錄的authorId
設定為20
檢索記錄並包含相關記錄
以下查詢依 id
檢索 User
,並包含任何相關的 Post
記錄
const getAuthor = await prisma.user.findUnique({
where: {
id: "20",
},
include: {
posts: true, // All posts where authorId == 20
},
});
在底層資料庫中,此查詢
- 檢索
id
為20
的User
記錄 - 檢索所有
authorId
為20
的Post
記錄
將現有記錄與另一個現有記錄關聯
以下查詢將現有的 Post
記錄與現有的 User
記錄關聯
const updateAuthor = await prisma.user.update({
where: {
id: 20,
},
data: {
posts: {
connect: {
id: 4,
},
},
},
})
在底層資料庫中,此查詢使用巢狀 connect
查詢,將 id
為 4 的文章連結到 id
為 20 的使用者。此查詢透過以下步驟執行此操作
- 查詢首先尋找
id
為20
的使用者。 - 然後,查詢將
authorID
外鍵設定為20
。這會將id
為4
的文章連結到id
為20
的使用者。
在此查詢中,authorID
的目前值並不重要。無論目前值為何,查詢都會將 authorID
變更為 20
。
關聯類型
Prisma ORM 中有三種不同的關聯類型(或基數)
以下 Prisma 結構描述包含每種關聯類型
- 一對一:
User
↔Profile
- 一對多:
User
↔Post
- 多對多:
Post
↔Category
- 關聯式資料庫
- MongoDB
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[]
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
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)
categories Category[] @relation(fields: [categoryIds], references: [id])
categoryIds String[] @db.ObjectId
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[] @relation(fields: [postIds], references: [id])
postIds String[] @db.ObjectId
}
請注意,關聯式資料庫和 MongoDB 之間的語法略有不同 - 特別是對於多對多關聯。
對於關聯式資料庫,以下實體關聯圖代表與範例 Prisma 結構描述對應的資料庫
對於 MongoDB,Prisma ORM 使用標準化的資料模型設計,這表示文件透過 ID 互相參考,方式與關聯式資料庫類似。請參閱MongoDB 章節以取得更多詳細資訊。
隱含和顯式多對多關聯
關聯式資料庫中的多對多關聯可以透過兩種方式建模
隱含多對多關聯要求兩個模型都具有單一 @id
。請注意以下事項
- 您不能使用多欄位 ID
- 您不能使用
@unique
來取代@id
若要使用這些功能中的任一項,您必須設定顯式多對多關聯。
隱含多對多關聯仍然會在底層資料庫中的關聯表格中呈現。但是,Prisma ORM 管理此關聯表格。
如果您使用隱含多對多關聯而不是顯式關聯,則可以簡化Prisma Client API(例如,因為在巢狀寫入內,您會減少一層巢狀結構)。
如果您未使用 Prisma Migrate,而是從內省取得資料模型,您仍然可以透過遵循 Prisma ORM 的關聯表格慣例,利用隱含多對多關聯。
關聯欄位
關聯欄位是 Prisma 模型上不具有純量類型的欄位。相反地,它們的類型是另一個模型。
每個關聯都必須恰好有兩個關聯欄位,每個模型上各一個。在一對一和一對多關聯的情況下,需要額外的關聯純量欄位,該欄位由 @relation
屬性中的兩個關聯欄位之一連結。此關聯純量欄位是底層資料庫中外鍵的直接表示。
- 關聯式資料庫
- MongoDB
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)
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique
role Role @default(USER)
posts Post[] // relation field (defined only at the Prisma ORM level)
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
author User @relation(fields: [authorId], references: [id]) // relation field (uses the relation scalar field `authorId` below)
authorId String @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
posts
和 author
都是關聯欄位,因為它們的類型不是純量類型,而是其他模型。
另請注意,已註解的關聯欄位 author
需要在 @relation
屬性內連結 Post
模型上的關聯純量欄位 authorId
。關聯純量欄位代表底層資料庫中的外鍵。
關聯欄位(即 posts
和 author
)都是純粹在 Prisma ORM 層級定義的,它們不會在資料庫中呈現。
已註解的關聯欄位
需要關聯的一側使用 @relation
屬性註解的關聯稱為已註解的關聯欄位。這包括
- 一對一關聯
- 一對多關聯
- 僅適用於 MongoDB 的多對多關聯
使用 @relation
屬性註解的關聯側代表在底層資料庫中儲存外鍵的一側。表示外鍵的「實際」欄位也需要在關聯的該側,它稱為關聯純量欄位,並在 @relation
屬性內參考
- 關聯式資料庫
- MongoDB
author User @relation(fields: [authorId], references: [id])
authorId Int
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
當純量欄位用於 @relation
屬性的 fields
時,純量欄位會變成關聯純量欄位。
關聯純量欄位
關聯純量欄位命名慣例
由於關聯純量欄位始終屬於關聯欄位,因此以下命名慣例很常見
- 關聯欄位:
author
- 關聯純量欄位:
authorId
(關聯欄位名稱 +Id
)
@relation
屬性
@relation
屬性只能套用至關聯欄位,而不能套用至純量欄位。
在以下情況下,@relation
屬性是必要的
- 當您定義一對一或一對多關聯時,它在關聯的一側(與對應的關聯純量欄位)上是必要的
- 您需要消除關聯歧義(例如,當您在相同模型之間有兩個關聯時)
- 您定義自關聯
- 您定義MongoDB 的多對多關聯
- 您需要控制關聯表格在底層資料庫中的表示方式(例如,為關聯表格使用特定名稱)
注意:關聯式資料庫中的隱含多對多關聯不需要
@relation
屬性。
消除關聯歧義
當您在相同的兩個模型之間定義兩個關聯時,您需要在 @relation
屬性中新增 name
引數,以消除它們的歧義。作為說明為何需要這樣做的範例,請考慮以下模型
- 關聯式資料庫
- MongoDB
// 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?
}
// NOTE: This schema is intentionally incorrect. See below for a working solution.
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
writtenPosts Post[]
pinnedPost Post?
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String?
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
pinnedBy User? @relation(fields: [pinnedById], references: [id])
pinnedById String? @db.ObjectId
}
在這種情況下,關聯是模稜兩可的,有四種不同的方式來解釋它們
User.writtenPosts
↔Post.author
+Post.authorId
User.writtenPosts
↔Post.pinnedBy
+Post.pinnedById
User.pinnedPost
↔Post.author
+Post.authorId
User.pinnedPost
↔Post.pinnedBy
+Post.pinnedById
為了消除這些關聯的歧義,您需要使用 @relation
屬性註解關聯欄位,並提供 name
引數。您可以設定任何 name
(空字串 ""
除外),但它在關聯的兩側都必須相同
- 關聯式資料庫
- MongoDB
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
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String?
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId String @db.ObjectId
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById String? @unique @db.ObjectId
}