SpringBoot(Gradle)+MySQL8.0+CSVで初期DBインポート のアプリを Windows Dockerで動かす

# 目的
* SpringBootで作ったJavaサーバ、DBはMySQLの8.0を使用する
* サーバー起動時に、data.csvとinit.sqlを用意し、init.sqlから`local infile`コマンドを使用してdata.csvで初期DBをインポート
* 他の人の環境で環境構築が大変なので、Dockerを使用することにした
* 結構ハマったり苦労したので共有

# Dockerでどうやるか
* Dockerは1コンテナで1サービスが基本。JavaサーバコンテナとMySQLコンテナの2つを作り、docker-composeで連携させます

# 設定に必要な全体構成図
* root
    * src/main/java
        * @Entity をもったクラスファイル
    * src/main/resources/application.yml
    * docker-compose
        * mysql
            * config/my.cnf
                * 権限は読み取り専用
            * initdb
                * data.csv
                * init.sql
            * Dockerfile
        * spring
            * Dockerfile
    * docker-compose.yml
    * build.gradle
* ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658564/4e7c3556-ef95-6cd5-866e-40b3939669b4.png)


# How
* @Entity をもったクラスファイル
    * GenerationType.AUTO ではなく、GenerationType.IDENTITY を使用してください
    * CSVを事前インポートした後に、新しくデータを追加すると同じIDが重複するという問題が発生します

```
@Entity
@Table(name="shohin")
public class MyData {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(nullable = true, unique = true)
	private Integer id;

```

* application.yml
    * 上がローカルで回すとき、下がdocker-composeで回すときの設定です
    * ローカルのurlが長いのは、何かとエラーがでて対策を入れた結果です

```
server:
  port: 8081

---
spring:
  datasource:
    driverClassName: com.mysql.cj.jdbc.Driver
    password: hogehoge
    url: jdbc:mysql://localhost:3306/hogehoge_db?allowPublicKeyRetrieval=true&useSSL=FALSE&characterEncoding=UTF-8&serverTimezone=JST
    username: hoge
  jpa:
    database: MYSQL
    hibernate:
      ddl-auto: update

---
spring:
  profiles: docker-compose
  datasource:
    url: jdbc:mysql://dbserver/hogehoge_db
    username: hoge
    password: hogehoge
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
```

* my.cnf
    * MySQL8.0から「load data local infile」コマンドがデフォルトオフになりました。local_infile=1で有効にします
    * また、Windows Proの環境のDockerならばこのままで問題ありませんが、Home環境だとパーミッション777のファイルはMySQL8.0が読みにいかないので読み取り専用にプロパティから変更しておきます
        * `[Warning] World-writable config file '/etc/mysql/conf.d/my.cnf' is ignored`
    * 文字コードも調整しておきます

```
[mysqld]
character-set-server=utf8
local_infile=1

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8
local_infile=1
```

* init.sql
    * docker-compose up --build コマンドを叩いたとき、1回目に呼ばれるSQLです
    * IDには「id int AUTO_INCREMENT PRIMARY KEY」を指定してください。 @Entity で指定したカラムにしてください。
    
```
use hogehoge_db;
CREATE TABLE shohin(hogehoge);
load data local infile '/docker-entrypoint-initdb.d/data.csv' INTO TABLE sector FIELDS TERMINATED BY ',';
select * from shohin;
```

* mysql/Dockerfile

```
FROM mysql:8.0
RUN touch /var/log/mysql/mysqld.log
```

* spring/Dockerfile
    * jarのファイル名は、/build/libsの中のjarを指定してください
    * gradle assembleをすればできあがります
        * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658564/c247bc8e-d544-ca32-684a-498216c811ee.png)

```
FROM openjdk:jdk-alpine
VOLUME /tmp
RUN mkdir /app
WORKDIR /app
ENV JAR_TARGET "hogehoge-0.0.1-SNAPSHOT.jar"
ENTRYPOINT ["sh","-c","java -jar -Dspring.profiles.active=docker-compose ./build/libs/${JAR_TARGET}"]
```

* docker-compose.yml
    * データの永続化をすると、mysqlコンテナにデータが残らなくなり、後々CSVを読み込むとき面倒だったのでコメントアウト
    * mysqlコンテナを docker rm hogehoge 等で削除しない限りは残りますので安心してください


```
version: "3"
services:
  dbserver:
    container_name: mysql8.0_hogehoge_db
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: hogehoge_db
      MYSQL_USER: hoge
      MYSQL_PASSWORD: hogehoge
      MYSQL_ROOT_PASSWORD: hogehogehoge
    expose:
      - 3307
    ports:
      - 3307:3307
    volumes:
      # 起動スクリプト
      - ./docker-compose/mysql/initdb:/docker-entrypoint-initdb.d
      # MySQLの設定ファイル
      - ./docker-compose/mysql/config:/etc/mysql/conf.d
      # DBの永続化 アプリコンテナ、DBコンテナを消しても残り続ける。今回はCSVを読みたいのでコメントアウト。コンテナ消すとDB消えるから注意しろな！
      #- mysql_db:/var/lib/mysql
      # logの出力
      - ./log/mysql:/var/log/mysql
  app:
    container_name: hogehoge_app
    build: ./docker-compose/spring
    depends_on:
      - dbserver
    ports:
      - "8081:8081"
    volumes:
      - .:/app
    environment:
      # mysqlの接続設定 host:portはコンテナ名指定
      spring.datasource.driverClassName: "com.mysql.cj.jdbc.Driver"
      spring.datasource.url: "jdbc:mysql://dbserver/hogehoge_db"
      spring.datasource.username: "hoge"
      spring.datasource.password: "hogehoge"
# DBの永続化先
volumes:
  mysql_db:
    driver: local

```

* build.gradle
    * dependenciesにmysqlを追加。以下サンプル

```
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	compile 'org.springframework.boot:spring-boot-starter-web'
	compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
	compile 'org.springframework.boot:spring-boot-devtools'
	compile('org.springframework.boot:spring-boot-starter-data-jpa')
	runtime('mysql:mysql-connector-java')
}
```

# gitで配布するとき
* /build/libsの中のjarがデフォルトgitignoreに入ってると思いますが、gitで管理してください。このjarを元に回るため
* readmeに、mysql\config のmy.cnfを読み取り専用にするように記載してください

# dockerの準備
* dockerをインストール済みのWindowsの用意
    * docker desktopの設定を開き、Resources→File SharingでCドライブを追加する
    * https://qiita.com/kikako/items/7b6301a140cf37a5b7ac

# dockerで動かす
* エクスプローラのファイルよりpowershellを管理者で起動（管理者権限があるユーザなら普通のpowershellでよい）
* docker-compose up --build
    * その後右下に警告がでたら share it を2回押下。2回でるきがする
    * Ctrl Cで止められるが、怖いなら docker-compose up --build -d でバックグラウンド実行
        * down叩くまで動き続ける
* 終わらせるときは同一フォルダで docker-compose down
* 1回目はアプリ起動時にSQLが実行されておらず、起動に失敗すると思う。2回目に起動成功します

# 2回目以降のCSVインポート、既存DB削除等
* mysqlのコンテナを削除して、もう一度docker-compose up --build
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658564/9a530e07-9b26-8a2f-aef8-247206872238.png)
* こうしちゃってもいいかも

```
    * 全コンテナ停止: docker stop $(docker ps -q)
    * 全コンテナ削除：docker rm $(docker ps -q -a)
    * 全イメージ削除: docker rmi $(docker images -q)
```