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 Boot | Flyway の依存 | 起動 | migration |
|---|---|---|---|
| 4.0.0 | flyway-core のみ | 成功する | 実行されない |
| 4.0.0 | spring-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 の移行ガイドおよび自動設定クラスの一覧で確認しました。