Doobie book说,从存储库层返回ConnectionIO是一个好习惯。它提供了将呼叫链接并在一个事务中执行的功能。
很好,很清楚。
现在,让我们想象一下我们正在研究REST API服务,我们的场景是:
在数据库中查找对象
使用此对象执行一些异步操作(使用cats.effect.IO或monix.eval.Task)。
将对象存储在数据库中。
我们希望在1个事务中执行所有这些步骤。问题在于,如果没有transactor.trans()
给我们的自然变换,我们将在2个monad中工作-Task
和ConnectionIO
。那不可能
问题是-如何将doobie ConnectionIO
与任何效果monad混合在1个组成中,例如我们正在处理1个事务,并且能够在世界末尾提交/回滚所有DB突变?
谢谢!
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
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)
最佳答案
doobie has a ConnectionIO
instance中的cats.effect.Async
,除其他外,它还允许您通过cats.effect.IO
方法将任何ConnectionIO
转换为liftIO
:
import doobie.free.connection._
import cats.effect.{IO, Async}
val catsIO: IO[String] = ???
val cio: ConnectionIO[String] = Async[ConnectionIO].liftIO(catsIO)
对于
monix.eval.Task
,最好的选择是使用Task#toIO
并执行相同的技巧,但是您需要在范围内使用monix Scheduler
。关于scala - 1次交易中的Doobie和DB访问组合,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50472904/