Spring Boot + Reactで記事投稿アプリを作成予定｜Flyway入門：DBマイグレーションの基礎からテーブル作成・データ挿入まで解説してみた

※学習中のため間違っている箇所があるかもしれません。
# はじめに
今回はマイグレーションツールである`Flyway`に関して解説します。
また、依存関係を紹介していなかったため、あわせて紹介します。

## マイグレーションとは
マイグレーションとは「構造」、「バージョン」、「状態の移行」をコードベースで管理し、
テスト・本番・開発環境で再現可能にすること。

コードベースで管理をするために`Flyway`が代表するマイグレーションツールを使用します。

# Flywayとは
`Flyway`はSpring Bootとの親和性が高く、最も広く使われているマイグレーションツールの一つです。
Spring Bootはマイグレーション機能を自前では提供しておらず、FlywayやLiquibaseなどを使うのが一般的です。

本記事では、オープンソースで広く使用されている`Flyway`を解説していきます。

# Flywayの導入
## 依存関係
まず、Dependenciesに以下を記載しましょう。
```gradle
dependencies {
	implementation 'org.flywaydb:flyway-core' //Flyway本体
    implementation 'org.flywaydb:flyway-mysql' //MySQL用のライブラリ
}
```
これを記載することで、Spring Bootに`Flyway`ライブラリが自動的に組み込まれます。

※すでにテーブルを作成している人は以下を`application.property`に追加しましょう。
```properties:application.property
#本番DBなどデータがある場合、初回実行時のみtrueに設定しましょう
#その後、運用中は事故防止のためfalseにしましょう(CICDやテストで誤適用を防ぐため)
spring.flyway.baseline-on-migrate=true 
```
これを記載することで既存のテーブルを起点としてマイグレーションを行ってくれます。
逆にこれがないと既存のDBを破壊する可能性があるため、実行時にエラーが発生するはずです。
ただ、これを使用すると以降のマイグレーションファイル設定で既存DBを上書きしてしまう危険があるので、**初回実行後は必ずfalse**に戻すようにしましょう

上記を記入後、クラスパスを作成します。
以下のクラスパスが必要なのでプロジェクト内に作成してください。
```
db/migration
```
このように`db`フォルダ作成後、その配下に`migration`フォルダを作成すれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/8b77b8af-4a8f-4b05-849a-722ee1fdc8a8.png)
※src/main/resources/db/migrationです。

※場所を変更したい場合は、`application.property`に以下を記載しましょう
```properties
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
```
これを記載することで、デフォルトの値と`/opt/migration`の両方を指定することができます。

マイグレーションのスクリプトは上記で設定したフォルダ内に配置されます。

#### スクリプト例
```
V<VERSION>__<NAME>.sql
```
例：V1__init.sql や V2_1__add_users.sql

## スクリプト:SQL
それでは`migration`フォルダ直下にスクリプトを配置しましょう。
スクリプトには`Java`と`SQL`の2種類で書くことができます。
`JAVA`での記述は読みにくく、初学者には扱いづらいため、基本的には`SQL`だけで十分です。
今回は`SQL`で記載していきます。

今回は以前ER図を作成したテーブル情報をもとに作成していきます。
※各カラムや制約に関しては後日別記事でまとめる予定です。
## V1__テーブル作成
```sql:V1__create_tables.sql
create Table users (
  id int AUTO_INCREMENT not null primary key,
  name varchar(50) NOT NULL,
  email varchar(50) NOT NULL,
  role ENUM('USER', 'ADMIN') NOT NULL,
  created_by varchar(50) NOT NULL,
  updated_by varchar(50),
  created_at timestamp NOT NULL default current_timestamp,
  updated_at timestamp default current_timestamp ON UPDATE current_timestamp,
  delete_flg boolean NOT NULL
);

create Table articles (
  id int AUTO_INCREMENT primary key,
  user_id int NOT NULL,
  title varchar(50) NOT NULL,
  content varchar(255),
  like_cnt int NOT NULL default 0,
  view_cnt int NOT NULL default 0,
  release_flg boolean NOT NULL,
  reservation_day timestamp default current_timestamp,
  created_by varchar(50) NOT NULL,
  updated_by varchar(50),
  created_at timestamp NOT NULL default current_timestamp,
  updated_at timestamp default current_timestamp ON UPDATE current_timestamp,
  delete_flg TINYINT(1) NOT NULL
);

create Table tags (
  tag_id int AUTO_INCREMENT primary key,
  tagname varchar(50) NOT NULL,
  created_by varchar(50) NOT NULL,
  updated_by varchar(50),
  created_at timestamp NOT NULL default current_timestamp,
  updated_at timestamp default current_timestamp ON UPDATE current_timestamp,
  delete_flg TINYINT(1) NOT NULL
);

create Table articles_tags (
  tag_id int NOT NULL,
  article_id int NOT NULL,
  created_at timestamp NOT NULL default current_timestamp,
  PRIMARY KEY(tag_id, article_id),
  FOREIGN KEY(tag_id)
      REFERENCES tags(tag_id)
      ON DELETE CASCADE,
  FOREIGN KEY(article_id)
      REFERENCES articles(id)
      ON DELETE CASCADE
);
```

※簡単にスクリプトに関する解説を記載します。
NOT NULL:データをINSERTする際にNULLの場合エラーになります。
primary key：主キーを指定します。
ENUM(a, b)：指定した文字列のみ挿入できます。
default：挿入する際に値がない場合のデフォルト値を設定します。
current_timestamp：現在時刻を挿入します。
ON UPDATE：アップデート時に自動的に更新日時を上書きする指定です。
FOREIGN KEY：外部キーが存在する場合指定します。
REFERENCES：外部キーが存在するテーブルとカラムを指定します。
ON DELETE CASCADE：これを記述すると、親テーブル(今回はtags,articles)の接続している行が削除されると、子テーブル(今回はarticles_tags)の行も削除されます。

