跳到主要內容

關係故障排除

建模您的 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)

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 Schema 中關係欄位的字典序發生了變化,但資料庫中的列 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 表示)都沒有主鍵。

解決方案

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

© . This site is unofficial and not affiliated with Prisma Data, Inc.