前回は SQL テーブルの構成を ER 図を用いて定義しました。
今回は MySQL 上にテーブルを生成します。
DB起動の準備
単に SQL ファイルを直置きしても無視されるだけなので、SQL ファイルを MySQL にロードしてもらうための事前準備を行います。
Dockerコンテナを起動
compose.yaml を開いてください。

現状だと十分な設定がなされていないので、下記内容に変更します。
services:
mysql:
container_name: mysql
image: 'mysql:8.1.0'
environment:
- 'MYSQL_DATABASE=tododatabase'
- 'MYSQL_PASSWORD=secret'
- 'MYSQL_ROOT_PASSWORD=verysecret'
- 'MYSQL_USER=myuser'
volumes:
- mysqldata:/var/lib/mysql
ports:
- "3306:3306"
adminer:
container_name: adminer
hostname: adminer
image: adminer:latest
restart: always
ports:
- 8000:8080
depends_on:
- mysql
volumes:
mysqldata:
name: db_volume
下記変更を適用しました。
- MySQL のバージョンを 8.1.0 に変更
- DB 管理ツール「Adminer」を追加
- volume を「mysqldata」として定義
1のバージョン変更は、後半で扱う Flyway というライブラリが MySQL 8.1 までしか対応していないので、それに合わせています。
2の Adminer は、データベースの中身を容易に把握するために、ブラウザ上で使用するツールです。
3のボリューム定義は、データベースの最新状態をセーブしておく volume を「mysqldata」として定義しています。
次に、「Ctrl」+「Esc」を押して「docker desktop」と検索し、「開く」を押してください。
この画面が表示されたら起動成功です。

最後に、ubuntu 上で下記コマンドを実行します。
github/ozack-todo-app/todoapp$ docker compose up -d
[+] Running 4/4
✔ Network todoapp_default Created 0.0s
✔ Volume "db_volume" Created 0.0s
✔ Container mysql Started 0.5s
✔ Container adminer Started

実行後は Docker Desktop 上の表示も以下に切り替わります。

これでコンテナの起動が完了しました。
build.gradle を変更
build.gradle を開いてください。

flyway の設定を反映します。
大体はスタートガイドに書いてあることをパクって少し追記するだけです。
// gradle スクリプトを実行するために必要な依存関係を追加
buildscript {
dependencies {
// Flyway の MySQL ライブラリをビルドスクリプトに追加
classpath 'org.flywaydb:flyway-mysql:10.0.0'
}
}
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7'
id "org.flywaydb.flyway" version "10.0.0"
}
group = 'com.ozack' // ここはプロジェクト生成時に決めた名前にする
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
// マイグレーションツールの設定
flyway {
// データベースへの接続 URL を指定
url = 'jdbc:mysql://127.0.0.1:3306/tododatabase'
// データベースに接続するためのユーザー名を指定
user = 'myuser'
// データベースに接続するためのパスワードを指定
password = 'secret'
// Flywayが適用されるスキーマ(データベース)を指定
schemas = ['tododatabase']
// Flywayのクリーンアップ(マイグレーションを全て取り消し、データベースを初期状態に戻す設定)を無効
cleanDisabled = false
}
tasks.named('test') {
useJUnitPlatform()
}
// ./gradlew を実行したときに起動するタスクを設定
defaultTasks 'clean', 'build', 'bootRun'
一番最後の行で定義しており、コマンド「./gradlew」を実行したときに作動します。
ビルドファイルの初期化・生成・起動を行うためのコマンドを指定しました。
左側から順番に実行されます。
application.properties を変更
このファイルにも flyway の設定を反映します。

下記内容に変更してください。
spring.application.name=todoapp
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tododatabase?serverTimezone=Asia/Tokyo
spring.datasource.username=myuser
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.flyway.locations=filesystem:src/main/resources/db/migration/table
SQL テーブルを生成
ここから SQL 文をゴリゴリ書きます。
前回作成した以下の ER 図のとおりにテーブルを生成します。
SQLファイルの作成
migration フォルダ内に「table」フォルダを作成してください。
そして、table フォルダ内に5つの SQL ファイルを作成します。

