以下の内容はhttps://kazuhira-r.hatenablog.com/entry/2024/08/29/011522より取得しました。


Flywayのマイグレーションの管理を考えてみる(Spring Bootでのサンプル付き)

これは、なにをしたくて書いたもの?

Flywayのマイグレーションをどう管理するのがいいのかなと悩んだことがあって、ちょっとまとめておこうかなと。

方針?

Gitを使い、ブランチで開発 → マージする、といったフローを組んでいるチーム開発を背景にしています。

こういう感じで考えました。

ちょっとずつ書いていきます。

引用しているFlywayのドキュメントは、Flyway 10.17.2時点の情報で参照しています。

マイグレーションファイルのバージョンは日付をベースにする

Flywayのドキュメントで、マイグレーションのページを見るとバージョニングされたマイグレーションファイル名の例で最初に
V2__Add_new_table.sqlといった例が目に入るので、単純増加な整数的なものを想像してしまいます。

Migrations / Versioned Migrations

ですが、実際にはもっと柔軟にバージョンを付与できます。例も書かれています。

  • 1
  • 001
  • 5.2
  • 1.2.3.4.5.6.7.8.9
  • 205.68
  • 20130115113556
  • 2013.1.15.11.35.56
  • 2013.01.15.11.35.56

単純な増加な整数(もしくはVx.y.zのようなもの)というバージョニングを採用すると、複数人で開発している時にはバージョンの採番で
悩みますし、ブランチのマージのタイミングでバージョンの順序が入れ替わったりして微妙なことになるので、それなら日付をベースにした
バージョニングにした方がいいのかなと思います。

dateコマンドで採番するくらいでもいいのではないでしょうか?

$ date '+%Y%m%d.%H%M%S'
20240828.115208

もちろん、バージョンの後に意味がわかりやすい名前をつけることは必要ですが。

日付をベースにしたバージョニングを使っても、結局マージするタイミングで順番が入れ替わり後からマージされたマイグレーション
バージョンが古く、エラーになるのでは?という話についてはOut Of Orderで対応することもできます。

環境ごとにディレクトリを分ける

Flywayはクライアントによりますが、デフォルトでclasspath:db/migrationJava APIMaven、Gradleの場合)または
filesystem:sqlCLIの場合)配下にあるマイグレーションファイルを参照します。

Locations - Flyway - Product Documentation

これをいつも適用するマイグレーションを含めたディレクトリと、環境別のディレクトリに分けて管理するとよいのかなと思います。
たとえばこんな感じですね。

db
└── migration
    ├── common
    │   ├── ....sql
    │   └── ....sql
    ├── development
    │   ├── ....sql
    │   └── ....sql
    ├── production
    │   ├── ....sql
    │   └── ....sql
    └── staging
         ├── ....sql
         └── ....sql

いつも適用するマイグレーションを含んだものをcommonとすると、プロダクション環境向けには以下の2つをマイグレーションファイルの
配置先として指定するイメージです。

  • classpath:db/migration/common
  • classpath:db/migration/production

マイグレーションファイルの置き場所は分かれますが、ディレクトリの内容をまとめて適用順が決まるのでふつうにバージョンをつけておけば
OKです。

DDL(DCLも?)とデータ(DML)でディレクトリを分ける

環境別にディレクトリを分けた場合、マイグレーションの内容がDDLなのかDMLなのかでディレクトリを分けた方が後々にわかりやすいのかなと
思いまして。

イメージ的にはこんな感じです。

└── db
    └── migration
        ├── data
        │   ├── common
        │   │   ├── ....sql
        │   │   └── ....sql
        │   ├── development
        │   │   ├── ....sql
        │   │   └── ....sql
        │   ├── production
        │   │   ├── ....sql
        │   │   └── ....sql
        │   └── staging
        │        ├── ....sql
        │        └── ....sql
        └── ddl
            ├── common
            │   ├── ....sql
            │   └── ....sql
            ├── development
            │   ├── ....sql
            │   └── ....sql
            ├── production
            │   ├── ....sql
            │   └── ....sql
            └── staging
                 ├── xxx.sql
                 └── xxx.sql

