PrismaでN対N(多対多)のテーブルを作成するときのTips

PrismaでN対N(多対多)のテーブルを作成するときのTips

2023-04-3012 min read

目次

  1. 概要
  2. 検証した環境
  3. 1-対-n-の場合
  4. n-対-n-のテーブルを定義する
  5. おまけ-自己結合

概要

Prisma で N 対 N(多対多)のテーブルを作成するときの Tips についてのメモです。

ここでは簡単なブログサービスの DB を例に説明していきます。

検証した環境

  • MySQL 8.0
  • Prisma 4.12

1 対 N の場合

まず Prisma の基本的な使い方として、Post(記事テーブル)と Author(著者テーブル)を例に 1 対 N のテーブルを定義してみます。


model Post {
  id             String          @id @default(id()) @map("id")
  content        String          @map("content")
  authorId       Int             @map("author_id")
  author         Author          @relation(fields: [authorId], references: [id])

  @@index([authorId])
  @@map("posts")
}

model Author {
  id        Int        @id @default(autoincrement()) @map("id")
  name      String     @unique
  posts     Post[]

  @@map("authors")
}

N 対 N のテーブルを定義する

N 対 N のテーブルを表現する方法がいくつかあったので紹介していきます。

連想エンティティ(連関エンティティ)を自分で定義する

ブログサービスにおける記事(Post)とカテゴリ(Category)の関係を N 対 N とします。 N 対 N のテーブルは 2 つのテーブルのみで表すことができないため、 以下のスキーマの定義にある PostCategory のような連想エンティティを定義することで表現します。


model Post {
  id             String          @id @default(id()) @map("id")
  content        String          @map("content")
  categories     PostCategory[]
  @@map("posts")
}

model Category {
  id        Int        @id @default(autoincrement()) @map("id")
  name      String     @unique @map("name")
  posts     PostCategory[]
  @@map("categories")
}

model PostCategory {
  postId    String   @map("post_id")
  categoryId Int     @map("category_id")
  post      Post     @relation(fields: [postId], references: [id])
  category  Category @relation(fields: [categoryId], references: [id])

  @@id([postId, categoryId])
  @@map("post_categories")
}

連想エンティティを隠蔽する

上記で紹介した連想エンティティについて、Prisma では隠蔽することができます。 例えば以下の定義のように N 対 N で結合したいキーに対して、@relation("post_category")のようなオプションを加えることで 連想エンティティが隠蔽された(=prisma.schema 上では定義されていない)が作成されます。


model Post {
  id             String          @id @default(id()) @map("id")
  content        String          @map("content")
  authorId       Int             @map("author_id")
  categories     Category[]      @relation("post_category")
  @@map("posts")
}

model Category {
  id        Int        @id @default(autoincrement()) @map("id")
  name      String     @unique @map("name")
  posts     Post[]     @relation("post_category")
  @@map("categories")
}

DB を直接確認すると、_post_categoryという名前でテーブルが作成されていることが確認できると思います。

> show tables;
+-----------------------+
| Tables_in_dev         |
+-----------------------+
| _post_category        |
| categories            |
| posts                 |
+-----------------------+

おまけ: 自己結合

以下の定義のようにカテゴリに親子関係を表現できるような自己結合の定義もできました。

model Category {
  id        Int        @id @default(autoincrement()) @map("id")
  name      String     @unique @map("name")
  parentId  Int?       @map("parent_id")
  parent    Category?  @relation("category_to_category", fields: [parentId], references: [id])
  children  Category[] @relation("category_to_category")

  @@map("categories")
}

※ 自己結合は場合によってはパフォーマンスの悪化につながる可能性があると言われているので、 程々にしておくべきだとだと思います。

Author
githubzennqiita
ただの備忘録です。

※外部送信に関する公表事項