Ktor + Exposedを使ってlocalhostでMySQLに接続(with MAMP)

#はじめに
こんにちは！tasukuです！
今回はMySQLからデータをSELECTしてこようと思います！
(Insertやら他のはExposedについての記事をまた別の機会に書こうと思っています)

・どうやらKtorでDBと接続するときはExposedというライブラリが一般的らしいので今回はそれを使います
・MAMPを使うとphpmyadmin使えるしDBの設定とかも楽なのでDBサーバーにはMAMPを使います

Exposedのgithubはこちらになります。
<a href="https://github.com/JetBrains/Exposed/wiki/Getting-Started">https://github.com/JetBrains/Exposed/wiki/Getting-Started</a>

#環境
・macOS Mojave (10.14.5)
・IntelliJ IDEA (2019.2.2)
・Exoposed (0.16.1)
・MAMP (5.5)
・MAMPの中のMySQL(5.7.26)

#実装の前に
まずは実装の前にMAMPのMySQLにデータを準備しましょう(mampやphpmyadminの説明になってしまうので軽めにいきます)
必要のない方はここは飛ばしちゃってください

###DB作成します
MAMPを起動してphpMyAdminを開きます。そうするとこんな画面が開きます。
(すでに自分で作ったDBが何個かありますが気にしないでください。。)
<img width="1440" alt="スクリーンショット 2019-09-10 13.23.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456836/8c8cbfaa-4951-9af9-1467-32731d167079.png">

画面左にNewってのがあるのでそこを押すとDB作成画面へ移ります。今回はExposedSampleというDB名にすることにします。名前を入力したらあとはいじらずに```create```を押します。
<img width="1066" alt="スクリーンショット 2019-09-10 13.25.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456836/c6ff8ba6-bcc7-b908-fd3f-4eafb6fa4d30.png">

そうするとテーブル名の入力を求められるので今回は```Table1```にしておきます。そして次に進みます(画像は割愛)

そうすると次に各カラムの名前や型などを決める画面に移ります。
わかる方は適当に作っちゃってください
DB全くわからないよーという方は今回は以下の画像のように設定しておけば大丈夫です。
(画面から見切れているcommentsやVirtualityなどは何も書かなくて大丈夫です)
<img width="1433" alt="スクリーンショット 2019-09-10 13.37.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456836/6b09eccf-5ba8-b52e-a13c-8d349581ee1d.png">

最後に右下のsaveを押せばテーブルが完成します:clap:

###データを入れます
今回はDBからデータを取ってこれたーってとこまでなのでとりあえず1レコードだけデータを入れておきましょう。
SQLタブを押して以下のSQLを入力して右下の```Go```を押すとレコードが入ります！
入ってるかどうかはBrowseタブを押せばわかります。
<img width="1229" alt="スクリーンショット 2019-09-10 13.40.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456836/154c6da1-3532-84b6-bf73-4ba9e4b3ff2a.png">

#実装
やっとDBにデータを入れて準備ができました。お疲れ様です。
それではKtorのプロジェクトにExposedを入れていきましょう

###まずはbuild.gradle(追加箇所だけ抜粋)
build.gradleにExposedを追加します。下のやつはmysqlを使うときに必要なやーつです。

```gradle:build.gradle
dependencies {
    compile "org.jetbrains.exposed:exposed:0.16.1"
    compile "mysql:mysql-connector-java:5.1.46"
}

```

###DBに接続する
Application.ktに以下を追加します

```kotlin:Application.kt
fun initDB() {
    val user = "root"
    val pass = "root"
    val url = "jdbc:mysql://localhost:8889/ExposedSample?useSSL=false"
    val driver = "com.mysql.jdbc.Driver"
    Database.connect(url = url, driver = driver, user = user, password = pass)
}
```
これを呼ぶだけでDBに接続できます:clap:
MAMPのphpmyadminではユーザーとパスワードはroot(設定とか変えてなければ)なはずなので```root```に。
urlは```jdbc:mysql://DBサーバーのipアドレス:ポート/DB名?オプション```となっています。
MAMPはデフォルトではMySQLのポート番号が8889になっています。MAMPのpreferenceから自分で変更している場合はこちらもそれに合わせてください。
useSSL=falseはつけないとWARNの警告が出るのでとりあえずつけました。

