首页 / Prisma ORM 入门教程 / 自关系与多重关系消歧

Prisma ORM 入门教程

自关系与多重关系消歧

本教程共 54 篇 · 第 13 篇 · 更新于 2026-08-11 · 约 4 分钟阅读

Prisma自关系self-relation树形结构@relation关系消歧schema建模

本节目标:掌握关系字段指向自身模型的三种写法,学会用 @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 属性与消歧)