问题描述
Doobie book 说从存储库层返回 ConnectionIO 是一个很好的做法.它提供了链接调用并在一个事务中执行它们的能力.好看又清晰.
Doobie book says that it's a good practice to return ConnectionIO from your repository layer. It gives an ability to chain calls and perform them in one transaction.Nice and clear.
现在让我们假设我们正在开发 REST API 服务,我们的场景是:
Now let's imagine we are working on REST API service and our scenario is:
- 在数据库中查找对象
- 使用该对象执行一些异步操作(使用cats.effect.IO 或monix.eval.Task).
- 将对象存储在数据库中.
我们希望在 1 个事务中执行所有这些步骤.问题是,如果没有 transactor.trans()
为我们提供的自然转换,我们将在 2 个 monad 中工作 - Task
和 ConnectionIO
.那是不可能的.
And we want to perform all these steps inside 1 transaction. The problem is that without natural transformation which is given for us by transactor.trans()
we are working inside 2 monads - Task
and ConnectionIO
. That's not possible.
问题是 - 如何将 doobie ConnectionIO
与任何效果 monad 混合在 1 个组合中,例如我们在 1 个事务中工作并且能够在世界末日提交/回滚所有数据库突变?
The question is - how to mix doobie ConnectionIO
with any effect monad in 1 composition such as we are working in 1 transaction and able to commit/rollback all DB mutations at the end of the world?
谢谢!
更新:小例子
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
,如下所示:
UPD2: The correct answer provided by @oleg-pyzhcov is to lift your effect datatypes to ConnectionIO
like this:
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)
推荐答案
ConnectionIO
in doobie 有一个 cats.effect.Async
实例,其中包括东西,允许你通过liftIO
方法将任何cats.effect.IO
转换为ConnectionIO
:
ConnectionIO
in doobie has a cats.effect.Async
instance, which, among other things, allows you do turn any cats.effect.IO
into ConnectionIO
by means of liftIO
method:
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
范围内.
For monix.eval.Task
, your best bet is using Task#toIO
and performing the same trick, but you'd need a monix Scheduler
in scope.
这篇关于Doobie 和 DB 访问组合在 1 个事务中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!