PrismaでMany-to-manyのテーブルを定義する
Prismaで多対多のテーブルを作る際は、Relations (Reference)に書かれているように、明示的な指定と暗黙的な指定の2つの方法が存在する。
今回、コード側をシンプルにするために暗黙的な指定を使おうとして、SQLの指定方法でハマったのでメモを残しておく。
MySQLは5.7で試した。
例として記事(Post)にタグ(Tag)付けをする際のモデル定義を以下に示す。
// schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
tags Tag[] @relation("tag_posts")
@@map("posts")
}
model Tag {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("tag_posts")
@@map("tags")
}
暗黙的な定義なのでモデル定義はシンプルになっている。
このモデルに対応するSQLを以下に示す。
CREATE TABLE `posts` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `tags` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 1. テーブル名は_始まりにする必要がある
CREATE TABLE `_tag_posts` (
-- 2. カラム名はA, B固定
`A` int NOT NULL,
`B` int NOT NULL,
UNIQUE INDEX `tag_post_idx`(`A`, `B`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 3. A, Bの指定順はアルファベット順にする必要がある
ALTER TABLE
`_tag_posts`
ADD
FOREIGN KEY (`A`) REFERENCES `posts`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE
`_tag_posts`
ADD
FOREIGN KEY (`B`) REFERENCES `tags`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ポイントは以下の3点
- 中間テーブルの名前は_始まりにする必要がある
- カラム名はA, B固定
- A, Bの指定順はアルファベット順にする必要がある
ドキュメントをちゃんと読めばこの暗黙のルールが書かれているが、見落としていたのでハマってしまった。
本来は手動でSQLを作るのではなく、Prisma Migrateを使ってSQLを自動生成するのが良いと思われる。
ただ、MySQLの場合だと自動生成されたSQLのcollationを変更することができなかったので今回は手動でSQLを作成した。
暗黙的な指定を使うことでコード側は中間テーブルを意識しなくて良いのでシンプルになったものの、DB単体で見た時にカラム名がA, Bだと何を示しているかが分かりづらいのでちゃんとしたサービスを作るときは明示的な指定を使ったほうが良いと思う。
ActiveRecordのhas many throughに対応する機能が実装されれば、明示的な中間テーブルを利用する場合のコード側もシンプルになりそう。