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.sql
  • V2__add_email_column.sql
  • V3__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.propertiesapplication-prod.properties を用意して、Profileで切り替えます。詳しくは Spring BootのProfileを使って環境によって違う設定を安全に切り替える方法 を参照してください。

マイグレーション失敗時の対処

マイグレーション実行中にエラーが発生すると、flyway_schema_historysuccess=false のレコードが残ります。

SELECT version, description, success FROM flyway_schema_history WHERE success = false;

チェックサム不一致エラーは、既に適用済みのスクリプトを修正すると発生します。本番環境ではスクリプトを修正せず、新しいバージョンで変更を追加するのが原則です。

開発中に誤ってコミットしたスクリプトを修正したい場合は、次の手順で対処します。

  1. スクリプトを修正する
  2. ./gradlew flywayRepair(または mvn flyway:repair)でチェックサム再計算と失敗レコード削除を実行
  3. アプリケーションを再起動してマイグレーションを再実行

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でエンティティのリレーションシップをマッピングする方法 も合わせてご覧ください。