Skip to content

多对多关系 ​

多对多 (m-n) 关系指,其中一侧的零或多条记录可以关联另一侧的零或多条记录的关系。

关系型数据库中,多对多关系一般通过 关系表 实现。Prisma schema 中多对多关系既可以是 显式的 也可以是 隐式的。

表结构 ​

ts
model course {
  id              Int              @id @default(autoincrement())
  name            String?          @db.VarChar(10)
  student_courses student_course[]
}
model student {
  id              Int              @id @default(autoincrement())
  name            String?          @db.VarChar(10)
  no              String?          @db.VarChar(10)
  student_courses student_course[]
}

model student_course {
  id        Int     @id @default(autoincrement())
  studentid Int
  student   student @relation(fields: [studentid], references: [id])
  courseid  Int
  course    course  @relation(fields: [courseid], references: [id])
}

查询 ​

  • 第一种通过 student-> student_course->course 查询
ts

  async manybymany() {
    const result = await this.prismadbService.student.findMany({
      select: {
        name: true,
        student_courses: {
          select: {
            course: {
              select: {
                name: true,
              },
            },
          },
        },
      },
    });
    return result;
  }
  • 第二种通过 student_course->student 或者course 查询
ts
  async manybymany2() {
    const result = await this.prismadbService.student_course.findMany({
      select: {
        student: {
          select: {
            name: true,
          },
        },
        course: {
          select: {
            name: true,
          },
        },
      },
    });
    return result;
  }

有的时候需要把关联的全部查询 ​

ts
const list = await this.prisma.users.findMany({
  include: {
    user_roles: {
      select: {
        role_id: true,
      },
    },
  },
});

插入语法 ​

场景说明 ​

学生表 student 和课程表 course 是多对多关系

中间表 student_course 负责记录"哪个学生选了哪门课"

比如:张三选了 数学 和 英语 两门课

方式一:嵌套创建(一次性全建好) ​

学生和课程都不存在,同时创建学生、课程,并自动写入中间表

ts
  async insertManyByMany() {
    const result = await this.prismadbService.student.create({
      data: {
        name: '张三',
        no: 'S001',
        student_courses: {
          create: [
            // 每一条 create 都会往中间表 student_course 插一条记录
            {
              course: {
                create: { name: '数学' }, // 同时新建课程
              },
            },
            {
              course: {
                create: { name: '英语' },
              },
            },
          ],
        },
      },
    });
    return result;
  }

说明

student_courses: { create: [...] } 就是往中间表插数据

中间表的 studentid 由 Prisma 自动填,course 里面的 create 表示同时新建课程

方式二:连接已有数据(connect) ​

学生已存在,要给他选一门已经存在的课程(比如 id 为 1 的数学课)

ts
  async insertConnect() {
    const result = await this.prismadbService.student.update({
      where: { id: 1 }, // 找到 id 为 1 的学生
      data: {
        student_courses: {
          create: [
            {
              course: {
                connect: { id: 1 }, // 连接到已存在的课程
              },
            },
          ],
        },
      },
    });
    return result;
  }

create 和 connect 的区别

  • create:课程不存在,新建课程并关联
  • connect:课程已存在,直接关联过去

方式三:直接操作中间表(最简单直白) ​

如果学生 id 和课程 id 都知道,直接往中间表插数据就行

ts
  async insertByRelationTable() {
    const result = await this.prismadbService.student_course.create({
      data: {
        studentid: 1, // 学生 id
        courseid: 2,  // 课程 id
      },
    });
    return result;
  }

方式四:批量插入选课记录(createMany) ​

一个学生一次选了多门课,可以用 createMany 批量插入

ts
  async insertMany() {
    const result = await this.prismadbService.student_course.createMany({
      data: [
        { studentid: 1, courseid: 1 },
        { studentid: 1, courseid: 2 },
        { studentid: 1, courseid: 3 },
      ],
    });
    return result;
  }

注意 ​

注意

  1. 多对多插入本质就是往中间表 student_course 写数据

  2. 嵌套创建时中间表的外键(studentid / courseid)不用自己写,Prisma 自动处理

  3. 用 connect 时,连接的课程必须真实存在,否则会报错

  4. createMany 只能直接写外键,不支持嵌套 create