Scalaの新しいデータベースアクセスライブラリ「quill」を試してみた

Slickがモナドの世界に旅立ってしまったので、Slick3への移行を進めつつ他のデータベースアクセスライブラリの動向からも目が離せません。

同期的に実行可能なデータベースアクセスライブラリとしてはScalikeJDBCが使いやすそうなのですが、SlickのタイプセーフクエリはScalaの特徴を活かした非常に便利かつ安全なもので、この方向性のものが欲しいなーと思っていたところ、最近活発に開発が行われているのがquillというライブラリです。

github.com

以前から気になっていたのですが、少し時間があったので軽く試してみました。

まずは以下のような感じでdbを準備をしておきます(SnakeCaseH2Dialectは使用する命名規則やデータベースに応じて適切なものに変えるなり自作するなりする感じ)。

import io.getquill._
import io.getquill.naming.SnakeCase
import io.getquill.sources.sql.idiom.H2Dialect

val db = source(new JdbcSourceConfig[H2Dialect, SnakeCase]("db"))

クラスパスルートにはapplication.confを作成しておきます。デフォルトではHikariCPによるコネクションプールが使われるようです。

db.dataSourceClassName="org.h2.jdbcx.JdbcDataSource"
db.dataSource.url="jdbc:h2:~/h2/data"
db.dataSource.user="sa"
db.dataSource.password="sa"

簡単な検索の例です。

case class Account(userName: String, mailAddress: String)

val q = quote { (userName: String) =>
  query[Account].filter(_.userName == userName).map(_.mailAddress)
}

val account: Option[Account] = db.run(q)("takezoe").headOption

クエリはSlickと同じような感覚で記述することができます。db.runのあたりがSlick3っぽくてドキドキしますが、単にクエリを実行しているだけなので安心です。

quillはマクロを使ってコンパイル時にSQLに変換しているようで、クエリで使うパラメータは明示的に渡してあげる必要があります。この点はSlickと比べるとちょっと面倒かもしれません。逆にquillではSlickのようなテーブルとのマッピング定義する必要がなく、ケースクラスを作成するだけで利用できるのは便利です。

更新処理の例を見てみます。

// 登録
val q = quote { query[Account].insert }
db.run(q)(Account("takezoe", "takezoe@gmail.com"))

// 更新
val q = quote { (userName: String, mailAddress) =>
  query[Account].filter(_.userName == userName).update(_.mailAddress -> mailAddress)
}
db.run(q)(("takezoe", "takezoe@gmail.com"))

// 削除
val q = quote { (userName: String) =>
  query[Account].filter(_.userName == userName).delete
}
db.run(q)("takezoe")

Slick2と比べると全体的に冗長な記述が必要になっていますが、安全性という面では同等以上という印象です。マクロを駆使しているので何かトラブルがあった時に手を入れにくいというのは問題かもしれません(でも難易度的にはSlickもなかなかなので大差ないといえばないかも…)。

quillはすでにScalaにおけるデータベースアクセスライブラリの有力な選択肢のひとつであると同時に、冒頭でも触れたように現在も精力的に開発が進められており将来性にも期待できるのではないかと思います。

イシューには以下のような不穏なチケットもあがっているのですが、Slickのように異世界に旅立ってしまうことがないよう祈るばかりです。

github.com