DDLがあまり環境別に変わることはないような気はしますが、DCL(DDLではないですが)のようなケースを考えると分けておいた方が
いいのかもしれない、くらいです。 DMLdataに置きます。こちらは登録するデータなどになるので、環境別に分けたくなると思います。

使い方は環境別にディレクトリを分けた場合の拡張のようなものなので、プロダクション環境向けの場合は以下の4つを
マイグレーションファイルの配置先として指定するイメージです。

  • classpath:db/migration/ddl/common
  • classpath:db/migration/ddl/production
  • classpath:db/migration/data/common
  • classpath:db/migration/data/production

DDLは全環境で同じなら、こういうのでもいいかもですね。

└── db
    └── migration
        ├── data
        │   ├── common
        │   │   ├── ....sql
        │   │   └── ....sql
        │   ├── development
        │   │   ├── ....sql
        │   │   └── ....sql
        │   ├── production
        │   │   ├── ....sql
        │   │   └── ....sql
        │   └── staging
        │        ├── ....sql
        │        └── ....sql
        └── ddl
             ├── ....sql
             └── ....sql

指定はこちら。

  • classpath:db/migration/ddl
  • classpath:db/migration/data/common
  • classpath:db/migration/data/production

プロダクション用のデータはGitリポジトリー側には含めたくないという場合は、実行時にマウントする前提にしてそのディレクトリを指定、
でしょうか。

(気になるなら)Out Of Orderを許可する

バージョンの時に少し触れましたが、Flywayはマイグレーションを適用した後に最新のバージョンよりも古いバージョンのマイグレーション
ファイルを追加するとデフォルトではエラーになります。

これはチーム開発をしている時に、マイグレーションを含む修正をマージする順番が入れ替わったりした時などに発生します。

たとえば20240820日付のマイグレーションファイルを含む修正と、20240821日付のマイグレーションファイルを含む修正を並行して
行っていたとします。この時、バージョンが新しい20240821日付のマイグレーションが先にマージ・適用されてしまうと、後から取り込まれた
20240820日付のマイグレーションは適用時にエラーになります。

この挙動を変更するのがOut Of Orderです。

Out Of Order - Flyway - Product Documentation

並行して開発している時のマイグレーションの内容に依存関係がないことが前提ですが、Out Of Orderをtrueにすると古いバージョンの
マイグレーションが追加されても適用してくれます。

というわけで

ここまでいろいろ並べてみましたが、簡単にサンプルを作っておこうと思います。

サンプルはSpring Bootで作ることにして、マイグレーションファイルの置き場所はこの構造にすることにします。

└── src
    └── main
        └── resources
            └── db
                └── migration
                    ├── data
                    │   ├── common
                    │   │   ├── ....sql
                    │   │   └── ....sql
                    │   ├── development
                    │   │   ├── ....sql
                    │   │   └── ....sql
                    │   ├── production
                    │   │   ├── ....sql
                    │   │   └── ....sql
                    │   └── staging
                    │        ├── ....sql
                    │        └── ....sql
                    └── ddl
                        ├── ....sql
                        └── ....sql

環境

今回の環境はこちら。

$ java --version
openjdk 21.0.4 2024-07-16
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-119-generic", arch: "amd64", family: "unix"

デーベースにはMySQLを使用します。MySQLサーバーは172.17.0.2で動作しているものとします。

 MySQL  localhost:3306 ssl  practice  SQL > select version();
+-----------+
| version() |
+-----------+
| 8.4.2     |
+-----------+
1 row in set (0.0003 sec)

複数環境を扱うサンプルも作ろうと思うのですが、これについては複数のデータベースまでは用意せず、テーブルをdropして環境を
リセットしながら試していこうと思います。

Spring Bootプロジェクトを作成する

それでは、Flywayを使うSpring Bootプロジェクトを作成します。

Flywayが実行できればよいので、依存関係にはjdbcflywaymysqlを含めます。

