すでに本番で動いているデータベースに、今からFlywayを導入したいというケースはよくありますよね。新規プロジェクトならV1からマイグレーションを書いていけば済みますが、既存テーブルがすでに存在する環境だと話が変わってきます。

この記事では、baseline-on-migrate を中心に、すでにデータが入っている既存DBへFlywayを安全に後付けする手順に絞って解説します。Flyway自体の基本概念や新規導入手順はSpring BootでFlywayを使ったデータベースマイグレーション管理で扱っているので、そちらを先に読んでおくとスムーズです。checksum不一致の復旧はFlywayのchecksum mismatch対処ガイドを参照してください。

こんな場面でbaselineOnMigrateが必要になる

典型的なのは、users や orders などのテーブルがすでに本番で稼働していて、flyway_schema_history テーブルはまだ存在しない、という状況です。Flywayを依存に追加しただけで何もマイグレーションを書いていない状態ですね。

この状態でいきなりFlywayを有効化すると、Flywayは「まだ何も適用されていない」と判断し V1__init.sql のようなスクリプトを実行しようとします。しかし対象のテーブルはすでに存在するので、CREATE TABLE が重複エラーで失敗し、アプリケーションが起動しなくなります。

新規プロジェクトでのFlyway導入(何もないDBにV1を適用する)とは前提が違います。既存DBへの後付けでは、「今あるテーブル構造をどこかのバージョンとして認識させる」一手間が必要で、それが baseline-on-migrate の役割です。

baselineOnMigrate・baselineVersion・baseline-descriptionの設定

application.yml に次のように設定します。

spring:
  flyway:
    baseline-on-migrate: true
    baseline-version: 1
    baseline-description: "既存スキーマ (2026-10導入前の状態)"

baseline-on-migrate=true にすると、flyway_schema_history が存在しない場合だけbaseline処理が走ります。すでに履歴テーブルがある環境では何も起きないので、新規・既存どちらの環境にも同じ設定を使えます。

baseline-version は既存スキーマをどのバージョンとして扱うかの指定で、デフォルトは 1 です。これから追加する最初のマイグレーションは、このバージョンより大きい番号にする必要があります。baseline-version=1 なら V2__add_email_column.sql から始める形です。

baseline-description は省略可能で、省略時のデフォルト値は << Flyway Baseline >> になります。日付やチケット番号を含めて明示しておくと、後から履歴を見た人が状況を把握しやすくなります。これらのプロパティはSpring Boot 2系以降で標準サポートされています。

Gradle/Mavenプラグインには別途設定が必要

application.yml の spring.flyway.* は、Spring Bootが起動時に自動構成するFlywayインスタンスにしか渡りません。本番適用前のドライラン確認で使うGradleプラグインの flywayInfo タスクは、Spring Bootのコンテキストとは無関係に単独で動くため、spring.flyway.* を読み込みません。

そのため build.gradle 側にも接続情報とbaseline設定を別途書く必要があります。

flyway {
    url = 'jdbc:mysql://localhost:3306/shop_db'
    user = 'app_user'
    password = System.getenv('DB_PASSWORD')
    baselineOnMigrate = true
    baselineVersion = '1'
    baselineDescription = '既存スキーマ (2026-10導入前)'
}

Mavenなら pom.xml の flyway-maven-plugin の <configuration> に同じ項目を書きます。これを忘れると flywayInfo が接続先を見つけられずエラーになります。

flyway infoで本番適用前にドライラン確認する

Gradleプラグインの設定が済んだら、適用前に状態を確認します。

./gradlew flywayInfo
Version | Description                  | Type     | State
1       | 既存スキーマ (2026-10導入前)  | BASELINE | Pending
2       | add email column             | SQL      | Pending

flyway info は読み取り専用で、スキーマには何も書き込みません。想定外のスクリプトが Pending に出てきたら、flyway migrate を実行する前に中身を確認してください。

flyway_schema_historyに作られる最初のbaselineマーカー行

baseline-on-migrate が走ると、flyway_schema_history に次の1行が追加されます。

SELECT version, description, type, success
FROM flyway_schema_history ORDER BY installed_rank;
version | description                  | type     | success
1       | 既存スキーマ (2026-10導入前)  | BASELINE | 1
2       | add email column             | SQL      | 1

type が BASELINE の行がマーカーです。実際にSQLを実行したわけではなく、「ここまでは適用済み」という印だけが記録されます。以降Flywayはこの行より新しいバージョンだけを適用対象として扱い、V1はbaseline、実際に実行されるのはV2からになります。

ステージング環境で検証する

本番に適用する前に、必ずステージングで試しておきます。まずスキーマだけを本番からダンプしてリストアします。

mysqldump -u app_user -p --no-data shop_db > schema.sql
mysql -u app_user -p shop_db_staging < schema.sql

ステージング用の設定ファイルにも同じ値を書き、アプリケーションを起動して flyway_schema_history にbaseline行が1件だけ作られることを確認します。その後マイグレーションを1本追加し、V2以降が正しく適用されることまで確認できれば、同じ設定を本番用Profileに適用して問題ありません。MySQL接続自体の設定が不安な場合はSpring BootでMySQLに接続するapplication.yml設定も確認しておくと安心です。

baselineVersionを誤ると起きる典型的な失敗

よくあるのは、baseline-version を実際のマイグレーションバージョンと同じか、それより大きくしてしまうケースです。baseline-version=2 なのに V2__add_email_column.sql を用意すると、FlywayはV2をbaseline済みとみなしてスキップしてしまい、意図した変更が反映されません。

もう一つは baseline-on-migrate=false(デフォルト)のまま既存DBに適用してしまうケースです。履歴テーブルが存在しない状態で最初のマイグレーションを実行しようとし、既存テーブルとの重複エラーで起動に失敗します。

org.flywaydb.core.api.FlywayException: Found non-empty schema(s) "shop_db" but no schema history table.

このエラーが出たら、baseline-on-migrate が true か、baseline-version が既存スクリプトと衝突していないかを確認してください。

まとめ 新規導入とbaseline導入の違いを整理する

新規プロジェクトでのFlyway導入は、何もないDBにV1から適用していくシンプルな話です。一方、稼働中のテーブルがある既存DBへの後付け導入は、baseline-on-migrate で「今の状態をどこかのバージョンとして認識させる」一手間が必要になります。

baseline-version と baseline-description を設定し、GradleやMavenのプラグイン側にも同じ接続情報とbaseline設定を書いた上で flyway info でPendingの内容を確認し、ステージングで一度試してから本番に反映すれば、既存テーブルを壊すリスクはかなり抑えられます。

導入後にchecksum不一致のエラーに遭遇した場合は、Flywayのchecksum mismatch対処ガイドを参照してください。