Prismaを活用したマイグレーション・ロールバックSQLの作成　DB構築→モデル作成をやっている人向け

## はじめに

基本的なORMの利用方法としては、以下のようにモデル→データベースの順番で作成・構築していくと思う。

1. モデルを定義
1. データベースにそのモデルに合致するテーブルを構築

ただ、モデルの定義をしてからテーブルを構築だとどうしてもかゆい所に手が届かなかったりもするので、上記の逆の手順で、データベース→モデルという順番で作成・構築する事もあると思う。

1. GUIツール（MySQL WorkbenchやNavicatなど）を利用してテーブルを設計
1. テーブル定義からモデルを作成

ただこの場合、ORMライブラリに備わっている機能を使ってモデルの変更に合わせマイグレーションをするという事ができないので、dev, staging, productionと環境があった時には、手動でSQLを流してマイグレーションを行う必要がある（モデル→テーブルの変更の順であれば、Prismaの場合、`prisma migrate deploy`で開発環境で作成したマイグレーションを環境ごとに適用するだけ（詳細は[ここ](https://www.prisma.io/docs/concepts/components/prisma-migrate/mental-model#what-is-prisma-migrate)を参照）。

そのマイグレーションのSQLとロールバックのSQLを、うまく簡単に作り、バージョン管理もできなかな～と思い、今回はPrismaを利用して、手動でマイグレーション・ロールバックのSQLを作成する手順を考えてみたいと思う。

## データベースへの更新、モデルへの反映、マイグレーション用のSQL作成の流れ

まず、初期化と変更の2つがあると思うので、2つに分けてみていく。

- 初期化
一番最初にテーブルを作成するタイミングで、マイグレーションの初期ファイルを作る
- 変更
テーブルの追加・変更など繰り返し行う手順で、今回で言うとこの変更時のマイグレーション・ロールバックのSQLを作成する部分が肝になる

### 初回

初回は[Introspection](https://www.prisma.io/docs/concepts/components/introspection)にあるように、データベースからテーブルの定義を読み出し、モデルを作成したり、マイグレーションの初期ファイルを作成する。手順化すると以下のようになるだろう。

1. GUI（MySQL WorkbenchやNavicat）でテーブルを設計・作成
1. `npx prisma db pull`でモデルを作成
1. `mkdir -p prisma/migrations/0_init`で初期状態を作るマイグレーションの格納先を作成
1. `npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql`で初期状態を作るマイグレーションのSQLを作成

実際にやってみた時のログは以下。

https://github.com/yuta-katayama-23/vite-vue3-vuetify-express/commit/350609626a8e2e4ea8b2966cacbd86f3715420f0

### 変更

続いて、今回やりたかったことの本題になる、テーブルの追加・変更を行った時に、各環境に反映するためのマイグレーションのSQLをどうやって作成するか、またロールバックのSQLをどうやって作成するか、を見ていきたいと思う。手順のサマリとしては以下のようになる。

1. GUI（MySQL WorkbenchやNavicat）でテーブルを設計・作成
1. `mkdir -p prisma/migrations/1_hogehoge`でマイグレーションの格納先を作成
1. `npx prisma migrate diff --from-schema-datamodel prisma/schema.prisma --to-url mysql://root:@localhost:3306/prisma-express --script > prisma/migrations/1_hogehoge/migration.sql`でマイグレーションのSQLを作成する（詳細はのちほど）
1. `npx prisma migrate diff --from-url mysql://root:@localhost:3306/prisma-express --toschema-datamodel prisma/schema.prisma script > prisma/migrations/1_hogehoge/rollback.sql`でマイグレーションのSQLを作成する（詳細はのちほど）
1. `npx prisma db pull`でモデルを更新

少し補足する。

4，5の手順だが、それぞれ以下のように考えてマイグレーション・ロールバックのSQLを作成している。

- 4
データベースのテーブルが最新で、手元のモデルはそれを反映していないので、手元のモデル→データベースのテーブルに同期するために必要なSQLを出力すると、それが各環境に適用すべきマイグレーションのSQLになる
そのため、`--from-schema-datamodel`でローカルのモデルを指定し、`--to-url`でデータベース（のURL）を措定している
- 5
こちらは4の逆の考えでロールバックのSQLを作成している
つまり、データベースのテーブルが最新なので、その最新の状態を手元のモデルに反映する（データベースのテーブル→モデル）をするために必要なSQLを出力すると、 それがロールバックのSQLになる
そのため、`--from-url`でデータベース（のURL）を指定し、`--to-schema-datamodel`でローカルのモデルを措定している

実際にやってみた時のログは以下。

https://github.com/yuta-katayama-23/vite-vue3-vuetify-express/commit/40100f945a921bfcd803387a0f9c126b3ed4b3bc


※ただ、上記の方法で課題がある。`prisma migrate diff`で--scriptオプションを付与して生成されるSQLのCOLLATEが`utf8mb4_unicode_ci`になってしまい、データベースのテーブルの状態と乖離してしまう
現時点で[issue](https://github.com/prisma/prisma/issues/14179)を見る限り、この部分は手動で修正が必要になる。

https://github.com/yuta-katayama-23/vite-vue3-vuetify-express/commit/e95aa0b6603acc9b13322f816f3d2e42c2aee932

※上記のPrismaの動きに関して、MySQL8のデフォルトのCOLLATEは`utf8mb4_0900_as_ci`であるにもかかわらず、Prismaで作成したSQLのCOLLATEがなぜutf8mb4_unicode_ciになるのかは謎・・・。どこからきているのか？不明だった（MySQL8のデフォルトのCollationについては[ここ](https://dev.mysql.com/doc/refman/8.0/en/charset-mysql.html)を参照）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/bd0d0918-913e-23e5-00b7-75ae9c366645.png)

- 参考：[MySQLのデフォルトcollationの注意点](https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0157)
- 参考：[MySQLの文字コードとCollation](https://qiita.com/tfunato/items/e48ad0a37b8244a788f6)

## まとめとして

Prismaを活用する事で、データベース→モデルという逆の順番で開発を進めていても、効率よくマイグレーション・ロールバックのSQLを作成でき、またそれをGitの管理下に置く事でリリース時にはそれを流せばいいか？という事だけ気にすればよくなるのでいいのではと思った（マイグレーション・ロールバックのSQLをリリースの準備で作らないで良くなるし、そのSQLがどこにあるか分からないという事態にもならないので）。