doobie
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
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
pgEnumString("queue_status", QueueStatus.valueOf, _.toString)
配列を扱う
Array、List、Vectorに標準で対応する
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に変換できる。
トランザクション中にIOしたい
WeakAsyncを使う
IOをConnectionIOに持ち上げてくれる
code:scala
import doobie.WeakAsync
import doobie.ConnectionIO
import cats.effect.IO
import doobie.implicits.{*, given}
import doobie.syntax.*
WeakAsync
.use { lifter =>
for {
_ <- sql"...".queryString.unique _ <- lifter(IO("関係ないIO"))
result <- sql"...".queryInt.unique } yield result
}