みなさん、こんにちは。サイオステクノロジー武井です。DBマイグレーションツールflywayを触る機会があったので、利用用途や利用方法をブログに残したいと思います。
Flywayとは?
DBマイグレーションツールです。スキーマの作成や変更などの処理をリリースするアプリの中に埋め込むことができます。
Flywayが必要になる理由
ID・姓・名の3つのフィールドを持つusersという名前のテーブルがあるとします。DDLは以下のとおりです。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
last_name VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL
);
まず、このテーブルを管理するアプリをリリースできたとします。
そして、次に改修要件として、メールアドレスを格納するフィールドmailを追加する必要がでてきて、Aさんはその開発担当者にアサインされました。
Aさんは、テーブルを変更するために、Aさん専用の開発環境用データベースに以下のDDLを適用し、開発を行い、無事完了させました。
ALTER TABLE users ADD mail VARCHAR(255);
しかし、Aさんは上記のスキーマ変更のことを誰にも共有せず、改修したソースだけをリポジトリにPushしました。
Bさんは、AさんがリポジトリにPushしたコードをPullしましたが、当然動きません。Bさん専用の開発環境用データベースには上記のDDLが適用されていない、つまりmailフィールドがないからです。
このような事態を防ぐには、「アプリケーションのバージョンとデータベース構成のバージョンを一致させること」が必須となるのですが、それを実現するのがflywayです。
やってみる
先程挙げた以下のユースケースをflywayを使って実際にやってみたいと思います。
- usersテーブルを作成し、そのテーブルを管理するアプリを開発する。
- usersテーブルにmailフィールドを追加し、mailフィールドを取得できるようアプリを改修する。
usersテーブルを作成し、そのテーブルを管理するアプリを開発する
バージョン管理にgradle、データベースにMySQLを使うとして、gradleに以下の設定を追加します。
dependencies {
runtimeOnly 'org.flywaydb:flyway-mysql' // 追加
runtimeOnly 'com.mysql:mysql-connector-j' // 追加
...
}
次にapplication.ymlに以下の内容を追記します。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/db # JDBC接続文字列
username: user // データベース接続ユーザー
password: password // データベース接続パスワード
driver-class-name: com.mysql.cj.jdbc.Driver # ドライバークラス名
flyway:
enabled: true # flyawyを有効にする
データベースに適用させたいSQLを定義します。V1__create_table1.sqlという名前のファイルを作成し、以下の内容を追記します。
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
last_name VARCHAR(100) NOT NULL,
first_name VARCHAR(100) NOT NULL
);
このファイル名は以下の規則によって命名されています。
V[バージョン番号]__[説明].sql
[バージョン番号]はこの番号が若い順から適用されていきます。仮にV1__create_table1.sqlとV2__alter_table1.sqlというファイル名があった場合、V1__create_table1.sql→V2__alter_table1.sqlの順にSQLが実行されます。 [説明]はこのSQL文がどういったものなのかを表す説明になります。日本語OKです。先程作成したV1__create_table1.sqlを以下のディレクトリに配置します。デフォルトではクラスパスの通っているディレクトリ配下にdb/migrationというディレクトリを作成し、そこにマイグレーションしたいSQLファイルを配置します。
main
└── resources
└── db
└── migration
└── V1__create_table1.sql
usersテーブルを操作するアプリを開発します。
そして以下のコマンドを実行します。
$ ./gradlew bootRun
すると以下のテーブルが作成されているはずです。
- flyway_schema_history
- users
flyway_schema_historyはマイグレーション情報を管理するテーブルです。どのマイグレーションがすでに適用されたか、またそれらのマイグレーションがいつ適用されたかを追跡するために使用します。
そしてこれらのファイルをリポジトリにPushします。
usersテーブルにmailフィールドを追加し、mailフィールドを取得できるようアプリを改修する
usersテーブルにmailフィールドを追加するためのSQLを定義します。V2__alter_table1.sqlという名前のファイルを作成し、以下の内容を追記します。
ALTER TABLE users ADD mail VARCHAR(255)
先程作成したV2__alter_table1.sqlを以下のディレクトリに配置します。
main
└── resources
└── db
└── migration
├── V1__create_table1.sql
└── V2__alter_table1.sql
usersテーブルのmailフィールドを取得するアプリを開発します。
そして以下のコマンドを実行します。
$ ./gradlew bootRun
するとusersテーブルにmailフィールドが追加されているはずです。
そしてこれらのファイルをリポジトリにPushします。
つまりアプリを実行すると同時に、事前に定義したスキーマ情報(db/migration配下に作成されたSQLファイル)に従い、flywayがテーブルを作成してくれます。
アプリのリリースとデータベースの構成が同時に行われるので、先に説明したようにアプリの改修だけ反映されて、それに伴うスキーマの構成が未反映という状態はなくなります。
便利ですね。
既にテーブルが存在する場合
既にテーブルが存在している場合のマイグレーションの方法を説明します。
つまり先の例で言えば、既にusersテーブルは作成されているけれど、mailフィールドが追加されていない状態からflywayを利用したい場合です。
このような場合はベースラインバージョンというものを指定します。つまり、どのSQLから適用させたいかを指定するのです。今回の場合は、V1__create_table1.sql(バージョン1)はスキップして、V2__alter_table1.sql(バージョン2)から適用させたいですよね。
main
└── resources
└── db
└── migration
├── V1__create_table1.sql ← これはスキップしたい
└── V2__alter_table1.sql ← ここから適用させたい
そのためには、application.ymlにbaseline-versionとbaseline-on-migrateを追記します。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/db # JDBC接続文字列
username: user // データベース接続ユーザー
password: password // データベース接続パスワード
driver-class-name: com.mysql.cj.jdbc.Driver # ドライバークラス名
flyway:
baseline-version: 1 # バージョン1をスキップする場合、ベースラインバージョンは1となる
enabled: true # flyawyを有効にする
baseline-on-migrate: true # 既にテーブルが存在するときにマイグレーションする場合はtrueとする
そして以下のコマンドを実行します。
$ ./gradlew bootRun
flyway_schema_historyテーブルが作成され、usersテーブルにmailフィールドが追加されているはずです。
まとめ
flywayいい感じですよね。つかってみようかなぁ。