Spring BootにjOOQ + Flyway + MySQLを導入する方法(Kotlinの場合)
Spring BootにjOOQ + Flyway + MySQLをKotlinで導入する記事があまりなかった&自分の学習の一環として導入する手順を確認し、理解を深めるため記事にしました。
これから新規でサーバーサイドKotlinを始める方の参考に少しでもなれば幸いです。
前提
記事内に出てこない開発環境は以下の通りです。
Gradle:7.3.2
Kotlin:1.5.31
JVM:11.0.14 (Amazon.com Inc. 11.0.14+9-LTS)
Spring Boot:2.6.3
dockerでMySQLを用意する
今回はお手軽に準備ができるdockerを使用し、MySQLを用意しました。
自分が用意したdocker-compose.ymlを参考までに載せておきます。
version: "3.8"
services:
mysql:
container_name: todo-mysql
image: mysql:8.0
restart: always
ports:
- ${MYSQL_PORT}:3306
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DB_NAME}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
docker-compose up -d
で起動すれば、MySQLが利用できるはずです。
また環境変数は.env
を用意し、direnvで読み込んでいます。
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_ROOT_PASSWORD=password
MYSQL_DB_NAME=example
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_OPTIONS=autoReconnect=true&allowPublicKeyRetrieval=true&useSSL=false
MYSQL_URL=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DB_NAME}?${MYSQL_OPTIONS}
Flywayプラグインのセットアップ
今回はFlywayが公式で提供しているGradleプラグインを利用します。
plugins {
id("org.flywaydb.flyway") version "8.0.1"
}
dependencies {
runtimeOnly("mysql:mysql-connector-java")
}
flyway {
url = System.getenv("MYSQL_URL")
user = System.getenv("MYSQL_USER")
password = System.getenv("MYSQL_PASSWORD")
}
これにより、GraldeのtasksにFlywayのtaskが追加されます。
利用方法
マイグレーション用のSQLの準備
まずはマイグレーション用のディレクトリをresourcesに用意します。
デフォルトの設定ではdb/migration
配下にSQLファイルを配置すればOKです。
resources
└── db
└── migration
└── V1.0.0__create_table.sql
ちなみにファイル名はV<Version>__<Description>.sql
という形式が基本になります。
更に詳細を理解したい方は、以下のページが参考になります。
コマンド
あとはマイグレーションを実際に実行していくだけです。
その際に、よく使うコマンドをいくつか紹介しておきます。
$ ./gradlew flywayMigrate // マイグレーションを実行する
$ ./gradlew flywayRepair // スキーマの履歴テーブルを修復する
$ ./gradlew flywayClean // 全テーブルをdropする
その他の情報については、公式のドキュメントを参考にしてみてください。
余談
Spring Bootに詳しい方の中には、flyway-core
を導入すれば起動時にデフォルトでマイグレーションを行ってくれるので良いのでは?といった疑問をもつ方もいるかもしれません。
この反応は当然で自分も最初はflyway-core
を導入して起動できないかと試してみたのですが、後述するjOOQのプラグインとの兼ね合いから採用を諦めました。
ただし、jOOQのプロセスをSpring Bootに上手く組み込むことができれば可能かもしれません。
いずれ時間ができた際には、調べてみたいと思っています。
jOOQプラグインのセットアップ
jOOQは公式のGradleプラグインがないため、公式が紹介しているサードパーティのgradle-jooq-pluginを導入します。
plugins {
id("nu.studer.jooq") version "7.1.1"
}
dependencies {
jooqGenerator("mysql:mysql-connector-java")
jooqGenerator("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
}
jooq {
configurations {
create("main") {
jooqConfiguration.apply {
jdbc.apply {
url = System.getenv("MYSQL_URL")
user = System.getenv("MYSQL_USER")
password = System.getenv("MYSQL_PASSWORD")
}
generator.apply {
name = "org.jooq.codegen.KotlinGenerator"
database.apply {
name = "org.jooq.meta.mysql.MySQLDatabase"
inputSchema = System.getenv("MYSQL_DB_NAME")
excludes = "flyway_schema_history"
}
generate.apply {
isDeprecated = false
isTables = true
}
target.apply {
packageName = "com.example.ktknowledgeTodo.infra.jooq"
directory = "${buildDir}/generated/source/jooq/main"
}
}
}
}
}
}
Flywayと同様に、GraldeのtasksにgenerateJooq
が追加されます。
ただしFlywayと異なり、デフォルトの設定ではbuild
やtest
の実行前にもgenerateJooq
が実行されるようになります。(必要がない場合は設定でオフにもできます。)
ちなみにjooqGenerator("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1")
を追加しているのは、SpringのBOMがjakarta.xml.bind-api
のバージョンをjOOQのプラグイン側がサポートしている3系ではなく2系に強制ダウングレードするせいで起こるエラーを解消するためです。
設定について
今回利用している設定は基本的に公式のExampleをカスタマイズしたものなので、詳細についてはGitHubを参考にしてください。
またjOOQでMySQLのBooleanを扱う場合は別途設定が必要になるのですが、そちらは別の記事で解説をしています。
利用方法
jOOQはデータベースの状態をもとに、ORMのコードを生成してくれます。
以下のコマンドを実行すると、build/generated/source/jooq/main
配下にKotlinのコードが生成されます。
$ ./gradlew generateJooq
生成されたコードの使い方は公式のドキュメントを参考にしてください。
基本的な使い方
基本的にはFlywayでデータベースの状態をバージョン管理しつつ、最新のデータベースの状態をもとにjOOQでリポジトリの実装に必要なコードを生成します。
jOOQで生成されるコードをGit管理するパターンも調査したところあるようですが、Git管理をしてしまうと「データベースの変更に強いというjOOQのメリットが活かしにくい」「チーム開発時にコンフリクトが多発する」といった意見に納得がいったため、自分の実装ではGit管理をしない選択をしています。
Tips
データベースの更新をすると、何度も以下のようなコマンドを打つことになると思います。
$ ./gradlew flywayMigrate
$ ./gradlew generateJooq
しかし、毎回同じコマンドを2回も打つのは手間ですし、何よりタスク名が長いので面倒です。
そのため、build
などのタスク時にも実行されるgenerateJooq
の直前に、flywayMigrate
を実行させるようにタスクを設定しておくと便利です。
以下のようにbuild.gradle.kts
に記述を足すことで、generateJooq
の前にflywayMigrate
が実行されるようになります。
tasks.named("generateJooq") {
dependsOn(tasks.flywayMigrate)
}
Gradleでは-m
オプションを使うとタスクの実行を全てスキップし、実行されるタスクの一覧を確認できます。
例えばbuildを-m
オプションで実行してみると、generateJooq
よりも前にflywayMigrate
が実行されていることが確認できます。
$ ./gradlew -m build
:flywayMigrate SKIPPED
:generateJooq SKIPPED
:compileKotlin SKIPPED
:compileJava SKIPPED
... // 省略
これでデータベースの更新を気にせずbuild
などのタスクを実行できます!
さいごに
以上でSpring BootにjOOQ + Flyway + MySQLを導入できます。
今回の実装ではjOOQとFlywayのGradleプラグインを利用しているので、Spring BootだけでなくKtorなどのフレームワークにも同様の手順で導入が可能だと思います。
Discussion