首页 > 解决方案 > 如何使用 slick 在同一事务中与非数据库操作一起运行相互依赖的查询

问题描述

给定数据模型(<- 表示外键依赖)

TableA <- TableB <- TableC
   ^                 v
    -----------------

我需要执行一个 api DELETE 操作来软删除 TableC 中的一行。如果没有更多未删除的 TableC 条目引用 TableB 中该行的父项,则此删除还必须触发对另一个服务的调用(需要来自 TableA 和 TableB 的值)。如果外部调用失败,我想回滚软删除。我想以惯用的方式完成所有这些工作(我实际上是 scala/slick 的新手),并使用事务进行回滚

根据我所阅读的内容,我需要使用理解来组装查询,但是我在让数据库操作与外部服务调用很好地融合时遇到了问题。我原来的计划是:

val select = for {
  tableCRow <- tableBDao.TableQueryC.filter(_.id === idParam)
  tableBRow <- tableBDao.TableQueryB if tableCRow.tableBForeignKey === tableBRow.id
  tableARow <- TableADao.TableQueryA if tableCRow.tableAForeignKey === tableARow.id
  count <- tableBDao.TableQueryC.filter(_.tableBForeignKey === tableBRow.id).map(_.id).countDefined
  _ <- tableBDao.softDeleteRow(idParam)
  _ <- if (count > 1) DBIO.successful(httpRequestService.deleteOtherResource(tableARow.someValue, tableBRow.someValue))
} yield ()
db.run(select.result)

但这有问题,因为我无法将 Slick 的 Rep[T] 值传递给我的 httpRequestService 方法。然后我尝试将其分解为两部分 - 先选择,然后删除,如下所示:

val select = for {
  tableCRow <- tableBDao.TableQueryC.filter(_.id === idParam)
  tableBRow <- tableBDao.TableQueryB if tableCRow.tableBForeignKey === tableBRow.id
  tableARow <- TableADao.TableQueryA if tableCRow.tableAForeignKey === tableARow.id
  count <- tableBDao.TableQueryC.filter(_.tableBForeignKey === tableBRow.id).map(_.id).countDefined
} yield (tableBRow.date.formatted("yyyy-MM-DD"), tableARow.externalServiceId, count)
val result: Future[Option[(String, Long, Integer)]] = db.run(select.result.headOption)
result.map {
  case None => throw new IllegalArgumentException("exception message")
  case Some(data) =>
    val delete = for {
      _ <- tableBDao.softDeleteRow(idParam)
      _ <- if (data._3 > 1) DBIO.successful(httpRequestService.cancelSchedulerJob(data._2, data._1))
    } yield numRows
    db.run(delete.transactionally)
}

但是,尽管这实际上通过了 IntelliJ IDEA 检查,但它不会编译,因为我的count查询返回一个缺少map函数的 Rep[Int]。此外,我的每个 table(A|B|C)Row 映射都会引发错误,因为它们正在期待slick.lifted.Query[Nothing,Nothing,Seq]并且正在获取slick.lifted.Query[Nothing,T,Seq]. 最后,该db.run语句不想使用headOption,并且显然返回Any了不支持的map

停止

标签: postgresqlscalaslickplayback

解决方案


通过最终了解 slick 如何将事物组合在一起以供理解,从而解决了这个问题。我不得不将计数从查询中拉出并进入后续 groupby->map 函数,该函数累积了我想要计数的事物列表,然后将它们计数,而不是作为查询的一部分计数。这也解决了所有其他问题,因为计数查询抛弃了其他所有东西的预期返回类型。

基本上,解决方案看起来像(thing1 用于连接):

val select = (for {
  thing1 <- query1
  thing2 <- query2
  thing3 <- query3
  listToCount <- query4 selecting everything I wanted to count
} yield (thing2, thing3, listToCount))
  .groupBy({
    case (thing2, thing3, listToCount) =>
      (thing2, thing3)
  })
  .map({
    case ((thing2, thing3), list) =>
      (thing2.deliveryDate, thing3.schedulerJobId, list.map(_._3).length)
  })

推荐阅读