postgresql - 如何使用 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
停止
解决方案
通过最终了解 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)
})
推荐阅读
- python - 模块“thinc”没有属性“层”
- python - 在python中着色时间序列图
- diff - 如何在执行差异操作时忽略文件的时间戳
- javascript - 这是关于 javascript 动态创建的 div,它不在脚本标签中工作,而是在 HMTL 的正文标签中工作
- google-cloud-platform - 导入工作流 Google Cloud Build
- angular - 按钮单击后的角度单元测试 ng-template 触发器不起作用
- javascript - React Hook useEffect 缺少依赖项:'location.state'。要么包含它,要么移除依赖数组 react-hooks/exhaustive-deps
- javascript - 查找对象数组中的第 n 个项目
- angular - 如何在另一个组件中重置表单构建器?
- javascript - 等待调试器断开连接... VSCode 中的 Node.js