Prisma 中文文档 下载 App

模型关系:一对一、一对多、多对多怎么定义

关系的本质

在关系数据库里,表与表之间的关系通过外键实现。Prisma Schema 把这些关系翻译成更接近对象模型的声明式写法,同时在 Prisma Client 生成 include、select、嵌套写入等类型安全的查询 API。

一对多(最常见)

场景:一个作者可以有多篇文章,每篇文章只有一个作者。

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  posts Post[]           // Prisma 级关联字段
}

model Post {
  id       Int     @id @default(autoincrement())
  title    String
  author   User?   @relation(fields: [authorId], references: [id])
  authorId Int?              // 真实外键列
}

关键点:

Prisma Client 查询时可以这样用:

// 查询用户并包含他的文章
const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true },
})

// 嵌套写入:创建用户同时创建文章
await prisma.user.create({
  data: {
    email: 'alice@prisma.io',
    posts: { create: { title: '我的第一篇文章' } },
  },
})

一对一

场景:每个用户有且只有一份个人档案,每份档案只属于一个用户。

model User {
  id     Int     @id @default(autoincrement())
  email  String  @unique
  profile Profile?
}

model Profile {
  id     Int    @id @default(autoincrement())
  bio    String?
  userId Int    @unique
  user   User   @relation(fields: [userId], references: [id])
}

与一对多的区别是:

多对多

场景:一篇文章可以有多个标签,一个标签可以贴在多篇文章上。

Prisma 支持两种方式:隐式多对多(自动创建中间表)和 显式多对多(手动定义中间表 model)。

隐式多对多(推荐,简单场景)

model Post {
  id     Int      @id @default(autoincrement())
  title  String
  tags   Tag[]
}

model Tag {
  id    Int    @id @default(autoincrement())
  name  String @unique
  posts Post[]
}

你不需要自己建中间表。Prisma 会自动在数据库里创建一个隐式的中间表 _PostToTag,包含两列:A 和 B,分别指向 Post 和 Tag 的主键。

Client 查询用法:

// 创建 Post 并关联已有的 Tag
await prisma.post.create({
  data: {
    title: 'Prisma 上手教程',
    tags: { connect: { name: 'prisma' } },
  },
})

// 带 tags 查询
const post = await prisma.post.findUnique({
  where: { id: 1 },
  include: { tags: true },
})

显式多对多(中间表有额外字段时)

如果中间表本身需要额外信息(比如 assignedAt 标签分配时间),就手动定义中间表:

model Post {
  id         Int        @id @default(autoincrement())
  title      String
  tags       PostTag[]
}

model Tag {
  id    Int       @id @default(autoincrement())
  name  String    @unique
  posts PostTag[]
}

model PostTag {
  postId     Int
  tagId      Int
  assignedAt DateTime @default(now())

  post Post @relation(fields: [postId], references: [id])
  tag  Tag  @relation(fields: [tagId], references: [id])

  @@id([postId, tagId])
}

中间表 PostTag 有独立的 model 定义,可以像普通 model 一样查询和写入。用 @@id([postId, tagId]) 把两个外键列组合成复合主键。

关系行为:onDelete

定义关系时可以指定删除行为:

author User? @relation(
  fields: [authorId],
  references: [id],
  onDelete: Cascade   // 删除用户时级联删除他的文章
)
值行为
Cascade删除主表记录时,从表关联记录也一起删
Restrict / NoAction有外键关联时禁止删除主表记录
SetNull删除主表记录时,外键列设为 NULL(从表可空才适用)
SetDefault删除主表记录时,外键列设为默认值(PostgreSQL 支持)