自关系与多重关系消歧
本教程共 54 篇 · 第 13 篇 · 更新于 2026-08-11 · 约 4 分钟阅读
本节目标:掌握关系字段指向自身模型的三种写法,学会用 @relation(“名字”) 区分多条关系。
员工有上级,上级也是员工。分类能嵌套子分类,子分类还是分类。这种关系字段指向自己所属模型的关系,叫自关系(self-relation)。它同样有一对一、一对多、多对多三种形态,而且必须写 @relation 属性。
一对一自关系:前任与继任
博客的站长会换届。每个用户最多有一个继任者,也最多有一个前任:
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
三个要点:
- 两侧字段用同一个关系名「BlogOwnerHistory」
- 只有一侧(successor)完整标注 fields 和 references
- 外键列 successorId 加
@unique,保证一对一
还有个隐含规则:两侧不能都必填。否则第一条记录都插不进去——先有鸡还是先有蛋?
一对多自关系:师生与树形分类
老师带多个学生,学生只有一个老师:
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}
teacherId 不加 @unique,多个学生可以共享同一个老师。查询时用 include 带上 students 即可。
树形结构是它最经典的应用。无限层级的分类目录:
model Category {
id Int @id @default(autoincrement())
name String
parentId Int?
parent Category? @relation("CategoryTree", fields: [parentId], references: [id])
children Category[] @relation("CategoryTree")
}
parentId 为空表示顶级分类。递归地 include children,就能取出整棵树:
const tree = await prisma.category.findMany({
where: { parentId: null },
include: { children: true },
});
Note递归层级多时,嵌套 include 会变成 N+1 查询。真要做多级树,建议先取全表,在内存里组装。
多对多自关系:关注与粉丝
用户互相关注,是典型的多对多自关系。关系数据库里可以直接隐式建模:
model User {
id Int @id @default(autoincrement())
name String?
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
关系表由 Prisma 自动管理。想记录关注时间这类附加字段,就显式化:
model User {
id Int @id @default(autoincrement())
name String?
followedBy Follows[] @relation("followedBy")
following Follows[] @relation("following")
}
model Follows {
followedBy User @relation("followedBy", fields: [followedById], references: [id])
followedById Int
following User @relation("following", fields: [followingId], references: [id])
followingId Int
@@id([followingId, followedById])
}
同一模型上还能叠加多个自关系。User 既当师生,又玩关注,只要关系名各不相同,就能和平共处。
多重关系消歧:同名模型之间的多条关系
不止自关系需要命名。两个模型之间存在多条关系时,同样要用关系名区分。比如「作者」和「置顶」都是 User 与 Post 之间的关联:
model User {
id Int @id @default(autoincrement())
writtenPosts Post[] @relation("WrittenPosts")
pinnedPost Post? @relation("PinnedPost")
}
model Post {
id Int @id @default(autoincrement())
author User @relation("WrittenPosts", fields: [authorId], references: [id])
authorId Int
pinnedBy User? @relation("PinnedPost", fields: [pinnedById], references: [id])
pinnedById Int? @unique
}
规则与自关系完全一致:成对的关系字段名字必须相同;带外键的那一侧写全 fields 和 references。
Tip关系名只存在于 Schema 里,不落数据库。它相当于给关系贴的标签,Prisma 靠它把两侧字段配对。名字本身可以随意起,成对一致就行。
参考来源
- Prisma 官方文档:Self-relations
- Prisma 官方文档:Relations(@relation 属性与消歧)