Scala Slick-确定DBIO.sequence中哪个DBIO失败

Scala Slick-确定DBIO.sequence中哪个DBIO失败,scala,slick,scala-cats,Scala,Slick,Scala Cats,我用下面的方式在DB上执行3个不同的事务性操作 // firstDBIO, secondDBIOA, thirdDBIO: DBIOAction[Unit] F.delay { val unitOfWork = DBIO.sequence( List( firstDBIO, secondDBIO, thirdDBIO, ), ) db.run(unitOfWork.transactionally) }.futureLift.voi

我用下面的方式在DB上执行3个不同的事务性操作

// firstDBIO, secondDBIOA, thirdDBIO: DBIOAction[Unit]

F.delay {
  val unitOfWork = DBIO.sequence(
    List(
      firstDBIO,
      secondDBIO,
      thirdDBIO,
    ),
  )
  db.run(unitOfWork.transactionally)
}.futureLift.void.map(_.asRight[ImportError]).recover {
  case ex: SQLException => Left(ImportError.UnexpectedError)
}
这可以正常工作,但当事务失败时,在
recover
中,我无法根据导致错误的
DBIO
中的哪一个进行逻辑分析(我不想依赖
SQLException

我希望能够做一些像

.recover {
  case ex: ImportError.CauseFirst => ...
  case ex: ImportError.CauseSecond => ...
  case ex: ImportError.CauseThird => ...
  ...
}
dbio.cleanUp({
  case Some(error) => DBIO.failed(improveError(error)) // add idx to Exception or sth
  case None => DBIO.successful(())
}, keepFailure = false)

如果您使用
.sequence
,那么您将在第一次失败的将来失败。您有两种选择:

  • 将每个DBIO的错误映射为包含数字-我想您可能会滥用
    .cleanUp
    方法,例如
    dbio.cleanUp({
      case Some(error) => DBIO.failed(improveError(error)) // add idx to Exception or sth
      case None => DBIO.successful(())
    }, keepFailure = false)
    
  • 将单个结果保存为
    Try
    并在事务处理后解决
    dbio.asTry
    // then use db.run(DBIO.sequence(dbios).transactionally)
    // to get Future[List[Try[Int]]]
    

与前者相比,我不确定后者将如何处理事务和回滚,但这两种情况都会让您发现哪个操作失败。

我发现这个解决方案正是我所寻找的

dbio.asTry.flatMap {
  case Success(v) => DBIO.successful(v)
  case Failure(e) => DBIO.failed(ImportError.CauseFirst)
}
它保留触发请求的原子事务中止的成功/失败结果。 最后,它只是映射失败中包含的错误,实际上实现一个函数来实现这一点是非常有用的

implicit class DBIOOps[A](dbio: DBIO[A]) {
  def mapFailure(f: Throwable => E with Throwable) = dbio.asTry.flatMap {
    case Success(v) => DBIO.successful(v)
    case Failure(e) => DBIO.failed(f(e))
  }
}

使用Try的解决方案在第一次失败后将不再中止,这可能是一个问题。如果DBIO中有
折叠
映射错误
recoverWith
,那就好了,但根据他们的文档,使用
清理
转换可丢弃文件确实是一个预期的用例。所以这里没有虐待;-)