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

Scala Slick - Identify which DBIO is failing in DBIO.sequence

我按以下方式在数据库上执行 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 => ...
  ...
}

如果你使用 .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)
}

它保留 success/failure 结果触发原子事务的中止请求。 最后只是把failure中包含的error映射出来而已,确实实现一个函数来做那个还是蛮有用的

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 => ...
  ...
}