Doobie 和 DB 访问组合在 1 个事务中 [英] Doobie and DB access composition within 1 transaction

查看:46
本文介绍了Doobie 和 DB 访问组合在 1 个事务中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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:

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

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

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 个事务中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