首页 / Prisma ORM 入门教程 / 关系基础:一对多与一对一

Prisma ORM 入门教程

关系基础:一对多与一对一

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

Prisma关系一对多一对一外键@relation数据建模多字段关系

本节目标:区分关系字段与外键标量字段,用 @relation 定义一对多和一对一关系。

应用里的数据几乎从不孤立存在:用户发布文章,文章有作者。关系(relation)就是模型之间的这种连接。在数据库层面,关系靠外键实现;在 Prisma 层面,关系由成对出现的关系字段描述。

关系字段 vs 外键

看这个一对多例子,User 和 Post 各有一个关系字段:

model User {
  id    Int    @id @default(autoincrement())
  posts Post[]                        // 关系字段(列表侧)
}

model Post {
  id       Int  @id @default(autoincrement())
  author   User @relation(fields: [authorId], references: [id])  // 关系字段(带注解)
  authorId Int                         // 关系标量字段:真正的外键
  title    String
}

两个概念要分清:

  • 关系字段postsauthor):类型是另一个模型,只存在于 Prisma 层,数据库里没有对应列。
  • 关系标量字段authorId):真正的外键列,存在数据库里。

@relation(fields: [authorId], references: [id]) 把两者串起来:fields 是本模型的外键字段,references 是被引用模型上的字段(通常是主键)。命名约定是「关系字段名 + Id」:authorauthorId。外键字段的类型必须和被引用字段一致,IntIntStringString

一条关系恰好由两个关系字段组成,两边各一个。每个关系必须成对出现,漏了任何一侧,prisma validate 都会报错。

一对多:列表侧 + 外键侧

一对多表达「一个用户有多篇文章」。判断方法:哪一侧能有多条记录,哪一侧就是列表。posts Post[] 在 User 上,所以是「一」;author 在 Post 上,所以是「多」。

一对多可以必填,也可以可选。可选的 1-n 允许文章没有作者:

model Post {
  id       Int    @id @default(autoincrement())
  author   User?  @relation(fields: [authorId], references: [id])
  authorId Int?   // 外键可空
}

关系字段和外键标量必须同时加 ? 或同时不加,否则校验报错。必填版(User + authorId Int)则强制每篇文章都有作者,创建时要么嵌套创建作者,要么用 connect 接上已有作者。

在数据库层面,外键就是普通列加约束。迁移生成的 SQL 大致长这样:

ALTER TABLE "Post" ADD FOREIGN KEY ("authorId") REFERENCES "User"("id");

理解这张底牌,后面看迁移文件就不慌。

一对一:@unique 决定一切

一对一表达「一个用户只有一个资料页」。关键差异在数据库层:外键加 @unique 就是 1-1,不加就是 1-n。唯一约束保证同一个用户不会被两个资料页引用。

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

model Profile {
  id     Int  @id @default(autoincrement())
  bio    String
  user   User @relation(fields: [userId], references: [id])
  userId Int  @unique          // 外键 + 唯一约束 → 一对一
}

两条规则要记住:

  1. 没有外键的那一侧必须可选。User 侧的 profile Profile? 写死不能去掉——User 表里没有外键列,无法强制「必须有资料页」。
  2. 外键放哪边都可以。上例放 Profile 侧;反过来放 User 侧(profileId Int? @unique)同样合法。选择标准:哪个实体「必须依附」另一个,就把外键放哪边。

必填的 1-1(创建 User 必须同时给 Profile)也能建模,把外键侧写成 profile Profile @relation(...) + profileId Int @unique 即可,代价是创建 User 时 profile 不能缺席。

Note

@relation 不只是 1-1/1-n 的必需品。同一对模型之间有多条关系(比如「作者」和「置顶」都是 User 与 Post 的关系)、自关联(用户关注用户)时,都需要 @relation("名字") 来区分。这些进阶形态在第 13 章展开;引用操作的默认值见第 14 章。

Note

也可以引用非主键字段,只要被引用字段带 @unique。比如用 email 而非 id 做关系锚点:@relation(fields: [authorEmail], references: [email])。少见但合法。

多字段关系

被引用方是复合主键时,fieldsreferences 都要写成数组,一一对应:

model User {
  firstName String
  lastName  String
  posts     Post[]

  @@id([firstName, lastName])
}

model Post {
  id               Int    @id @default(autoincrement())
  author           User   @relation(fields: [authorFirstName, authorLastName], references: [firstName, lastName])
  authorFirstName  String
  authorLastName   String
}

字段数量、顺序、类型必须完全匹配。多字段关系仅关系型数据库支持。

关系能做什么

定义好关系,Prisma Client 立刻获得三种能力:创建时嵌套写入(posts: { create: [...] })、查询时用 include 带上关联数据、以及按关系条件过滤(where: { posts: { some: { published: true } } })。先看 include 的直观效果:

const user = await prisma.user.findUnique({
  where: { id: 1 },
  include: { posts: true },   // 把关联文章一起查出来
});

返回结果里多出 posts 数组,一次请求拿到用户和全部文章。嵌套写入、关系过滤的完整用法,本教程第三部分会逐个展开,这里先记住:关系字段就是通往关联数据的入口

参考来源

  • Prisma 官方文档:Relations、One-to-many relations、One-to-one relations