データベーススキーマの変更を手動で管理していると、開発環境と本番環境でテーブル構造が違ってしまったり、SQLの実行順序を間違えたりするトラブルが起きますよね。特に複数人で開発している場合、誰がいつどんな変更を加えたのか追跡できないと大変です。
Flywayを使えば、データベーススキーマの変更履歴をコードと同じようにバージョン管理できるようになります。この記事では、Spring BootプロジェクトにFlywayを導入して、安全にマイグレーションを管理する方法を解説します。
Flywayとは
Flywayの仕組み
Flywayはアプリケーション起動時(または CLI 実行時)に db/migration 配下の SQL ファイルをスキャンし、ファイル名のバージョン番号順にソートします。flyway_schema_history テーブルを参照して未適用のスクリプトを特定し、トランザクション内で SQL を実行、成功すれば該当バージョン・説明・チェックサム(MD5)・実行時間・成否を履歴テーブルに INSERT します。
スクリプトのプレフィックスには以下の種類があります。
| プレフィックス | 用途 | 実行タイミング |
|---|---|---|
V (Versioned) | 通常のマイグレーション | バージョン昇順で一度だけ実行 |
R (Repeatable) | ビュー・関数・ストアド再定義 | チェックサム変化時に毎回実行 |
U (Undo) | ロールバック (Pro/Teams 版のみ) | flyway undo 実行時 |
Liquibase との比較
Flyway は SQL を直接書くシンプルさが特長で、PostgreSQL/MySQL など特定 DB に最適化された SQL を活かせます。Liquibase は changeSet を XML/YAML で抽象化するため DB 非依存のスキーマ記述が可能ですが、学習コストが高くなります。Spring Boot プロジェクトで単一 DB を使う場合は 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を使って環境によって違う設定を安全に切り替える方法 を参照してください。
マイグレーション失敗時の対処
flywayRepair の実行例
失敗レコードが残った状態で flywayRepair を実行すると、次のような出力が得られます。
$ ./gradlew flywayRepair
> Task :flywayRepair
Database: jdbc:postgresql://localhost:5432/mydb (PostgreSQL 15.4)
Successfully repaired schema history table "public"."flyway_schema_history" (execution time 00:00.045s).
Manual cleanup of the remaining effects of the failed migration may still be required.
BUILD SUCCESSFUL
注意したいのは、メッセージにもあるとおり flywayRepair は履歴テーブルを修復するだけで、失敗した DDL/DML の中途半端な適用結果(例えば ALTER TABLE が途中まで走った状態)は自動で巻き戻されない点です。PostgreSQL のように DDL がトランザクション可能な DB ではエラー時に自動ロールバックされますが、MySQL のように DDL がオートコミットされる DB では手動でテーブル状態を確認・修正する必要があります。
履歴テーブルの現状確認には次の SQL が便利です。
SELECT installed_rank, version, description, type, checksum, success, installed_on
FROM flyway_schema_history
ORDER BY installed_rank DESC
LIMIT 10;
マイグレーション実行中にエラーが発生すると、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でコンテナ化する実践ガイド も参考になります。
まとめ
よくある質問 (FAQ)
Flywayとは何ですか?
Flywayは、データベーススキーマの変更履歴をSQLファイルとしてバージョン管理し、アプリケーション起動時に自動適用するマイグレーションツールです。flyway_schema_history テーブルに適用済みスクリプトのバージョン・チェックサム・実行日時を記録するため、開発・ステージング・本番の各環境でスキーマの整合性を保てます。Hibernate の ddl-auto と違い、変更内容を明示的に SQL で管理できる点が本番運用に向いています。
Flywayと Liquibase はどう違いますか?
Flyway は SQL ファイル中心でシンプル、Liquibase は XML/YAML/JSON で DB 非依存の抽象化が可能という違いがあります。Spring Boot プロジェクトで PostgreSQL や MySQL など特定 DB を使い続けるなら Flyway、複数 DB エンジンを切り替える可能性があるなら Liquibase が選ばれることが多いです。
baseline-version=0 と baseline-version=1 の違いは何ですか?
baseline-version=0 は既存スキーマを「何も適用されていない初期状態」として扱い、V1 から全マイグレーションを実行します。baseline-version=1 は V1 を既存スキーマ相当として扱い、V2 以降のみを実行します。すでに本番にテーブルがある状態で Flyway を後付け導入する場合は baseline-version=1 + baseline-on-migrate=true が一般的です。
flywayRepair はいつ実行するべきですか?
以下のケースで使用します。(1) マイグレーション実行中に SQL エラーが発生し flyway_schema_history に success=false レコードが残った場合、(2) 既に適用済みのマイグレーションファイルを開発中に修正してチェックサム不一致が出た場合。./gradlew flywayRepair(または mvn flyway:repair)を実行すると、失敗レコードの削除とチェックサムの再計算が行われます。本番環境では原則実行せず、新バージョンで修正を追加してください。
flyway_schema_history テーブルを手動で編集してもよいですか?
推奨されません。このテーブルは Flyway の整合性チェックの根拠であり、直接 UPDATE/DELETE すると監査証跡が壊れ、以後のマイグレーション挙動が不定になります。開発環境で初期化したい場合は flyway clean、本番で復旧が必要な場合は flywayRepair を使ってください。
Spring Boot で spring.flyway.enabled=true は必須ですか?
不要です。Spring Boot 3.x では org.flywaydb:flyway-core または org.springframework.boot:spring-boot-starter-flyway を依存に追加すれば自動的に有効化されます。無効化したいときだけ spring.flyway.enabled=false を明示します。
Flywayを使うことで、データベーススキーマの変更履歴が明確になり、環境間の一貫性を保てるようになります。手動でSQLを実行するヒューマンエラーを防ぎ、チーム開発の効率も向上します。
バージョン管理のルールを守り、本番適用前に十分な検証を行えば、デプロイのリスクを大幅に減らせます。まずは開発環境で試してみて、チームに合った運用方法を見つけていきましょう。
JPAのエンティティ設計については Spring BootのJPAでエンティティのリレーションシップをマッピングする方法 も合わせてご覧ください。