Spring Boot 4 で Flyway の migration が実行されない(起動は成功する)

Spring Boot 4 で Flyway の migration が実行されない(flyway-core だけでは自動設定が入らない)

FIX SUMMARY verified
Applies when
maven@sha256:2b4496088e7b80ae10a8c9f74e574ea21380325a006ec684532ad6bad5bc7273spring-boot 4.0.0 with flyway-core only (the Flyway auto-configuration moved to the separate spring-boot-flyway module, reached through spring-boot-starter-flyway)upgrading to Spring Boot 4 while keeping a direct flyway-core dependency — the application still starts and logs nothing about Flyway, so the missing schema only surfaces when code first touches a migrated table, typically on a clean database in CI or a new environment

Verified: reproduced in maven@sha256:2b4496088e7b80ae10a8c9f74e574ea21380325a006ec684532ad6bad5bc7273, then the Table "DEMO" not found signature was gone after the fix (exit 0).

Spring Boot を 4 に上げたあと、アプリケーションは今までどおり起動します。 起動ログに Flyway の行は出ず、エラーも警告も出ません。止まるのは、migration で作られるはずだったテーブルに最初にアクセスした時点です。

そこで出るのは、使っているデータベースの「テーブルが無い」エラーです——PostgreSQL なら relation "orders" does not exist、MySQL なら Table 'app.orders' doesn't exist、H2 なら Table "ORDERS" not found のように、テーブル名も文言も自分の環境のものになります。**共通しているのは「起動は成功していて、スキーマだけが無い」**という形です。

Flyway が失敗したのではなく、一度も動いていません。

直し方の要点を先に

flyway-core を spring-boot-starter-flyway に置き換えます。

<!-- 変更前 -->
<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>

<!-- 変更後 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-flyway</artifactId>
</dependency>

Gradle なら次の形です(errfix の検証は Maven で行っており、Gradle では実行していません。依存座標は同じです)。

implementation("org.springframework.boot:spring-boot-starter-flyway")

flyway-core を残したまま starter を足す必要はありません。 starter が flyway-core を推移的に運びます。実測では、Spring Boot 4.0.0 で依存をこの1行だけ入れ替えたところ、同じ migration が実行され、同じクエリが結果を返して終了コード 0 になりました。migration ファイルも application.properties も変えていません。

版を Spring Boot 3 系へ戻す必要もありません。 変わったのは依存の名前であって、Flyway の使い方ではありません。

なぜ Spring Boot 4 で出るのか:自動設定が別モジュールへ分離された

Spring Boot 3.5 までは、flyway-core をクラスパスに置くだけで Boot が Flyway を検出し、起動時に migration を実行していました。この自動設定が、Spring Boot 4.0.0 で spring-boot-flyway という別モジュールへ分離されました。

flyway-core は Flyway 本体(migration を実行するライブラリ)で、Spring Boot の自動設定はそこには入っていません。4.0.0 以降は、自動設定を運ぶモジュールを明示的に依存へ入れる必要があり、その入口が spring-boot-starter-flyway です。

この分離は、コンパイルも起動も止めません。 flyway-core はクラスパスに存在し続けるので、Flyway のクラスを直接参照しているコードもそのまま通ります。欠けるのは「Boot が起動時に Flyway を呼び出す」という配線だけです。エラーではなく配線の不在なので、症状はスキーマが無いこととしてだけ現れます。

Spring Boot 4.0 の移行ガイドは、Flyway を使う場合に直接の依存を spring-boot-starter-flyway へ置き換えるよう案内しています。

Spring BootFlyway の依存起動migration
4.0.0flyway-core のみ成功する実行されない
4.0.0spring-boot-starter-flyway成功する実行される

なぜ起動時ではなく、スキーマへのアクセス時に出るのか

止まる場所が、原因から離れています。

  • 起動は成功するので、ヘルスチェックやコンテナの起動判定は通ります。
  • Flyway 関連のログが出ないので、ログを検索しても手がかりが出ません(失敗ログすら無い)。
  • 最初に落ちるのは、スキーマに触るコードです。エラー文言はデータベースとアクセス層のものになり、Flyway の名前は出てきません。

