微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决Scala Slick - 确定 DBIO.sequence 中哪个 DBIO 失败

我通过以下方式在数据库上事务性地执行 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 中的哪个导致错误来制定逻辑(我不想依赖 { {1}})。

我希望能够做类似的事情

sqlException

解决方法

如果您使用 .sequence,那么您只会在第一个失败的未来失败。您有 2 个选择:

  • 映射每个 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))
  }
}
F.delay {
  val unitOfWork = DBIO.sequence(
    List(
      firstDBIO.mapFailure(_ => ImportError.CauseFirst),secondDBIO.mapFailure(_ => ImportError.CauseSecond),thirdDBIO.mapFailure(_ => ImportError.CauseThird),...
    ),)
  db.run(unitOfWork.transactionally)
}.futureLift.void.map(_.asRight[ImportError]).recover {
  case ex: ImportError.CauseFirst => ...
  case ex: ImportError.CauseSecond => ...
  case ex: ImportError.CauseThird => ...
  ...
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。