Doobie和DB访问组合在1个事务中

Eug*_*kov 10 scala scala-cats doobie

Doobie的书说,从存储库层返回ConnectionIO是一个很好的做法.它提供了链接调用并在一个事务中执行它们的能力.很好,很清楚.

现在让我们假设我们正在开发REST API服务,我们的场景是:

  1. 在数据库中查找对象
  2. 使用此对象执行一些异步操作(使用cats.effect.IO或monix.eval.Task).
  3. 将对象存储在数据库中.

我们希望在1个事务中执行所有这些步骤.问题是没有自然变换,我们transactor.trans()在2 monad工作 - TaskConnectionIO.那是不可能的.

问题是 - 如何将doobie ConnectionIO与1个组合中的任何效果monad 混合,例如我们在1个事务中工作并且能够在世界末尾提交/回滚所有数据库突变?

谢谢!

UPD:小例子

def getObject: ConnectionIO[Request]                      = ???
def saveObject(obj: Request): ConnectionIO[Request]       = ???
def processObject(obj: Request): monix.eval.Task[Request] = ???

val transaction:??? = for {
    obj       <- getObject             //ConnectionIO[Request]
    processed <- processObject(obj)    //monix.eval.Task[Request]
    updated   <- saveObject(processed) //ConnectionIO[Request]
  } yield updated
Run Code Online (Sandbox Code Playgroud)

UPD2:@ oleg-pyzhcov提供的正确答案是将效果数据类型提升到ConnectionIO这样:

def getObject: ConnectionIO[Request]                      = ???
def saveObject(obj: Request): ConnectionIO[Request]       = ???
def processObject(obj: Request): monix.eval.Task[Request] = ???

val transaction: ConnectionIO[Request] = for {
    obj       <- getObject                                           //ConnectionIO[Request]
    processed <- Async[ConnectionIO].liftIO(processObject(obj).toIO) //ConnectionIO[Request]
    updated   <- saveObject(processed)                               //ConnectionIO[Request]
} yield updated
val result: Task[Request] = transaction.transact(xa)
Run Code Online (Sandbox Code Playgroud)

Ole*_*cov 11

ConnectionIO在doobie 有一个cats.effect.Async实例,其中,除其他事项外,可以让你把任何cats.effect.IOConnectionIO通过以下方式liftIO方法:

import doobie.free.connection._
import cats.effect.{IO, Async}
val catsIO: IO[String] = ???
val cio: ConnectionIO[String] = Async[ConnectionIO].liftIO(catsIO)
Run Code Online (Sandbox Code Playgroud)

因为monix.eval.Task,你最好的选择是使用Task#toIO和执行相同的技巧,但你需要一个Scheduler范围的monix .

  • `.to[ConnectionIO]` 现在似乎适用于 `cats.effect.IO` (3认同)