# Flywayの実行
`Flyway`は基本的にSpring Bootを実行することで起動することができます。
実際に実行した後に、DBを確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/4670a061-23bd-48f9-be17-0cc2d09d922a.png)

このようにテーブルが作成されました。
次回からはこのテーブルを起点としてマイグレーションファイルを作成していきます。

※flyway_schema_history
`Flyway`が適用したマイグレーションの履歴が保存されます。

# データ挿入
`Flyway`を使用してデータを挿入していきましょう。
`Flyway`を使用することで、ファイルを共有して簡単に挿入データやテーブル環境をそろえてテストを行うことができます。
注意点としては、テスト環境で使用した`migration`ファイルは、
本番環境でも使用しないよう気を付けましょう。

```sql:R__test_data.sql
INSERT INTO users (name, email, role, created_by, delete_flg) 
VALUES ('root', 'test@test.com', 'ADMIN', 'System', '0');
```
### ファイル構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/b6e4953e-8a21-4b33-a584-a1e95130756d.png)

#### R__について
命名規則としてファイル名の先頭はバージョンを指定すると説明しました。
`R__`と命名することで`Repeatable Migration`(繰り返し実行型)を定義することができます。
通常`migration`ファイルは一度適用すると、二回目以降に読み込まれることはありません。
しかし`R__`を指定することで内容が変更された場合に再実行されます。
※名称を変更しただけでは再実行されません。
これはすべてのバージョン付き(V1,V2など)ファイルが実行された後に実行されます。
テストデータの共有や環境をそろえたい場合に有効です。

## migration実行
ということでSpring Boot実行後、テーブルを確認してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/dc79bdd0-a3f1-476f-a784-469fab160c9a.png)

このように実行データが挿入されています。
DBのカラムを増やしたり、属性を変更するなどの更新では基本的にV2__,V3__といったバージョンを増やして更新していく、テスト時のデータ挿入や毎回同じ環境を使用したい場合はR__を指定して毎回データを挿入するといった仕様用途になります。

ほかにもCLIやGUIなどが存在しており、様々な用途で使用できます。
なお、FlywayにはCLIやGUIツール（Flyway Desktop）も提供されており、より視覚的な操作やCI/CD環境との統合にも対応可能です。詳しくは以下のリンクをご覧ください。
[FLyway:CLI](https://documentation.red-gate.com/fd/command-line-277579359.html)
[Flyway:Desktop](https://www.red-gate.com/products/flyway/community/download/)

# flyway_schema_history
ここまででFlywayがどうマイグレーションを管理しているか確認してみましょう
```sql
SELECT * FROM flyway_schema_history
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155918/cecdb89b-42c5-46b3-b256-93371f695758.png)

一回目の実行で`create tables`、二回目の実行で`test data`が実行されているのがわかります。
このようにいつ、どのマイグレーションファイルが実行されているのか履歴を確認することができます。

# migrationを初期化したい場合
ファイル名称を変更したい場合や一度初期化したい場合は`Flyway`コマンドで初期化する必要があります。
上記CLIを導入し、以下コマンドを実行することで初期化することができます。

```
flyway clean
```
今回`CLI`のセットアップは割愛します。
詳細は今後の記事で紹介予定です。

# 今回使用している依存関係
今回は`gradle`を使用してプロジェクトを作成しています。
初学者の方は`Maven`と`Gradle`で迷った場合は`Gradle`を選択しましょう。
`Maven`を使用している企業は多いですが、初学者だとつまずきやすいため、
簡単に設定できる`Gradle`をお勧めします。
```gradle:build.gradle
dependencies {
    // JPA（DBとオブジェクトのマッピング）機能を使うためのモジュール
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    // Spring Security（ログイン認証や認可）を使うためのモジュール
	implementation 'org.springframework.boot:spring-boot-starter-security'
    
    // Webアプリケーション（REST API）を作成するための基本モジュール
	implementation 'org.springframework.boot:spring-boot-starter-web'

    // バリデーション（@Valid など）を使用するためのモジュール
	implementation 'org.springframework.boot:spring-boot-starter-validation'

    // Flyway（DBマイグレーション）のコアライブラリ
	implementation 'org.flywaydb:flyway-core'

    // MySQL用のFlyway拡張（方言サポートやバージョン制御）
	implementation 'org.flywaydb:flyway-mysql'

    // MySQLに接続するためのJDBCドライバ
	runtimeOnly 'com.mysql:mysql-connector-j'

    // Lombok（ボイラープレートコード削減用。@Getter @Setterなど）
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'

    // Spring Bootのテスト機能を利用するためのモジュール
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

    // Spring Securityのテスト機能（認証関連のテストを補助）
	testImplementation 'org.springframework.security:spring-security-test'

    // JUnitプラットフォームのランチャー（テスト実行用）
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
```

# まとめ
Flywayは、データベースの状態をコードとしてバージョン管理し、どの環境でも同じ構造・データを再現できる強力なツールです。  
Spring Bootと組み合わせることで、アプリ起動時に自動的にマイグレーションを実行でき、開発効率と安全性が向上します。

特にチーム開発やCI/CDを意識したプロジェクトでは、Flywayによるマイグレーション管理は必須のスキルと言えます。

次回以降
- Service,Dto
- Controller
- React

この記事が少しでもSpring Boot初学者の助けになれば幸いです。

[前：Spring Boot + Reactで記事投稿アプリを作成予定｜Service、DTOの機能を洗い出してみた](https://qiita.com/suica1010/items/cc8439cd5b83f04da167)

[次：](https://qiita.com/suica1010/items/6dd7e28826e5059c162e)