$ curl -s https://start.spring.io/starter.tgz \
  -d bootVersion=3.3.3 \
  -d javaVersion=21 \
  -d type=maven-project \
  -d name=flyway-migrations \
  -d groupId=org.littlewings \
  -d artifactId=flyway-migrations \
  -d version=0.0.1-SNAPSHOT \
  -d packageName=org.littlewings.spring.flyway \
  -d dependencies=jdbc,flyway,mysql \
  -d baseDir=flyway-migrations | tar zxvf -

生成されたプロジェクト内に移動。

$ cd flyway-migrations

依存関係などはこのようになっています。

        <properties>
                <java.version>21</java.version>
        </properties>
        <dependencies>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jdbc</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.flywaydb</groupId>
                        <artifactId>flyway-core</artifactId>
                </dependency>
                <dependency>
                        <groupId>org.flywaydb</groupId>
                        <artifactId>flyway-mysql</artifactId>
                </dependency>

                <dependency>
                        <groupId>com.mysql</groupId>
                        <artifactId>mysql-connector-j</artifactId>
                        <scope>runtime</scope>
                </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-test</artifactId>
                        <scope>test</scope>
                </dependency>
        </dependencies>

        <build>
                <plugins>
                        <plugin>
                                <groupId>org.springframework.boot</groupId>
                                <artifactId>spring-boot-maven-plugin</artifactId>
                        </plugin>
                </plugins>
        </build>

生成されたディレクトリおよびファイル。Flyway用のディレクトリが最初からありますね。

$ tree -a
.
├── .gitignore
├── .mvn
│   └── wrapper
│       └── maven-wrapper.properties
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── org
    │   │       └── littlewings
    │   │           └── spring
    │   │               └── flyway
    │   │                   └── FlywayMigrationsApplication.java
    │   └── resources
    │       ├── application.properties
    │       └── db
    │           └── migration
    └── test
        └── java
            └── org
                └── littlewings
                    └── spring
                        └── flyway
                            └── FlywayMigrationsApplicationTests.java

18 directories, 9 files

自動生成されたJavaソースコードは削除しておきます。

$ rm src/main/java/org/littlewings/spring/flyway/FlywayMigrationsApplication.java src/test/java/org/littlewings/spring/flyway/FlywayMigrationsApplicationTests.java

mainメソッドを持ったクラスの作成。

src/main/java/org/littlewings/spring/flyway/App.java

package org.littlewings.spring.flyway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String... args) {
        SpringApplication.run(App.class, args);
    }
}

application.propertiesには、ひとまずデータベース接続に関する情報を定義しておきます。

src/main/resources/application.properties

spring.application.name=flyway-migrations

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
spring.datasource.username=kazuhira
spring.datasource.password=password

Flywayの設定とマイグレーションの作成

では、Flywayに関する設定とマイグレーションの作成を行っていきましょう。

まずはディレクトリの作成。

$ mkdir src/main/resources/db/migration/{ddl,data}
$ mkdir src/main/resources/db/migration/data/{common,development,production}

環境数は2つにしておきましょう。

まずはこう用意しました。

$ tree src/main/resources/db/migration
src/main/resources/db/migration
├── data
│   ├── common
│   │   └── V20240825.150000__add_people_data.sql
│   ├── development
│   │   └── V20240825.180000__add_people_data.sql
│   └── production
│       └── V20240825.210000__add_people_data.sql
└── ddl
    ├── V20240825.120000__create_person_table.sql
    └── V20240827.150000__create_book_table.sql

5 directories, 5 files

DDLは人と書籍。

src/main/resources/db/migration/ddl/V20240825.120000__create_person_table.sql

create table person(
  id int,
  last_name varchar(20),
  first_name varchar(20),
  age int
  primary key(id)
);

src/main/resources/db/migration/ddl/V20240827.150000__create_book_table.sql

create table book (
  isbn varchar(14),
  title varchar(200),
  price int,
  primary key(isbn)
);

バージョンは少し飛ばしてあります。

データは、共通に磯野家の人たち(親除く)。