既存の環境では、テーブルが既に存在するため何も起きないことがあります。migration の効果が現れるのは、新しい migration を足したときか、空のデータベースに対して起動したときです。手元やステージングで発覚せず、新環境の構築時や CI のクリーンな DB で初めて出る、という順序になりやすいのはこのためです。

切り分け

順番に確認してください。下の2つは、先に設定を除外してからでないと判定に使えません。

  • spring.flyway.enabled を確認する — false なら明示的に無効化されているので migration は動きません。依存を替えても直らないので、ここを先に見ます。
  • spring.flyway.table を確認する — 既定は flyway_schema_history ですが、変更していればその名前になります。
  • 履歴テーブルが存在するか確認する(上で確認した名前で)— 無ければ Flyway は動いていません。あるのに最新の migration が当たっていないなら、これとは別の問題(baselineOnMigrate や location の設定など)です。
  • 起動ログに org.flywaydb のロガーが出るか — Flyway が動いていれば org.flywaydb.core.FlywayExecutor の Database: ... や o.f.core.internal.command.DbMigrate の行が出ます。ただし logging.level.org.flywaydb を WARN 以上にしていると、動いていても出ません。 ログレベルを既定に戻してから見てください。

上の設定 3 つを除外したうえで、履歴テーブルもログも無いなら、自動設定が入っていません。この記事の症状です。

  • Liquibase を使っている — Spring Boot 4.0 の移行ガイドは Flyway と Liquibase の両方について、従来の直接依存をそれぞれの starter へ置き換えるよう案内しています。置き換え先は spring-boot-starter-liquibase です。errfix が実測したのは Flyway だけで、Liquibase 側は移行ガイドの記述によります。
  • Spring Boot 3 系のままで migration が動かない — この記事の境界は 4.0.0 での分離なので、別の原因です。

検証環境

  • image: maven(digest 固定 sha256:2b449608...、Java 21.0.11)、ネットワーク有り(Maven Central から取得するため)
  • Spring Boot 4.0.0(spring-boot-starter-parent)、spring-boot-starter-jdbc、H2(runtime)
  • 再現:依存を flyway-core だけにして mvn spring-boot:run → アプリは起動し、migration で作られるはずのテーブルへのクエリが Table "DEMO" not found で停止
  • 修正:同じ Spring Boot 4.0.0 のまま spring-boot-starter-flyway へ置き換えて実行 → 同じ migration が適用され、同じクエリが結果(ROW_COUNT=0)を返して終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は pom.xml の依存 1 行だけで、Spring Boot の版・migration ファイル・アプリケーションコード・データベースはすべて同一です。

検証には H2 を使いました。 原因は Spring Boot 側の自動設定モジュールの分離であって、データベース固有の SQL の違いではありませんが、PostgreSQL や MySQL での実行は確認していません。 上の Table "DEMO" not found は H2 の文言で、検証用のアプリが「自動設定が入らなかったこと」を確実に失敗として出すために、起動後にそのテーブルを問い合わせています。読者が実際に目にする文言は、使っているデータベースとアクセス層のものになります。 自動設定モジュールの分離と置き換え先は、Spring Boot 4.0 の移行ガイドおよび自動設定クラスの一覧で確認しました。

検証(machine-verified)

この修正は maven@sha256:2b4496088e7b80ae10a8c9f74e574ea21380325a006ec684532ad6bad5bc7273 のバージョン固定コンテナ内で再現し、修正後に Table "DEMO" not found のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs jvm/spring-boot4-flyway-core-autoconfig-module-split
● reproduce Table "DEMO" not found present ✓
● apply fix exit 0
● re-run Table "DEMO" not found gone ✓
PASS verified · maven@sha256:2b4496088e7b80ae10a8c9f74e574ea21380325a006ec684532ad6bad5bc7273 · signature gone

確認したのは上のイメージの中だけです。別の環境で直らなかった、記述が違う、という場合は 報告してください(対象と検証イメージは件名・本文に入ります)。