Spring-Boot + MySQL + MyBatisを dockerで実行する

# docker-compose で Spring-Boot を起動して MｙBatis を使って MySQL からデータを取ってくるまで

## ディレクトリ構成

    Docker
    ├ docker-compose.yml
    ├ mysql # DB永続化のためのファイル
    |   ├ sql - mysql.cnf
    |   |     └ init.sql
    |   └ settings # ここはコンテナができ次第自動的に作成される
    |
    └ spring # スプリングのプロジェクト
        └ gradle
        └ gradlew
        └ src ...

## 開発手順

1. SpringBoot プロジェクトを作成し適切なディレクトリに配置
1. docker-compose.yml を起動(コンテナが 2 つ起動)
1. gradle のビルド
1. java の実行

## docker-compose.yml

```docker:docker-compose.yml
version: "3"
services:
# SQLのサービス
db:
    image: mysql:8
    container_name: "spring_db"
    ports:
    - "3306:3306"
    volumes:
    - ./mysql/sql/:/docker-entrypoint-initdb.d
    - ./mysql/settings/:/var/lib/mysql
    - ./mysql/sql/mysql.cnf:/etc/mysql/conf.d/my.cnf
    environment:
    MYSQL_DATABASE: "sample" # この場合sampleというデータベースが作成されます
    MYSQL_ROOT_USER: "root" # rootユーザーはパスワードを設定しないとエラーでます
    MYSQL_ROOT_PASSWORD: "root"
    TZ: "Asia/Tokyo"

# springアプリケーションのサービス
spring:
    image: openjdk:11
    container_name: "spring"
    restart: always
    ports:
    - "9090:8080"
    tty: true
    depends_on:
    - db
    volumes:
    - ./my-workbook:/srv:cached
    working_dir: /srv
```

## init.sql

```sql:init.sql
CREATE DATABASE IF NOT EXISTS sample;

CREATE TABLE user(
    user_id serial primary key,
    user_name char(16) NOT NULL,
    password char(16) NOT NULL
);

INSERT INTO user(user_name, password) VALUES('user', 'password');
```

## mysql.cnf

デフォルトが latin1 という文字コードのため日本語対応させる

※なぜか Spring 側で日本語は入れられますが、init.sql 等で記述した日本語は入りません。

```mysql:mysql.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4
```

## application.properties

ローカルと SQL の接続先が変わるので注意

```spring:application.properties
spring.datasource.url=jdbc:mysql://db:3306/sample
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
```

`spring.datasource.url=jdbc:mysql://db:3306/sample`について、`db`は docker-compose.yml のサービス名を指定。最後の`sample`は参照するデータベース名。

## 実行

### Docker コンテナの立ち上げ

docker-compose.yml がある階層に移動して

    docker compose up -d

### Gradle のビルド

ビルドのためにコンテナ内に入る

    docker compose exec spring bash

ビルド ※時間がかかります

    sh gradlew build

### SpringBoot プロジェクトの実行

    java -jar build/libs/spring-0.0.1-SNAPSHOT.jar

        .   ____          _            __ _ _
    /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
    \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
    '  |____| .__|_| |_|_| |_\__, | / / / /
    =========|_|==============|___/=/_/_/_/
    :: Spring Boot ::                (v2.5.7)

spring 実行画面が出たら docker-compose.yml で指定した 9090 ポートにアクセスしてみましょう

http://localhost:9090

データベースに INSERT した user_name が画面に出力するようにしているので、成功しました。

![スクリーンショット 2021-12-23 16.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1110704/b8e4abfd-3732-e4d5-aa52-839c3587ba2c.png)


### ハマったところ

- コンテナは正常に立ち上がってるのに連携ができない

  RowMapper は MyBatis を利用しており、xml ファイルを使用して SQL を書いていました。
  問題はそのディレクトリ配置で、Java 側の HogeMapper.java ファイルと同じパッケージ内に HogeMapper.xml を配置しており、ローカルでは正常に動作するのですが、コンテナ内ではしっかりとパスを指定してあげるか、resources 内に HogeMapper.java と同じパッケージ内(co.jp.example.mapper 等)に配置してあげる必要があるみたいです。

- MySQL に日本語が入らない

  これは上記 mysql.cnf で文字コードを変えることで解決しました。試しに新しいタブを開き、`docker exec -it spring_db bash`で MySQL のコンテナに入り、`mysql -u root -proot`を実行してから`show variables like '%char%';`を入力すると、utf-8mb4 に変わっていると思います。