Slick 3.2.3でMySQLのスキーマコードを自動生成

# はじめに
Slick 3.2.3のコードジェネレータを使ってMySQLのスキーマをコード化する方法をご紹介します．Slickはバージョンアップする度に渡すパラメータや必要な依存が変わり，意外にすんなり使えないので，記事にすることにしました．

# Slickとは
SlickはFRM(Functional Relational Mapping)と呼ばれるライブラリのことで，主にScalaで利用されています．JDBCやORMと同様にデータベースへの接続を楽にするためのライブラリです．

Slickの特徴はSQLを一切書かないでデータベースへのCRUD操作(読み書き)を実現できる点です．そのため，テーブルスキーマをクラスとして記述する必要があります．そこで，わざわざテーブルスキーマをクラス化するのは面倒だ，ということでコードジェネレータが同梱されています．

# コード生成までの流れ
Slickのコードジェネレータを使ってコードを自動生成するためのざっくりとした流れは下記のようになります．
1. 生成するスキーマの準備
2. sbtプロジェクトを作る
3. コードジェネレータ用の設定ファイルを書く
4. コードジェネレータを実行するMainクラスを書く
5. 実行する．以上！

# 準備
MySQLをセットアップし，自動生成したいスキーマを用意します．今回は下記のsqlをコード化します．DBは`test_db`としました．また，ここでは**MySQL 5.7.17**を使います．

```sql:user.sql
CREATE TABLE IF NOT EXISTS test_db.`user`(
  `id` INT UNSIGNED NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `age` TINYINT UNSIGNED,
  `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
MySQLにデータベースとテーブルが作成ができたら準備完了です．

# 手順
MySQLにテーブルを準備できたら，続いてコード自動生成用のプログラムを書いていきます．

## sbtでプロジェクトを作る
sbtプロジェクトの全体像は下記のようになります．プロジェクト名は`slick-codegen-sample`にしました．

```text:slick-codegen-sampleディレクトリ以下の構成
.
├── build.sbt
├── project
│   └── build.properties
└── src
    └── main
        ├── resources
        │   └── codegen.conf
        └── scala
            └── Main.scala
```

プロジェクトの雛形に合わせて`build.properties`や`build.sbt`を記述していきます．

```scala:build.properties
sbt.version = 1.1.6
```

```scala:build.sbt
name := "slick-codegen-sample"

scalaVersion := "2.12.6" // 2.11系のScalaを指定してもOKです

libraryDependencies ++= Seq(
  "com.typesafe.slick" %% "slick" % "3.2.3" ,
  "mysql" % "mysql-connector-java" % "5.1.38", // 利用しているMySQLのver.によって適宜変更して下さい
  "com.typesafe.slick" %% "slick-codegen" % "3.2.3",
  "com.typesafe.slick" %% "slick-hikaricp" % "3.2.3",
  "org.slf4j" % "slf4j-nop" % "1.6.4"
)
```

## コードジェネレータ用の設定ファイルを書く
次のような設定ファイルを書きます．DBの接続情報は環境によって適宜変更して下さい．下記の設定ファイルだと，`./src/main/scala/jp/mattsu`以下にコードが生成されます．

```conf:codegen.conf
codegen {
  test_db {
    profile = "slick.jdbc.MySQLProfile$" // 末尾に$が必要なので注意！
    db {
      dataSourceClass = "slick.jdbc.DatabaseUrlDataSource"
      properties {
        driver = com.mysql.jdbc.Driver
        user = "root" // 環境によって変更して下さい
        url = "jdbc:mysql://localhost/test_db?useSSL=false"
        password = "root" // 環境によって(ry
      }
    }
    codegen.package = "jp.mattsu" // 出力先のパッケージを指定します
    codegen.outputDir = "./src/main/scala" // 出力先のディレクトリを指定します
  }
}
```

## コードジェネレータを実行するMainクラスを書く
Mainのコードを書いてきます．`slick.codegen.SourceCodeGenerator`に引数で各種設定値を渡すこともできますが，ここでは設定ファイルを渡す方法を使います．コード自体は，設定ファイルのURIを取得して，コードジェネレータに渡しているだけなので非常にシンプルです．

```scala:Main.scala
object Main {

  def main(args: Array[String]): Unit = {

    // クラスパスからリソースファイルを取得します
    // file:/省略/slick-codegen-sample/target/scala-2.12/classes/codegen.conf のように取得されます．
    val uri = getClass.getClassLoader.getResource("codegen.conf")

    // スキーマコードを自動生成します．#以降で該当ファイル内のキーを辿っていけます
    // ここではcodegen.conf内のcodegen.test_db以下の設定値を指定しています．
    slick.codegen.SourceCodeGenerator.main(Array(uri.toString + "#codegen.test_db")
    )
  }
}
```

## 実行する
プロジェクトルートで下記コマンドを叩くと，`Tables.scala`というコードが生成されます．

```
$ sbt run
```

# 生成されたコードを見てみる
```Tables.scala
...
/** Entity class storing rows of table User
 *  @param id Database column id SqlType(INT UNSIGNED), PrimaryKey
 *  @param name Database column name SqlType(VARCHAR), Length(255,true)
 *  @param age Database column age SqlType(TINYINT UNSIGNED), Default(None)
 *  @param createdAt Database column created_at SqlType(TIMESTAMP) */
case class UserRow(id: Long, name: String, age: Option[Byte] = None, createdAt: java.sql.Timestamp)
...
```
コードからuserテーブルを触る際は`UserRow`クラスを使ってCRUD操作を行うことになります．`UserRow`を見てみると，テーブルスキーマをいい感じに反映してくれていることがわかります．NULL可な項目はOptionに置換えてくれたりしていますね．
ただ残念なことに`createdAt`にはデフォルト値を設定してくれていません．それに関しては，[slick-codegenの基本使用例、DEFAULT値の付いたTIMESTAMPをOption型として出力する](https://qiita.com/ceudyptula/items/fb14dcea37bb601cd151)に説明が載っていました．

## おわりに
以上でSlick 3.2.3でMySQLのスキーマコードを自動生成できるようになりました！尚，本記事で紹介したコードはhttps://github.com/mattsu6/slick-codegen-sample に上がっています．

何かあればご指摘等頂ければと思います．