今回はこのinitDB()。こんな感じでmain関数の中で呼んでしまいましょう。
こうするとIntelliJでRunしたタイミングでDBに接続されます。

```kotlin:Applicatoin.kt
fun main(args: Array<String>): Unit = run {
    initDB()
    io.ktor.server.netty.EngineMain.main(args)
}
```

###DBからデータを取ってくる
さあここまででDBへ接続することができました。
ここからDBからデータをselectしてきてみたいと思います。

Exposedを使ってDBからデータを取ってくる方法は
・DSL
・DAO
の2種類あるようです。
SQLに近い方が個人的には扱いやすいので今回はDSLを使おうと思います。

まず以下のようなobjectを追加します。

```kotlin:Application.kt
    object Table1 : Table() {
        val name = varchar("name", length = 255)
    }
```
このobjectの名前はデータを取ってくるテーブルの名前と一致させるようにしてください。
また```varchar```の引数の```name```もTable1で設定したカラム名と一致させてください。
いずれも一致していないと例外が発生してしまいます。
(lengthは50とかにしても普通に動くので一致していなくても平気です。ですがわざわざDBで行なった設定と違う値を設定する必要もないかと思います。)
もしname以外にも取ってきたかったらここに追加すれば取ってくることができます。

それでは取ってくるところの実装です。
これを書いて<a href="http://localhost:8080">localhost:8080</a>にアクセスしてtestNameと表示されたら成功です。

```kotlin:Application.kt
@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
    routing {
        get("/") {
            var name = ""
            transaction {
                val result = Table1.selectAll()
                result.forEach { index, resultRow ->
                    name += resultRow[Table1.name]
                    //↓複数ある時にカンマ区切りで表示するため
                    if (result.count() - 1 != index) name += ","
                }
            }
            call.respondText { "$name" }
        }
    }
}
```
まず```transaction{}```ですが、これは公式に**all SQL statements should be placed inside a transaction**と書かれているのでSELECTなりSQLを発行するときは必ずこれで囲う必要があります。
次に```Table1.selectAll()```でTable1から全レコードを取ってきます。今回は1レコードしかデータを準備してないので、Allといっても１つしか取れません。
selectAllをするとQueryという型の変数が戻ってきます。このQueryはIterableを継承しているのでforEachで回すことができ、回すとResultRow型の変数が取れます
ResultRowから欲しいカラムの値を取得するには上記のように```it[Table1.name]```といった感じで取れます。it["name"]ではないので気をつけてください。

最後にいつものcall.respondText{}で出力しています。

バラバラにApplication.ktを書いていったので分かりづらいと思いますので、最後にApplication.ktの全体を載せておきます。

```kotlin:Application.kt
package com.example

import io.ktor.application.*
import io.ktor.response.*
import io.ktor.request.*
import io.ktor.routing.get
import io.ktor.routing.routing
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction

fun main(args: Array<String>): Unit = run {
    initDB()
    io.ktor.server.netty.EngineMain.main(args)
}

@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
    routing {
        get("/") {
            var name = ""
            transaction {
                val result = Table1.selectAll()
                result.forEachIndexed { index, resultRow ->
                    name += resultRow[Table1.name]
                    if (result.count() - 1 != index) name += ","
                }
            }
            call.respondText { "$name" }
        }
    }
}

fun initDB() {
    val user = "root"
    val pass = "root"
    val url = "jdbc:mysql://localhost:8889/ExposedSample?useSSL=false"
    val driver = "com.mysql.jdbc.Driver"
    Database.connect(url = url, driver = driver, user = user, password = pass)
}

object Table1 : Table() {
    val name = varchar("name", length = 50)
}
```

#終わりに
今回はKtor + Exposedを使ってDB接続をしてSELECTでデータを取ってきてみました。
KtorやExposedはまだまだSpringとかに比べたら情報が少ないと思うので、少しでもこの記事がお役に立てたら嬉しいです。
もし間違いなどがありましたらコメントや編集リクエストしていただけたらと思います。

最後までお読みいただきありがとうございました。