MysqlでScalikeJDBC入門

# 目的
scalikejdbcをMysqlで動かしてみます。これからscalikejdbcを始める方向けです。 

解説したコード全体はこちら： [umeneri/scalikejdbc-example: example for scalikejdbc](https://github.com/umeneri/scalikejdbc-example)

# 参考
- [scalikejdbc](https://github.com/scalikejdbc/scalikejdbc)
- [コード自動生成](https://github.com/scalikejdbc/scalikejdbc-cookbook/blob/master/ja/09_source_code_generator.md)

# 環境
- intellij community edition 2018.1
- scalaVersion 2.12.7
- sbt.version 1.2.6
- scalikejdbc 3.3.2

# 前準備
新規or既存のscalaプロジェクトを立ち上げておきます。

intelljの場合、File > New.. > Projectから、sbtのプロジェクトを作っておきます。

# 全体の流れ
1. mysqlのセットアップ
1. テーブルの作成
1. コード自動生成
1. テスト実行 → こちらは別記事 

# mysqlの立ち上げ
ローカルのmysqlを立ち上げるか、以下のようにdocker-compose.ymlを用意してdocker上で立ち上げます。
dockerでの立ち上げはmacの場合Docker.Appが必要です。

Docker.Appは以下でインストールします。

```
$ brew cask install docker
```

docker-compose.ymlの例です。

```yml:docker-compose.yml
version: "3"
services:
    example-mysql:
        container_name: example
        image: "mysql:5.6"
        ports:
            - "4306:3306"
        environment:
          MYSQL_ALLOW_EMPTY_PASSWORD: 1
          MYSQL_DATABASE: "example"
```

上記docker-composeで立ち上げれば「example」という名前のdatabaseが作成されています。

作成されていない場合は、mysqlにログインして以下で作成です。

```
create database example;
```

# テーブルの作成
例としてmemberを管理するためのテーブルを作成します。

こちらのスクリプトを作ります。

```sql:init.sql
DROP TABLE IF EXISTS member;

CREATE TABLE member
(
  id BIGINT NOT NULL AUTO_INCREMENT,
  name VARCHAR(256) NOT NULL,
  description VARCHAR(1000),
  created_at DATETIME NOT NULL,
  updated_at DATETIME NOT NULL,
  PRIMARY KEY (id)
) ENGINE = 'InnoDB', DEFAULT CHARSET=utf8mb4, ROW_FORMAT=DYNAMIC;

insert into member values (1, 'Alice', '', current_timestamp, current_timestamp);
insert into member values (2, 'Bob',  '', current_timestamp, current_timestamp);
```

memberテーブルを作成します。

```
$ mysql -h 127.0.0.1 -uroot -P 4306 example < init.sql
```

# build.sbtの設定

build.sbtへ以下を追加。

- scalikejdbc … 本体
- scalikejdbc-test … コードの自動生成でテストも生成するため
- scalikejdbc-config … application.confにscalikejdbc設定を書くため
- logback-classic … あまり詳しく調べてないのですが、ログ出力に使用
- mysql-connector-java … mysqlとの連携に必要
- scalatest … テストコードの自動生成で使用

```scala:build.sbt
libraryDependencies ++= Seq(
  "org.scalikejdbc" %% "scalikejdbc" % "3.3.+",
  "org.scalikejdbc" %% "scalikejdbc-test"   % "3.3.+"   % "test",
  "org.scalikejdbc" %% "scalikejdbc-config" % "3.3.+",
  "ch.qos.logback" % "logback-classic" % "1.2.3",
  "mysql" % "mysql-connector-java" % "5.1.29",
  "org.scalatest" %% "scalatest" % "3.0.5" % "test"
)
```

## コードの自動生成
コード自動生成機能を使うため、以下をbuild.sbtへ追加。

```
enablePlugins(ScalikejdbcPlugin)
```


自動生成には、以下２つのファイルをprojectディレクトリ直下に入れる必要があります。

```scala:scalikejdbc-gen.sbt
libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.29"

addSbtPlugin("org.scalikejdbc" %% "scalikejdbc-mapper-generator" % "3.3.+")
```

```scala:scalikejdbc.properties
# ---
# jdbc settings
jdbc.driver="com.mysql.jdbc.Driver"
jdbc.url="jdbc:mysql://127.0.0.1:4306/example"
jdbc.username=root
jdbc.password=
jdbc.schema=

# ---
# source code generator settings

generator.packageName=models
# generator.lineBreak: LF/CRLF
generator.lineBreak=LF
# generator.template: interpolation/queryDsl
generator.template=queryDsl
# generator.testTemplate: specs2unit/specs2acceptance/ScalaTestFlatSpec
generator.testTemplate=ScalaTestFlatSpec
generator.encoding=UTF-8
# When you're using Scala 2.11 or higher, you can use case classes for 22+ columns tables
generator.caseClassOnly=true
# Set AutoSession for implicit DBSession parameter's default value
generator.defaultAutoSession=true
# Use autoConstruct macro (default: false)
generator.autoConstruct=false
# joda-time (org.joda.time.DateTime) or JSR-310 (java.time.ZonedDateTime java.time.OffsetDateTime)
generator.dateTimeClass=java.time.ZonedDateTime
```
上記ファイルはjdbcのアクセス先URL等を記述します。デフォルトでのアクセス先をexampleとしています。

## 自動生成実行

それでは自動生成します。

```
$ sbt "scalikejdbcGenAll"
```

コマンド実行後、エラーが無ければ以下2ファイルが生成されています。

- src/main/scala/models/Member.scala
- src/test/scala/models/MemberSpec.scala

## jdbc設定

アプリケーションコードでscalikejdbcを使用するため、application.confに以下設定を書きます。


```scala:src/main/resources/application.conf
# JDBC settings
db.default.driver="com.mysql.jdbc.Driver"
db.default.url="jdbc:mysql://127.0.0.1:4306/example"
db.default.user="root"
db.default.password=""

# Connection Pool settings
db.default.poolInitialSize=10
db.default.poolMaxSize=20
db.default.connectionTimeoutMillis=1000

# Connection Pool settings
db.default.poolInitialSize=5
db.default.poolMaxSize=7
db.default.poolConnectionTimeoutMillis=1000
db.default.poolValidationQuery="select 1 as one"
db.default.poolFactoryName="commons-dbcp2"
```

試しにMemberのデータにアクセスしてみます。

```scala:MysqlAccess.scala
package scalike

import models.Member
import scalikejdbc.config._


object MysqlAccess extends App {
  DBs.setupAll()

  val maybeFound = Member.find(1L)
  val name = maybeFound.get.name
  println(name) // => Alice

}

```

実行すると、実際のDBへのクエリもログに流れるのでデバッグしやすいですね。

```

  [SQL Execution]
   select m.id as i_on_m, m.name as n_on_m, m.description as d_on_m, m.created_at as ca_on_m, m.updated_at as ua_on_m from member m where m.id = 1; (8 ms)

  [Stack Trace]
    ...
    models.Member$.find(Member.scala:42)
    scalike.mysqlAccess$.delayedEndpoint$scalike$mysqlAccess$1(MysqlAccess.scala:10)
    scalike.mysqlAccess$delayedInit$body.apply(MysqlAccess.scala:7)
    scala.Function0.apply$mcV$sp(Function0.scala:39)
    scala.Function0.apply$mcV$sp$(Function0.scala:39)
    scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
    scala.App.$anonfun$main$1$adapted(App.scala:80)
    scala.collection.immutable.List.foreach(List.scala:392)
    scala.App.main(App.scala:80)
    scala.App.main$(App.scala:78)
    scalike.mysqlAccess$.main(MysqlAccess.scala:7)
    scalike.mysqlAccess.main(MysqlAccess.scala)
    ...
```

今回生成したテストはそのままでは通らないので、テストを通すよう書き換えます。（[次回へ](https://qiita.com/umeneri/items/2dbe257bfd72ed03b077)）