src/main/resources/db/migration/data/common/V20240825.150000__add_people_data.sql

insert into person(id, last_name, first_name, age)
values(1, 'フグ田', 'サザエ', 24);
insert into person(id, last_name, first_name, age)
values(2, 'フグ田', 'マスオ', 28);
insert into person(id, last_name, first_name, age)
values(3, '磯野', 'カツオ', 11);
insert into person(id, last_name, first_name, age)
values(4, '磯野', 'ワカメ', 9);
insert into person(id, last_name, first_name, age)
values(5, 'フグ田', 'タラオ', 3);

developmentに波野家。

src/main/resources/db/migration/data/development/V20240825.180000__add_people_data.sql

insert into person(id, last_name, first_name, age)
values(6, '波野', 'ノリスケ', 24);
insert into person(id, last_name, first_name, age)
values(7, '波野', 'タイコ', 22);
insert into person(id, last_name, first_name, age)
values(8, '波野', 'イクラ', 1);

productionだと磯野家が全員揃います。

src/main/resources/db/migration/data/production/V20240825.210000__add_people_data.sql

insert into person(id, last_name, first_name, age)
values(6, '磯野', '波平', 54);
insert into person(id, last_name, first_name, age)
values(7, '磯野', 'フネ', 50);

データの良し悪しについては置いておきます…。

これに合わせて、Flywayの設定を行います。

Common Application Properties / Data Migration Properties

こんな感じになりました。

src/main/resources/application.properties

spring.application.name=flyway-migrations

spring.datasource.url=jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin
spring.datasource.username=kazuhira
spring.datasource.password=password

## Flyway
spring.flyway.fail-on-missing-locations=true
### マイグレーションの配置先
spring.flyway.locations=classpath:db/migration/ddl,classpath:db/migration/data/common,classpath:db/migration/data/development
### Out Of Order
spring.flyway.out-of-order=false
### マイグレーションの命名規則の確認
spring.flyway.validate-migration-naming=true

ひとまずポイントはここだけですね。

### マイグレーションの配置先
spring.flyway.locations=classpath:db/migration/ddl,classpath:db/migration/data/common,classpath:db/migration/data/development

以下の3つをマイグレーションの配置先に指定しています。

  • classpath:db/migration/ddl
  • classpath:db/migration/data/common
  • classpath:db/migration/data/development

では、パッケージングして実行。

$ mvn package
$ java -jar target/flyway-migrations-0.0.1-SNAPSHOT.jar

Flywayが実行されました。

2024-08-28T15:45:12.997+09:00  INFO 26843 --- [flyway-migrations] [           main] org.flywaydb.core.FlywayExecutor         : Database: jdbc:mysql://172.17.0.2:3306/practice (MySQL 8.4)
2024-08-28T15:45:13.145+09:00  WARN 26843 --- [flyway-migrations] [           main] o.f.c.internal.database.base.Database    : Flyway upgrade recommended: MySQL 8.4 is newer than this version of Flyway and support has not been tested. The latest supported version of MySQL is 8.1.
2024-08-28T15:45:13.199+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Schema history table `practice`.`flyway_schema_history` does not exist yet
2024-08-28T15:45:13.206+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbValidate     : Successfully validated 4 migrations (execution time 00:00.044s)
2024-08-28T15:45:13.265+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Creating Schema History table `practice`.`flyway_schema_history` ...
2024-08-28T15:45:13.540+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `practice`: << Empty Schema >>
2024-08-28T15:45:13.552+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.120000 - create person table"
2024-08-28T15:45:13.738+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.150000 - add people data"
2024-08-28T15:45:13.812+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.180000 - add people data"
2024-08-28T15:45:13.855+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240827.150000 - create book table"
2024-08-28T15:45:14.012+09:00  INFO 26843 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 4 migrations to schema `practice`, now at version v20240827.150000 (execution time 00:00.254s)

テーブルの状態を確認してみます。

 MySQL  localhost:3306 ssl  practice  SQL > show tables;
