読者です 読者をやめる 読者になる 読者になる

Slick 3.0を試してみた

Scala

Slick 3.0がリリースされたので、GitBucketをSlick 3.0にアップデートするべくいろいろ試してみました。

Slick 3.0は別名Reactive Slickというだけあり、これまでとはプログラミングモデルがドラスティックに変わっています。具体的に言うとDBIOActionというオブジェクトを介してデータベース操作の定義と実行が明確に分離されています。要するにMonadicなプログラミングスタイルになっているということです。
他にもReactive StreamsとかOptionサポートの強化などSlick 3.0の新機能はいろいろありますが、一番大きいのはこの点だと思います。

また、DBIOActionの実行は全面的にFutureベースになっています。Playなどと組み合わせて使用する場合、Slickを使った処理があってもAsyncResultで返すことができるようになる反面、別にブロックすればいいじゃんという用途においては面倒が増えただけという印象があります。

Slick 3.0のドキュメントにある簡単な例だとこんな感じ。クエリの書き方はジョインのメソッド名が変わっていたりなど、一部違いはあるもののこれまでとほぼ変わりません。

// クエリを生成
val q = for (c <- coffees) yield c.name
// アクションを生成
val a = q.result
// 実行
val f: Future[Seq[String]] = db.run(a)

複数のアクションをまとめて実行したい場合はこんな感じ。トランザクショナルに実行したいのでtransactionallyで「このアクションはトランザクショナルに処理するべし」という印をつけています。

// 合成したアクションを生成
val a = (for {
  ns <- coffees.filter(_.name.startsWith("ESPRESSO")).map(_.name).result
  _ <- DBIO.seq(ns.map(n => coffees.filter(_.name === n).delete): _*)
} yield ()).transactionally

これまでのAPIとの互換APIも残されているので動かすだけであれば簡単なものであればそのままでもコンパイルして動作させることができるのですが、互換APIは3.1で削除されるとのことなので、今後もSlickを使い続けるのであればDBIOActionを使用したFutureベースのモデルへの移行は必須です。

ただ、クエリのAPIには互換性があるとはいえ、実行モデルが根本的に違うのでSlick 2.xから移行するのは結構きつそうな気がします。トランザクションの制御の仕方や、Webフレームワークとの接合点、アプリケーション内でのインターフェースの切り方などをこれまでとは大きく変える必要がありそうです。