Spring BootアプリにFlywayを導入してみた
Spring Bootのアプリで、ローカルDBのスキーマをうまく管理するためにFlywayを導入してみたのでメモ。
Flywayを選んだ理由
Flywayを選んだ理由は主に以下の辺り。
- DB設計・管理コストの削減
- フレームワーク・OSSによる一般的な枠組みの採用
- 学習コストが低い、シンプルな構成
- できるだけXMLレスな設定
他にはLiquibaseもあるが、XMLレスにしたいので回避。
MyBatis Migrationsも気になったけど、Flywayのほうがバージョン管理が楽そうでした。
FlywayはSpring Bootの依存関係に含まれていて、Auto Configurationにも対応してます。
Maven設定
pom.xmlに以下を追加。
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
マイグレーションスクリプトの配置
マイグレーションスクリプトの配置場所はspring.flyway.locations
で指定できるが、Spring Bootではデフォルトで以下のように指定されている。
spring.flyway.locations=classpath:/db/migration
そのため特にプロパティファイルには指定せず、/resources/db/migration/以下にスクリプトを配置していけばよい。
マイグレーションスクリプトはV1_0_0__baseline.sql
のようなファイル名にすると、バージョンが1.0.0
で、descriptionがbaseline
になる。
開発・テスト用のデータを投入する
開発時やテスト用の自動投入するには、プロファイル毎に異なるプロパティを参照させればよさそう。
Spring Boot Reference Guide の Execute Flyway Database Migrations on Startupにあるように、プロファイル毎のプロパティで設定を分け、開発時のプロファイル(dev)でのみ以下のように指定する。
spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
あとはサンプルデータの投入処理を/dev/db/migration/以下に配置すればよい。
ベースラインの設定
ベースラインとは、既存のDBスキーマがどのバージョンに相当するかを指定するもの。
これまでFlywayを使っていなかったDBスキーマでは明示的に指定する必要がある。
今回はSpring Bootアプリのプロパティで、以下を指定。
プロパティの内容はほぼFlywayの環境変数そのままなので、FlywayのEnvironment Variablesが詳しい。
spring.flyway.baseline-version=1.0.0
spring.flyway.baseline-on-migrate=true
前者はベースラインのバージョンを指定する。
デフォルトは1だが、今回はV1_0_0__baseline.sql
のようなファイル名でマイグレーションスクリプトを作っていく予定なので、1.0.0を指定。
後者はメタデータを持たないが空でないDBだった場合、マイグレーション実行時にベースラインを実行してよいかどうかを設定する。
つまり、今までFlywayを使ってこなかったDBスキーマに対し、ベースラインを自動的に適用してくれる。
なお、ベースラインが実行された時、そのマイグレーションスクリプトは実行されない。
例えばspring.flyway.baseline-version=1.0.0
を指定した場合、存在するDBに対してV1_0_0__baseline.sql
は実行されない。
マイグレーション時のエラー検知設定
マイグレーション実行時、エラーと判定するかどうかを設定する項目があるようで、Flywayの公式ドキュメントで以下の4つが記載されている。
- FLYWAY_IGNORE_MISSING_MIGRATIONS
- FLYWAY_IGNORE_IGNORED_MIGRATIONS
- FLYWAY_IGNORE_PENDING_MIGRATIONS
- FLYWAY_IGNORE_FUTURE_MIGRATIONS
これらはSpring Bootでは、以下の設定がデフォルトになっている。
spring.flyway.ignore-future-migrations=true # Whether to ignore future migrations when reading the schema history table.
spring.flyway.ignore-ignored-migrations=false # Whether to ignore ignored migrations when reading the schema history table.
spring.flyway.ignore-missing-migrations=false # Whether to ignore missing migrations when reading the schema history table.
spring.flyway.ignore-pending-migrations=false # Whether to ignore pending migrations when reading the schema history table.
ただ、これらの条件がいまいちよく分からなかったので、実際に試しつつ考察。
FLYWAY_IGNORE_FUTURE_MIGRATIONS
再現例
- Spring Bootの構成にマイグレーション用のスクリプトV1, V2を配置して起動→V1, V2が適用される
- その後V2のSQLが存在しない状態で、再度起動する
この時、spring.flyway.ignore-future-migrations
の値により、以下のようになる。
- true:エラーとならずに無視される
- false:エラーとなり停止する
考察
アプリ/システムのDBバージョンが低い状態、すなわちダウングレードさせた状態を許容するかどうかのチェック。
最新版で問題が起きたとき等、試しにダウングレードして動かすような使い方が想定されるならtrue。
ただし最新版でスキーマを大きく変更(特にカラムの削除や移動)したり、制約条件を追加した場合には、そもそもダウングレードしてもエラーになる可能性があるので注意。
FLYWAY_IGNORE_IGNORED_MIGRATIONS
再現例
- Spring Bootの構成にマイグレーション用のスクリプトV1, V2を配置して起動→V1, V2が適用される
- その後V1.1を追加した構成で、再度起動する
この時、spring.flyway.ignore-ignored-migrations
の値により、以下のようになる。
- true:エラーとならずに無視される。V1.1も実行されない
- false:エラーとなり停止する
考察
適切な順序で適用できないマイグレーションスクリプトが存在するかどうかのチェックをしている。
DBスキーマのカラム名変更や削除を行わず、カラムだけを追加していくような場合はtrueでも問題ないと思われる。
もし順番を気にせず実行して問題ない場合はspring.flyway.out-of-order=true
を指定すれば実行される(はず)。
FLYWAY_IGNORE_MISSING_MIGRATIONS
再現例
- Spring Bootの構成にマイグレーションスクリプトV1, V2, V3を配置して起動→V1, V2, V3が適用される
- その後V2を削除し、V2が存在しない構成で再度起動する
この時、spring.flyway.ignore-missing-migrations
の値により、以下のようになる。
- true:エラーとならずに無視する
- false:エラーとなり停止する
考察
アプリ/システムのマイグレーションスクリプトに欠落がないかどうかのチェックをしている。
DBスキーマのカラム名変更や削除を行わず、カラムだけを追加していくような場合はtrueでも問題ないと思われる。
FLYWAY_IGNORE_PENDING_MIGRATIONS
どういうケースが当てはまるのかまだ理解できておらず、具体例を確認できていない。
以下を試してみたが、本エラーは発生せず。
- V1, V2, V3を配置して起動したら、V3までマイグレーションされるので、いずれもPendingにはならない
- V1, V2, V3を配置して
spring.flyway.target=2
を指定しても、V3は認識されず、Pendingにならない。
その他:リピータブルマイグレーション
まだ使用していないが、リピータブルマイグレーションという枠組みもある。
マイグレーション毎に毎回実行されるため、ビューの再作成などに使用する(らしい)。
その他:DDL/DMLとの優先順位
Spring Bootでは、以下のプロパティで指定したSQLを自動的に実行することができるようになっている。
spring.datasource.schema= # Schema (DDL) script resource references.
spring.datasource.data= # Data (DML) script resource references.
どうやらこれらはFlywayのマイグレーション処理よりも先に実行されるようなので、それを考慮した構成にする必要がある模様。
所感
Flyway(というかDBマイグレーション)は前々から目をつけていたものの、ようやく導入。
DBマイグレーションに関する実装を設定ファイルとSQLファイルで完結できるのは大きなメリット。
構成がシンプルで、学習コストが低そうな点もおおよそ想定通り。
エラーレベルに関してはまだ完全に把握できていない部分もあるが、基本的にはデフォルトの設定で運用し、エラーが出たら開発や運用の体制に応じて見直せば問題なさそう。