關係疑難排解
模型化您的結構描述有時可能會產生一些意想不到的結果。本節旨在涵蓋其中最突出的問題。
如果關係欄位的順序變更,隱式多對多自反關係會傳回不正確的資料
問題
在以下隱式多對多自反關係中,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
)
A | B |
---|---|
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 結構描述中關係欄位的詞彙順序已變更,但資料庫中的欄 A
和 B
並未變更(它們沒有被重新命名,資料也沒有被移動)。因此,A
現在代表掠食者 (a_eatenBy
),而 B
代表獵物 (b_eats
)
A | B |
---|---|
8 (浮游生物) | 7 (鮭魚) |
7 (鮭魚) | 9 (熊) |
解決方案
如果您在隱式多對多自反關係中重新命名關係欄位,請確保您保持欄位的字母順序 - 例如,使用 a_
和 _b
作為前綴。
如何在多對多關係中使用關係表
有幾種方法可以定義 m-n 關係,隱式或顯式。隱式意味著讓 Prisma ORM 在底層處理關係表(JOIN 表),您只需為每個模型上的非純量類型定義一個陣列/列表,請參閱隱式多對多關係。
當建立顯式 m-n 關係時,您可能會遇到麻煩,也就是說,自行建立和處理關係表。可能會忽略 Prisma ORM 要求關係的雙方都必須存在。
以下面的範例為例,此處建立一個關係表以充當 Post
和 Category
表之間的 JOIN。但是,這將無法運作,因為關係表 (PostCategories
) 必須分別與其他兩個模型形成一對多關係。
從 Post
到 PostCategories
以及從 Category
到 PostCategories
模型中缺少反向關係欄位。
// 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
表示)都沒有主鍵。
解決方案
您需要使用顯式關係語法,手動建立聯結模型,並驗證此聯結模型是否具有主鍵。