+-----------------------+
| Tables_in_practice    |
+-----------------------+
| book                  |
| flyway_schema_history |
| person                |
+-----------------------+
3 rows in set (0.0014 sec)

適用されたマイグレーションの一覧。

 MySQL  localhost:3306 ssl  practice  SQL > select * from flyway_schema_history;
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version         | description         | type | script                                    | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 20240825.120000 | create person table | SQL  | V20240825.120000__create_person_table.sql | -123870336 | kazuhira     | 2024-08-28 06:45:13 |            114 |       1 |
|              2 | 20240825.150000 | add people data     | SQL  | V20240825.150000__add_people_data.sql     | -181951584 | kazuhira     | 2024-08-28 06:45:13 |             11 |       1 |
|              3 | 20240825.180000 | add people data     | SQL  | V20240825.180000__add_people_data.sql     | -745607530 | kazuhira     | 2024-08-28 06:45:13 |             32 |       1 |
|              4 | 20240827.150000 | create book table   | SQL  | V20240827.150000__create_book_table.sql   | 1689297802 | kazuhira     | 2024-08-28 06:45:13 |             97 |       1 |
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
4 rows in set (0.0005 sec)

DDLとcommonとdevelopmentのデータのみが適用されているはずです。

データで確認してみましょう。

 MySQL  localhost:3306 ssl  practice  SQL > select * from person;
+----+-----------+------------+-----+
| id | last_name | first_name | age |
+----+-----------+------------+-----+
|  1 | フグ田    | サザエ     |  24 |
|  2 | フグ田    | マスオ     |  28 |
|  3 | 磯野      | カツオ     |  11 |
|  4 | 磯野      | ワカメ     |   9 |
|  5 | フグ田    | タラオ     |   3 |
|  6 | 波野      | ノリスケ   |  24 |
|  7 | 波野      | タイコ     |  22 |
|  8 | 波野      | イクラ     |   1 |
+----+-----------+------------+-----+
8 rows in set (0.0005 sec)

OKですね。

もうひとつの方にはデータは入れていません。

 MySQL  localhost:3306 ssl  practice  SQL > select * from book;
Empty set (0.0010 sec)

ここで、すべてのテーブルをdropします。

 MySQL  localhost:3306 ssl  practice  SQL > drop table flyway_schema_history;drop table person;drop table book;

マイグレーションの配置先を、環境変数で切り替えてみましょう。

$ export SPRING_FLYWAY_LOCATIONS=classpath:db/migration/ddl,classpath:db/migration/data/common,classpath:db/migration/data/production

つまり、マイグレーションの配置先はこうなっています。

  • classpath:db/migration/ddl
  • classpath:db/migration/data/common
  • classpath:db/migration/data/production

developmentproductionに変わったわけですね。

実行します。

$ java -jar target/flyway-migrations-0.0.1-SNAPSHOT.jar

実行時のログ。

2024-08-28T15:49:09.550+09:00  INFO 27033 --- [flyway-migrations] [           main] org.flywaydb.core.FlywayExecutor         : Database: jdbc:mysql://172.17.0.2:3306/practice (MySQL 8.4)
2024-08-28T15:49:09.653+09:00  WARN 27033 --- [flyway-migrations] [           main] o.f.c.internal.database.base.Database    : Flyway upgrade recommended: MySQL 8.4 is newer than this version of Flyway and support has not been tested. The latest supported version of MySQL is 8.1.
2024-08-28T15:49:09.696+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Schema history table `practice`.`flyway_schema_history` does not exist yet
2024-08-28T15:49:09.702+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbValidate     : Successfully validated 4 migrations (execution time 00:00.032s)
2024-08-28T15:49:09.731+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.c.i.s.JdbcTableSchemaHistory         : Creating Schema History table `practice`.`flyway_schema_history` ...
2024-08-28T15:49:10.004+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `practice`: << Empty Schema >>
2024-08-28T15:49:10.018+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.120000 - create person table"
2024-08-28T15:49:10.173+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.150000 - add people data"
2024-08-28T15:49:10.224+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240825.210000 - add people data"
2024-08-28T15:49:10.341+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240827.150000 - create book table"
2024-08-28T15:49:10.542+09:00  INFO 27033 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 4 migrations to schema `practice`, now at version v20240827.150000 (execution time 00:00.253s)