ファイル数がER図のテーブル数と一致してることが分かると思います。
後ほど、このファイルに SQL 文を記載します。
また、バージョン番号「VX.Y.Z」の X を、ER図のテーブル名にある数字と一致させています。
X が同じ数値のテーブルが複数存在する場合は、Yでアルファベット順に番号を付与します。
要するに、ロードする順番を決めているのです。
一方、Zには1か2の数値を割り当てます。
2を使用するのは少し先になりますが、
- テーブル定義ファイル
- 本番データ登録ファイル
に場合わけして付与します。
本番環境では1と2、テスト環境では1のみをロードします。
SQL文の作成
5つの SQL ファイルを完成させます。
-- 権限情報
CREATE TABLE `authority`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '権限ID',
`name` varchar(16) NOT NULL COMMENT '権限名',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
PRIMARY KEY (`id`),
UNIQUE (`name`)
) ENGINE=InnoDB
-- カテゴリー情報
CREATE TABLE `category`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'カテゴリーID',
`name` varchar(16) NOT NULL COMMENT 'カテゴリー名',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
PRIMARY KEY (`id`),
UNIQUE (`name`)
) ENGINE=InnoDB
-- ユーザー情報
CREATE TABLE `user`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'ユーザーID',
`authority_id` bigint unsigned NOT NULL COMMENT '権限ID',
`name` varchar(16) NOT NULL COMMENT 'ユーザー名',
`email` varchar(32) NOT NULL COMMENT 'メールアドレス',
`password` varchar(32) NOT NULL COMMENT 'パスワード',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
PRIMARY KEY (`id`),
FOREIGN KEY (`authority_id`) REFERENCES `authority`(`id`) ON DELETE CASCADE,
UNIQUE (`email`)
) ENGINE=InnoDB
-- Todo 情報
CREATE TABLE `todo`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'TodoID',
`user_id` bigint unsigned NOT NULL COMMENT 'ユーザーID',
`title` varchar(32) NOT NULL COMMENT 'やること内容',
`is_check` tinyint(1) NOT NULL COMMENT 'チェックの有無を示す真偽値',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
PRIMARY KEY (`id`),
FOREIGN KEY (`user_id`) REFERENCES `user`(`id`) ON DELETE CASCADE,
UNIQUE (`user_id`, `title`)
) ENGINE=InnoDB
-- todo と category のマッピング情報
CREATE TABLE `todo_category`
(
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'マッピングID',
`todo_id` bigint unsigned NOT NULL COMMENT 'TodoID',
`category_id` bigint unsigned NOT NULL COMMENT 'カテゴリーID',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '作成日時',
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日時',
PRIMARY KEY (`id`),
FOREIGN KEY (`todo_id`) REFERENCES `todo`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`category_id`) REFERENCES `category`(`id`) ON DELETE CASCADE,
UNIQUE (`todo_id`, `category_id`)
) ENGINE=InnoDB
外部キーに設定できます。
参照元のデータが削除されたら、参照先のデータも削除するという制約です。
既に無いデータを参照されると困るので、今回は全ての外部キーに設定しました。
ただ、権限等を削除した場合は、その権限を保持するユーザーも全て消えてしまったりするため、とりあえず設定すればいいとは一概にいえません。
その他は以下の ER 図と全く同じ構成です。
動作確認
これで MySQL 上にテーブルを生成することができます。
確認するために、ubuntu 上で下記コマンドを実行してください。
./gradlew
Spring Boot の起動が開始されます。

起動後に Docker Desktop を開いて下線をクリックしてください。

Adminer のログイン画面に遷移します。
build.gradle 内の flyway 定義で記載した情報を入力してください。
パスワードは「secret」です。

ログインするとテーブル一覧ページに遷移します。
先ほど作成した5つのテーブルが登録されています。
「flyway_schema_history」はデフォルトで勝手に登録されます。

テーブル名をクリックすると詳細情報を確認できます。

各キーの設定状況や依存関係も把握可能です。
MySQL 上にテーブルを生成することができました👍
エラーの確認方法
ちなみに、エラーメッセージは下記ファイルに記載されます。
build/reports/tests/test/index.html

このHTMLファイルを、ブラウザ上で開くことでエラー詳細を確認できます。

今回は、テーブル定義の変更内容が Flyway に反映されていないエラーです。
下記コマンドを入力して対応します。
github/ozack-todo-app/todoapp$ ./gradlew flywayClean

一見、どこにもエラー内容が出力されていないので戸惑うと思います。
コマンド上ではなくHTMLファイルに出力されるので、覚えておきましょう。
まとめ
今回は、事前に定義した ER 図を用いて SQL テーブルを生成しました。
src
- compose.yaml
- build.gradle
src/main/…/resources
- application.properties
src/main/…/resources/…/table
- フォルダ内の全ファイル
プロジェクト構成は GitHub にプッシュしています。
これでデータベース関連の準備が完了したので、Spring Boot 側の開発に移れます。
なのですが、Spring Boot は基本的な開発スタイルを知っていないと全く分からなかったりするので、次回は SpringBoot での開発方法を説明します。