多对多关系的建模和查询
问题
在关系数据库中对多对多关系进行建模和查询可能具有挑战性。本文将展示使用 Prisma ORM 处理此问题的两个示例。第一个示例使用 隐式,而第二个示例使用 显式 多对多关系。
解决方案
隐式关系
这是一种多对多关系类型,其中 Prisma ORM 在内部处理 关系表。隐式多对多关系的基本示例如下
model Post {
id Int @id @default(autoincrement())
title String
tags Tag[]
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
要创建帖子及其标签,可以使用 Prisma Client 编写以下内容
await prisma.post.create({
data: {
title: 'Types of relations',
tags: { create: [{ name: 'dev' }, { name: 'prisma' }] },
},
})
在上面的示例中,我们可以直接查询帖子及其标签,如下所示
await prisma.post.findMany({
include: { tags: true },
})
获得的响应将是
[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"name": "dev"
},
{
"id": 2,
"name": "prisma"
}
]
}
]
另一个用例是,如果您想添加新标签以及将现有标签连接到帖子。例如,用户为帖子创建了新标签,并且还选择了要添加的现有标签。在这种情况下,我们可以通过以下方式执行此操作
await prisma.post.update({
where: { id: 1 },
data: {
title: 'Prisma is awesome!',
tags: { set: [{ id: 1 }, { id: 2 }], create: { name: 'typescript' } },
},
})
显式关系
当您需要在关系表中存储额外的字段,或者您正在 内省 已经设置了多对多关系的现有数据库时,通常需要创建显式关系。这是与上面相同的模式,但使用显式关系表
model Post {
id Int @id @default(autoincrement())
title String
tags PostTags[]
}
model PostTags {
id Int @id @default(autoincrement())
post Post? @relation(fields: [postId], references: [id])
tag Tag? @relation(fields: [tagId], references: [id])
postId Int?
tagId Int?
@@index([postId, tagId])
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts PostTags[]
}
将标签添加到帖子将是创建关系表(PostTags
)以及标签表(Tag
)中的内容
await prisma.post.create({
data: {
title: 'Types of relations',
tags: {
create: [
{ tag: { create: { name: 'dev' } } },
{ tag: { create: { name: 'prisma' } } },
],
},
},
})
另外,查询帖子及其标签需要额外的 include
,如下所示
await prisma.post.findMany({
include: { tags: { include: { tag: true } } },
})
这将提供以下输出
[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"postId": 1,
"tagId": 1,
"tag": {
"id": 1,
"name": "prisma"
}
},
{
"id": 2,
"postId": 1,
"tagId": 2,
"tag": {
"id": 2,
"name": "dev"
}
}
]
}
]
有时,在 UI 中显示关系表的数据并不理想。在这种情况下,最好在服务器端获取数据后对其进行映射,并将该响应发送到前端。
const result = posts.map((post) => {
return { ...post, tags: post.tags.map((tag) => tag.tag) }
})
这将提供与使用隐式关系获得的输出类似的输出。
[
{
"id": 1,
"title": "Types of relations",
"tags": [
{
"id": 1,
"name": "prisma"
},
{
"id": 2,
"name": "dev"
}
]
}
]
本文介绍了如何实现隐式和显式多对多关系,以及如何使用 Prisma Client 查询它们。