Spring BootでFlywayを使ったデータベースマイグレーション管理 - バージョン管理から本番適用まで
データベーススキーマの変更を手動で管理していると、開発環境と本番環境でテーブル構造が違ってしまったり、SQLの実行順序を間違えたりするトラブルが起きますよね。特に複数人で開発している場合、誰がいつどんな変更を加えたのか追跡できないと大変です。
Flywayを使えば、データベーススキーマの変更履歴をコードと同じようにバージョン管理できるようになります。この記事では、Spring BootプロジェクトにFlywayを導入して、安全にマイグレーションを管理する方法を解説します。
Flywayとは
Flywayは、データベーススキーマのバージョン管理を自動化するツールです。SQLファイルを順番に実行し、どのマイグレーションが適用済みかを flyway_schema_history テーブルで記録します。
Hibernateの ddl-auto を使ってテーブルを自動生成している方も多いと思いますが、これは開発初期には便利な反面、本番環境では予期しない変更が起きるリスクがあります。Flywayなら、スキーマ変更を明示的にコントロールでき、チーム全体で変更履歴を共有できるので安全です。
Spring BootへのFlyway導入
まずは依存関係を追加しましょう。Gradleならこう書きます。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.flywaydb:flyway-core'
runtimeOnly 'org.postgresql:postgresql'
}
Mavenの場合はこちらです。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
次に application.properties でデータベース接続とFlywayの設定を記述します。
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=user
spring.datasource.password=pass
spring.jpa.hibernate.ddl-auto=validate
FlywayはSpring Boot 3.x系ではデフォルトで有効になっているので、spring.flyway.enabled=true を明示的に書く必要はありません。無効化したい場合のみ false を指定してください。
ddl-auto=validate にすることで、Hibernateによる自動テーブル生成を無効化し、エンティティとスキーマの一致を確認するだけにしています。FlywayとHibernateは独立して動作するので、Flywayでスキーマを管理し、Hibernateのauto-ddlは無効化(または validate)するのが推奨パターンです。
データソース設定の詳細は Spring Bootのapplication.propertiesで設定を管理する基本 も参考にしてください。
マイグレーションスクリプトの配置
Flywayは src/main/resources/db/migration ディレクトリを自動的に探しに行くので、ここにSQLファイルを配置しましょう。
ファイル名は V{バージョン}__{説明}.sql という形式にする必要があります。
V1__init.sqlV2__add_email_column.sqlV3__create_orders_table.sql
バージョン番号は昇順で一意にする必要があるので注意してください。アンダースコア2つ(__)で説明部分を区切ります。この命名規則を守らないとFlywayが認識してくれません。
初期スキーマの作成
最初のマイグレーションスクリプト V1__init.sql を作成してみましょう。
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_users_username ON users(username);
Spring Bootアプリケーションを起動すると、Flywayが自動的にこのスクリプトを実行します。確認してみると、users テーブルと flyway_schema_history テーブルが見つかるはずです。
SELECT * FROM flyway_schema_history;
このテーブルには、適用されたマイグレーションのバージョン、説明、実行日時、チェックサムなどが記録されます。一度記録されたスクリプトのチェックサムが変わると、Flywayはエラーを出して実行を拒否するので、履歴の改ざんや意図しない変更を防げます。
スキーマ変更の追加
既存のテーブルにカラムを追加する場合、新しいマイグレーションスクリプトを作成します。
-- V2__add_email_column.sql
ALTER TABLE users ADD COLUMN email VARCHAR(100);
NOT NULL制約を追加したい場合、既存データがある環境では注意が必要です。まずNULL許可で追加し、デフォルト値を設定してからNOT NULLに変更するのが安全です。
-- V3__make_email_required.sql
UPDATE users SET email = '[email protected]' WHERE email IS NULL;
ALTER TABLE users ALTER COLUMN email SET NOT NULL;
この例では仮のデフォルト値を使っていますが、実務では業務要件に応じた適切な値設計やデータクレンジング戦略が必要になります。example.com はRFC2606で予約されているドメインなので、テスト用には安全ですが、実運用では要件に合わせて調整してください。
複数のテーブル変更をどうまとめるかですが、関連する変更(外部キー追加とテーブル作成など)は1スクリプトにまとめると、ロールバックが容易になります。独立した変更は分割しておくと、問題が起きたときに切り分けやすいです。
既存データベースへの適用と環境別設定
すでにテーブルが存在するデータベースにFlywayを導入する場合は、baseline-on-migrate を使います。
spring.flyway.baseline-on-migrate=true
spring.flyway.baseline-version=1
これを設定すると、既存のデータベース環境では baseline-version で指定したバージョン(デフォルトは1)までのマイグレーションをスキップし、履歴テーブルにbaseline記録を追加します。その後、V2以降のマイグレーションだけが実行されます。
一方、まっさらな新規環境では全マイグレーション(V1から)が通常通り実行されるので、既存のテーブル構造を V1__init.sql として再現しておくと、新しい環境でも同じ構造を作れます。つまり、V1は新規環境構築用、baselineは既存環境への後付け導入用という使い分けになります。
baseline-version=0 を指定すると既存スキーマを初期状態として扱い、V1から適用開始します。baseline-version=1 ならV1を既存として扱い、V2から適用します。環境に応じて調整してください。
本番環境では特に慎重な設定が必要です。Profileごとに設定ファイルを分けましょう。
# application-prod.properties
spring.flyway.clean-disabled=true
spring.flyway.baseline-on-migrate=false
Flywayの clean コマンドは、データベース内のすべてのテーブルを削除する危険な機能です。本番環境では clean-disabled=true にして、誤って実行されないようにします。開発環境では false にしておくと、テスト時に柔軟にスキーマをリセットできます。
環境別設定では、application-dev.properties や application-prod.properties を用意して、Profileで切り替えます。詳しくは Spring BootのProfileを使って環境によって違う設定を安全に切り替える方法 を参照してください。
マイグレーション失敗時の対処
マイグレーション実行中にエラーが発生すると、flyway_schema_history に success=false のレコードが残ります。
SELECT version, description, success FROM flyway_schema_history WHERE success = false;
チェックサム不一致エラーは、既に適用済みのスクリプトを修正すると発生します。本番環境ではスクリプトを修正せず、新しいバージョンで変更を追加するのが原則です。
開発中に誤ってコミットしたスクリプトを修正したい場合は、次の手順で対処します。
- スクリプトを修正する
./gradlew flywayRepair(またはmvn flyway:repair)でチェックサム再計算と失敗レコード削除を実行- アプリケーションを再起動してマイグレーションを再実行
SQL構文エラーや制約違反でマイグレーションが失敗した場合も同様です。スクリプトを直してから flywayRepair を実行し、再起動すればOKです。
開発環境では履歴テーブルから手動で失敗レコードを削除する方法もありますが、本番環境では履歴テーブルを直接操作するのは危険です。監査証跡や整合性リスクがあるので、flywayRepair コマンドを使うようにしましょう。
チーム開発とロールバック戦略
複数人で開発していると、同じバージョン番号のマイグレーションを作成してしまうことがあります。連番(V1, V2…)はシンプルですが、複数ブランチで重複しやすいのが難点です。タイムスタンプベース(V20260204120000__…)なら重複を自動回避できますが、可読性は下がります。チームの開発フローに応じて選択してください。
V20260204120000__add_user_email.sql
V20260204130000__create_orders_table.sql
Gitでマージする際にバージョン番号が重複していたら、後から作成した方のファイル名を変更してバージョン番号を繰り上げます。マイグレーションスクリプトはコードと同じようにレビューし、既存データへの影響やロールバック計画を確認しましょう。
Flywayの無料版には自動ロールバック機能がありません。Pro/Teams版では U1__, U2__ 形式のUndoスクリプトを作成し、flyway undo コマンドで実行できますが、無料版ではロールバック用のSQLスクリプトを別途用意して手動実行する必要があります。
最も確実なのは、本番適用前にデータベースバックアップを取得しておき、問題が発生したらバックアップから復元する方法です。ステージング環境で十分に検証してから本番適用すれば、リスクを大幅に減らせます。
本番環境にデプロイする前には、次の点を確認しましょう。
- データベースのバックアップを取得済みか
- ステージング環境で正常に動作したか
- ロールバック計画を用意しているか
- メンテナンスウィンドウを確保しているか
マイグレーション適用後は、flyway_schema_history を確認して最新のマイグレーションが success=true になっていること、アプリケーションが正常に起動することを確認します。
Dockerを使ったデプロイについては Spring BootアプリケーションをDockerでコンテナ化する実践ガイド も参考になります。
まとめ
Flywayを使うことで、データベーススキーマの変更履歴が明確になり、環境間の一貫性を保てるようになります。手動でSQLを実行するヒューマンエラーを防ぎ、チーム開発の効率も向上します。
バージョン管理のルールを守り、本番適用前に十分な検証を行えば、デプロイのリスクを大幅に減らせます。まずは開発環境で試してみて、チームに合った運用方法を見つけていきましょう。
JPAのエンティティ設計については Spring BootのJPAでエンティティのリレーションシップをマッピングする方法 も合わせてご覧ください。