適用されたマイグレーションの一覧。

 MySQL  localhost:3306 ssl  practice  SQL > select * from flyway_schema_history;
+----------------+-----------------+---------------------+------+-------------------------------------------+-------------+--------------+---------------------+----------------+---------+
| installed_rank | version         | description         | type | script                                    | checksum    | installed_by | installed_on        | execution_time | success |
+----------------+-----------------+---------------------+------+-------------------------------------------+-------------+--------------+---------------------+----------------+---------+
|              1 | 20240825.120000 | create person table | SQL  | V20240825.120000__create_person_table.sql |  -123870336 | kazuhira     | 2024-08-28 06:49:10 |            106 |       1 |
|              2 | 20240825.150000 | add people data     | SQL  | V20240825.150000__add_people_data.sql     |  -181951584 | kazuhira     | 2024-08-28 06:49:10 |             11 |       1 |
|              3 | 20240825.210000 | add people data     | SQL  | V20240825.210000__add_people_data.sql     | -1450894877 | kazuhira     | 2024-08-28 06:49:10 |              7 |       1 |
|              4 | 20240827.150000 | create book table   | SQL  | V20240827.150000__create_book_table.sql   |  1689297802 | kazuhira     | 2024-08-28 06:49:10 |            129 |       1 |
+----------------+-----------------+---------------------+------+-------------------------------------------+-------------+--------------+---------------------+----------------+---------+
4 rows in set (0.0005 sec)

データを確認してみます。

 MySQL  localhost:3306 ssl  practice  SQL > select * from person;
+----+-----------+------------+-----+
| id | last_name | first_name | age |
+----+-----------+------------+-----+
|  1 | フグ田    | サザエ     |  24 |
|  2 | フグ田    | マスオ     |  28 |
|  3 | 磯野      | カツオ     |  11 |
|  4 | 磯野      | ワカメ     |   9 |
|  5 | フグ田    | タラオ     |   3 |
|  6 | 磯野      | 波平       |  54 |
|  7 | 磯野      | フネ       |  50 |
+----+-----------+------------+-----+
7 rows in set (0.0004 sec)

development用のデータはなくなり、productionのデータが追加されました。

OKですね。

ところで、よく見るとFlywayはMySQL 8.4をまだサポートしていないみたいですね…。

2024-08-28T15:45:13.145+09:00  WARN 26843 --- [flyway-migrations] [           main] o.f.c.internal.database.base.Database    : Flyway upgrade recommended: MySQL 8.4 is newer than this version of Flyway and support has not been tested. The latest supported version of MySQL is 8.1.

確認時点だと、確かにそうでした。

Verified Versions: 5.7, 8.0, 8.1

MySQL - Flyway - Product Documentation

Out Of Orderを試す

最後にOut Of Orderを試してみましょう。

マイグレーションは、developmentで適用済みとします。

つまり、この状態ですね。

 MySQL  localhost:3306 ssl  practice  SQL > select * from flyway_schema_history;
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version         | description         | type | script                                    | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 20240825.120000 | create person table | SQL  | V20240825.120000__create_person_table.sql | -123870336 | kazuhira     | 2024-08-28 06:53:31 |            112 |       1 |
|              2 | 20240825.150000 | add people data     | SQL  | V20240825.150000__add_people_data.sql     | -181951584 | kazuhira     | 2024-08-28 06:53:31 |              9 |       1 |
|              3 | 20240825.180000 | add people data     | SQL  | V20240825.180000__add_people_data.sql     | -745607530 | kazuhira     | 2024-08-28 06:53:31 |              6 |       1 |
|              4 | 20240827.150000 | create book table   | SQL  | V20240827.150000__create_book_table.sql   | 1689297802 | kazuhira     | 2024-08-28 06:53:31 |             82 |       1 |
+----------------+-----------------+---------------------+------+-------------------------------------------+------------+--------------+---------------------+----------------+---------+
4 rows in set (0.0004 sec)


 MySQL  localhost:3306 ssl  practice  SQL > select * from person;
