關係故障排除
建模您的 Schema 有時可能會產生一些意外結果。本節旨在涵蓋其中最突出的一些問題。
如果關係欄位的順序發生變化,隱式多對多自引用關係會返回不正確的資料
問題
在以下隱式多對多自引用關係中,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 Schema 中關係欄位的字典序發生了變化,但資料庫中的列 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 表示)都沒有主鍵。
解決方案
您需要使用顯式關係語法,手動建立聯結模型,並驗證此聯結模型具有主鍵。