跳到主要內容

關係疑難排解

模型化您的結構描述有時可能會產生一些意想不到的結果。本節旨在涵蓋其中最突出的問題。

如果關係欄位的順序變更,隱式多對多自反關係會傳回不正確的資料

問題

在以下隱式多對多自反關係中,a_eats (1) 和 b_eatenBy (2) 中關係欄位的詞彙順序

model Animal {
id Int @id @default(autoincrement())
name String
a_eats Animal[] @relation(name: "FoodChain")
b_eatenBy Animal[] @relation(name: "FoodChain")
}

SQL 中產生的關係表如下所示,其中 A 代表獵物 (a_eats),而 B 代表掠食者 (b_eatenBy)

AB
8 (浮游生物)7 (鮭魚)
7 (鮭魚)9 (熊)

以下查詢傳回鮭魚的獵物和掠食者

const getAnimals = await prisma.animal.findMany({
where: {
name: 'Salmon',
},
include: {
b_eats: true,
a_eatenBy: true,
},
})
顯示查詢結果
{
"id": 7,
"name": "Salmon",
"b_eats": [
{
"id": 8,
"name": "Plankton"
}
],
"a_eatenBy": [
{
"id": 9,
"name": "Bear"
}
]
}

現在變更關係欄位的順序

model Animal {
id Int @id @default(autoincrement())
name String
b_eats Animal[] @relation(name: "FoodChain")
a_eatenBy Animal[] @relation(name: "FoodChain")
}

遷移您的變更並重新產生 Prisma Client。當您使用更新後的欄位名稱執行相同的查詢時,Prisma Client 會傳回不正確的資料(鮭魚現在吃熊,並被浮游生物吃掉)

const getAnimals = await prisma.animal.findMany({
where: {
name: 'Salmon',
},
include: {
b_eats: true,
a_eatenBy: true,
},
})
顯示查詢結果
{
"id": 1,
"name": "Salmon",
"b_eats": [
{
"id": 3,
"name": "Bear"
}
],
"a_eatenBy": [
{
"id": 2,
"name": "Plankton"
}
]
}

儘管 Prisma 結構描述中關係欄位的詞彙順序已變更,但資料庫中的欄 AB並未變更(它們沒有被重新命名,資料也沒有被移動)。因此,A 現在代表掠食者 (a_eatenBy),而 B 代表獵物 (b_eats)

AB
8 (浮游生物)7 (鮭魚)
7 (鮭魚)9 (熊)

解決方案

如果您在隱式多對多自反關係中重新命名關係欄位,請確保您保持欄位的字母順序 - 例如,使用 a__b 作為前綴。

如何在多對多關係中使用關係表

有幾種方法可以定義 m-n 關係,隱式或顯式。隱式意味著讓 Prisma ORM 在底層處理關係表(JOIN 表),您只需為每個模型上的非純量類型定義一個陣列/列表,請參閱隱式多對多關係

當建立顯式 m-n 關係時,您可能會遇到麻煩,也就是說,自行建立和處理關係表。可能會忽略 Prisma ORM 要求關係的雙方都必須存在

以下面的範例為例,此處建立一個關係表以充當 PostCategory 表之間的 JOIN。但是,這將無法運作,因為關係表 (PostCategories) 必須分別與其他兩個模型形成一對多關係。

PostPostCategories 以及從 CategoryPostCategories 模型中缺少反向關係欄位。

// This example schema shows how NOT to define an explicit m-n relation

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] // This should refer to PostCategories
}

model PostCategories {
post Post @relation(fields: [postId], references: [id])
postId Int
category Category @relation(fields: [categoryId], references: [id])
categoryId Int
@@id([postId, categoryId])
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] // This should refer to PostCategories
}

為了修正這個問題,Post 模型需要定義與關係表 PostCategories 的多對多關係欄位。Category 模型也適用相同的情況。

這是因為關係模型與其聯結的其他兩個模型形成一對多關係。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[]
postCategories PostCategories[]
}

model PostCategories {
post Post @relation(fields: [postId], references: [id])
postId Int
category Category @relation(fields: [categoryId], references: [id])
categoryId Int

@@id([postId, categoryId])
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
postCategories PostCategories[]
}

在多對多關係中使用 @relation 屬性

當組成隱式多對多關係時,在模型上的關係欄位新增 @relation("Post") 註解似乎是合乎邏輯的。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("Category")
Category Category? @relation("Post", fields: [categoryId], references: [id])
categoryId Int?
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("Post")
Post Post? @relation("Category", fields: [postId], references: [id])
postId Int?
}

但是,這會告訴 Prisma ORM 預期兩個獨立的一對多關係。有關使用 @relation 屬性的更多資訊,請參閱消除關係歧義

以下範例是定義隱式多對多關係的正確方法。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("Category")
categories Category[]
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("Post")
posts Post[]
}

@relation 註解也可用於命名在隱式多對多關係中建立的底層關係表

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("CategoryPostRelation")
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("CategoryPostRelation")
}

在具有強制主鍵的資料庫中使用 m-n 關係

問題

某些雲端供應商強制要求所有表格中都存在主鍵。但是,Prisma ORM 針對使用隱式語法的多對多關係建立的任何關係表(JOIN 表)(透過 @relation 表示)都沒有主鍵。

解決方案

您需要使用顯式關係語法,手動建立聯結模型,並驗證此聯結模型是否具有主鍵。