+----+-----------+------------+-----+
| id | last_name | first_name | age |
+----+-----------+------------+-----+
|  1 | フグ田    | サザエ     |  24 |
|  2 | フグ田    | マスオ     |  28 |
|  3 | 磯野      | カツオ     |  11 |
|  4 | 磯野      | ワカメ     |   9 |
|  5 | フグ田    | タラオ     |   3 |
|  6 | 波野      | ノリスケ   |  24 |
|  7 | 波野      | タイコ     |  22 |
|  8 | 波野      | イクラ     |   1 |
+----+-----------+------------+-----+
8 rows in set (0.0005 sec)

ここで最後のマイグレーションファイルの日付は20240827でしたが、20240826のものを割り込ませてみます。

src/main/resources/db/migration/ddl/V20240826.180000__create_category_table.sql

create table category (
  id varchar(3),
  name varchar(20),
  primary key(id)
);

パッケージングして実行。

$ mvn package
$ java -jar target/flyway-migrations-0.0.1-SNAPSHOT.jar

すると、未適用のマイグレーションが検出されたということで失敗します。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Validate failed: Migrations have failed validation
Detected resolved migration not applied to database: 20240826.180000.
To ignore this migration, set -ignoreMigrationPatterns='*:ignored'. To allow executing this migration, set -outOfOrder=true.
Need more flexibility with validation rules? Learn more: https://rd.gt/3AbJUZE
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1806) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:313) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:971) ~[spring-context-6.1.12.jar!/:6.1.12]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.1.12.jar!/:6.1.12]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.3.jar!/:3.3.3]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.3.jar!/:3.3.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.3.jar!/:3.3.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.3.jar!/:3.3.3]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.3.jar!/:3.3.3]
        at org.littlewings.spring.flyway.App.main(App.java:9) ~[!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
        at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:102) ~[flyway-migrations-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:64) ~[flyway-migrations-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:40) ~[flyway-migrations-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.flywaydb.core.api.exception.FlywayValidateException: Validate failed: Migrations have failed validation
Detected resolved migration not applied to database: 20240826.180000.
To ignore this migration, set -ignoreMigrationPatterns='*:ignored'. To allow executing this migration, set -outOfOrder=true.
Need more flexibility with validation rules? Learn more: https://rd.gt/3AbJUZE
        at org.flywaydb.core.Flyway.lambda$migrate$0(Flyway.java:160) ~[flyway-core-10.10.0.jar!/:na]
        at org.flywaydb.core.FlywayExecutor.execute(FlywayExecutor.java:205) ~[flyway-core-10.10.0.jar!/:na]
        at org.flywaydb.core.Flyway.migrate(Flyway.java:147) ~[flyway-core-10.10.0.jar!/:na]
        at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66) ~[spring-boot-autoconfigure-3.3.3.jar!/:3.3.3]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853) ~[spring-beans-6.1.12.jar!/:6.1.12]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802) ~[spring-beans-6.1.12.jar!/:6.1.12]
        ... 22 common frames omitted

この部分ですね。

Validate failed: Migrations have failed validation
Detected resolved migration not applied to database: 20240826.180000.

これをそのまま適用してもいいよという場合は、メッセージに書かれているように無視するかOut Of Orderをtrueにします。

To ignore this migration, set -ignoreMigrationPatterns='*:ignored'. To allow executing this migration, set -outOfOrder=true.

今回はOut Of Orderをtrueにしましょう。application.propertiesをこう変更します。

### Out Of Order
spring.flyway.out-of-order=true

環境変数で指定したりして、一時的に切り替えてもよいでしょう。

$ export SPRING_FLYWAY_OUTOFORDER=true

application.propertiesを変更した場合は、パッケージングして)実行。

