doobie
#scala
ScalaでDBを扱うためのライブラリ。Typelevelが開発している。Cats Effectとの相性が良い。
SELECT INするには
doobieでは空にならないことが保障されているNonEmptyListを利用してIN句を表現する。
JDBCの都合でIN句の挿入にはやや手間が必要だ。
sql文字列補間子の代わりにいったんfr文字列補間子を利用する。
あとはいつも通り.queryすればよい
code:in.scala
import doobie.*
import doobie.implicits.{*, given}
import doobie.postgres.implicits.*
import cats.data.NonEmptyList
val idNel = NonEmptyList(...)
val q =
fr"SELECT status FROM foobar WHERE " ++ Fragments
.in(
fr"id",
idNel,
)
val statuses = q
.queryQueueStatus
Opaque Typeを扱う
Opaque Type opaque type A = X をdoobieで扱うには、Meta[A]を用意すればよい。
たいていの場合Meta[X]が既にある
e.g. opaque type NodeId = Long
companion objectの中でgivenしてやればよい
code:opaque.scala
opaque type NodeId = Long
object NodeId {
def apply(id: Long): NodeId = id
given MetaNodeId = Meta.LongMeta
}
Enumを扱う
Enum A をdoobieで扱うには、Meta[A]を用意すればよい。
postgresの場合、import doobie.postgres.implicits.*してから以下のように書けばよい:
code:MetaFromEnum.scala
enum QueueStatus:
case waiting, claimed, grabbed, finished
given MetaQueueStatus =
pgEnumString("queue_status", QueueStatus.valueOf, _.toString)
配列を扱う
Array、List、Vectorに標準で対応する
https://tpolecat.github.io/doobie/docs/11-Arrays.html
Opaque TypeのArrayに対応するには、given (using foo: Meta[Array[元々の型]]): Meta[Array[Opaque Typeの型]]をcompanion objectに埋めるとよい
code:EnumArray.scala
opaque type NodeId = Long
object NodeId {
def apply(id: Long): NodeId = id
given MetaNodeId = Meta.LongMeta
given (using mai: Meta[ArrayInt]): Meta[ArrayNodeId] = mai
}
時刻を扱う
Postgresの話。
カラムにはTIMESTAMP WITH TIME ZONEといったタイムゾーンつきの型を使うことをおすすめする。
このような型は直接java.time.OffsetDateTimeやjava.time.ZonedDateTimeに変換できる。
https://tpolecat.github.io/doobie/docs/15-Extensions-PostgreSQL.html
トランザクション中にIOしたい
WeakAsyncを使う
IOをConnectionIOに持ち上げてくれる
code:scala
import doobie.WeakAsync
import doobie.ConnectionIO
import cats.effect.IO
import doobie.implicits.{*, given}
import doobie.syntax.*
WeakAsync
.liftKIO, ConnectionIO
.use { lifter =>
for {
_ <- sql"...".queryString.unique
_ <- lifter(IO("関係ないIO"))
result <- sql"...".queryInt.unique
} yield result
}