Scala Slick-确定DBIO.sequence中哪个DBIO失败
我用下面的方式在DB上执行3个不同的事务性操作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
// 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
,那就好了,但根据他们的文档,使用清理
转换可丢弃文件确实是一个预期的用例。所以这里没有虐待;-)