跳至主要内容

关系故障排除

模式建模有时会产生一些意想不到的结果。本节旨在涵盖其中最突出的一些。

如果关系字段的顺序改变,隐式多对多自关系返回不正确的数据

问题

在以下隐式多对多自关系中,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)

一、B
8 (浮游生物)7 (鲑鱼)
7 (鲑鱼)9 (熊)

以下查询返回鲑鱼的猎物和捕食者

const getAnimals = await prisma.animal.findMany({
where: {
name: 'Salmon',
},
include: {
b_eats: true,
a_eatenBy: true,
},
})
显示query结果
{
"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,
},
})
显示query结果
{
"id": 1,
"name": "Salmon",
"b_eats": [
{
"id": 3,
"name": "Bear"
}
],
"a_eatenBy": [
{
"id": 2,
"name": "Plankton"
}
]
}

尽管 Prisma 模式中关系字段的词典顺序改变了,但数据库中的列 AB **没有改变**(它们没有被重命名,数据也没有移动)。因此,A 现在代表捕食者 (a_eatenBy),B 代表猎物 (b_eats)

一、B
8 (浮游生物)7 (鲑鱼)
7 (鲑鱼)9 (熊)

解决方案

如果您在隐式多对多自关系中重命名关系字段,请确保保持字段的字母顺序——例如,通过添加 a_b_ 前缀。

如何将关系表与多对多关系一起使用

定义多对多关系有几种方法,隐式或显式。隐式意味着让 Prisma ORM 在底层处理关系表 (JOIN 表),您所要做的就是在每个模型上为非标量类型定义一个数组/列表,请参阅隐式多对多关系

您可能会遇到问题的地方是创建显式多对多关系,即自己创建和处理关系表。**Prisma ORM 要求关系的双方都存在,这一点可能会被忽略**。

以下面的示例为例,这里创建了一个关系表作为 PostCategory 表之间的 JOIN。但这将不起作用,因为关系表 (PostCategories) 必须分别与另外两个模型形成一对多关系。

PostPostCategoriesCategoryPostCategories 模型缺少反向关系字段。

// 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")
}

在具有强制主键的数据库中使用多对多关系

问题

一些云提供商强制所有表都存在主键。但是,Prisma ORM(通过 @relation 表达)使用隐式语法为多对多关系创建的任何关系表 (JOIN 表) 都没有主键。

解决方案

您需要使用显式关系语法,手动创建连接模型,并验证此连接模型具有主键。

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