$ mvn package
$ java -jar target/flyway-migrations-0.0.1-SNAPSHOT.jar

「outOfOrder mode is active.」と表示され、マイグレーションを適用してくれます。

2024-08-28T15:59:20.540+09:00  INFO 27655 --- [flyway-migrations] [           main] org.flywaydb.core.FlywayExecutor         : Database: jdbc:mysql://172.17.0.2:3306/practice (MySQL 8.4)
2024-08-28T15:59:20.633+09:00  WARN 27655 --- [flyway-migrations] [           main] o.f.c.internal.database.base.Database    : Flyway upgrade recommended: MySQL 8.4 is newer than this version of Flyway and support has not been tested. The latest supported version of MySQL is 8.1.
2024-08-28T15:59:20.682+09:00  INFO 27655 --- [flyway-migrations] [           main] o.f.core.internal.command.DbValidate     : Successfully validated 5 migrations (execution time 00:00.037s)
2024-08-28T15:59:20.702+09:00  INFO 27655 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Current version of schema `practice`: 20240827.150000
2024-08-28T15:59:20.702+09:00  WARN 27655 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : outOfOrder mode is active. Migration of schema `practice` may not be reproducible.
2024-08-28T15:59:20.724+09:00  INFO 27655 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Migrating schema `practice` to version "20240826.180000 - create category table" [out of order]
2024-08-28T15:59:21.088+09:00  INFO 27655 --- [flyway-migrations] [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema `practice`, now at version v20240826.180000 (execution time 00:00.219s)

確認してみましょう。

 MySQL  localhost:3306 ssl  practice  SQL > show tables;
+-----------------------+
| Tables_in_practice    |
+-----------------------+
| book                  |
| category              |
| flyway_schema_history |
| person                |
+-----------------------+
4 rows in set (0.0013 sec)

テーブルはできています。

適用されたマイグレーションの一覧。

 MySQL  localhost:3306 ssl  practice  SQL > select * from flyway_schema_history;
+----------------+-----------------+-----------------------+------+---------------------------------------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version         | description           | type | script                                      | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+-----------------+-----------------------+------+---------------------------------------------+------------+--------------+---------------------+----------------+---------+
|              1 | 20240825.120000 | create person table   | SQL  | V20240825.120000__create_person_table.sql   | -123870336 | kazuhira     | 2024-08-28 06:53:31 |            112 |       1 |
|              2 | 20240825.150000 | add people data       | SQL  | V20240825.150000__add_people_data.sql       | -181951584 | kazuhira     | 2024-08-28 06:53:31 |              9 |       1 |
|              3 | 20240825.180000 | add people data       | SQL  | V20240825.180000__add_people_data.sql       | -745607530 | kazuhira     | 2024-08-28 06:53:31 |              6 |       1 |
|              4 | 20240827.150000 | create book table     | SQL  | V20240827.150000__create_book_table.sql     | 1689297802 | kazuhira     | 2024-08-28 06:53:31 |             82 |       1 |
|              5 | 20240826.180000 | create category table | SQL  | V20240826.180000__create_category_table.sql | 1347572117 | kazuhira     | 2024-08-28 06:59:20 |            219 |       1 |
+----------------+-----------------+-----------------------+------+---------------------------------------------+------------+--------------+---------------------+----------------+---------+
5 rows in set (0.0005 sec)

古い日付のマイグレーションのinstalled_rankが最新になっているものの、適用されているようです。

テーブルにもアクセスできます。

 MySQL  localhost:3306 ssl  practice  SQL > select * from category;
Empty set (0.0018 sec)

というわけで、Out Of Orderの効果を確認できました。

おわりに

Flywayのマイグレーションの管理ということで、バージョンの付け方やマイグレーションの配置先などをいろいろ書いてみました。
このやり方で微妙に思うところはあったりもするのですが、いろいろ考えてこの付近が妥協点かなとも思ったり。

参考になればということもありますが、自分はこのあたりをベースに考えたいと思います。




以上の内容はhttps://kazuhira-r.hatenablog.com/entry/2024/08/29/011522より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14