我如何 运行 在同一事务中使用 slick 进行非数据库操作的相互依赖查询
How can I run inter-dependent queries alongside a non-DB operation in the same transaction using slick
给定数据模型(<- 表示外键依赖)
TableA <- TableB <- TableC
^ v
-----------------
我需要执行一个 api DELETE 操作来软删除 TableC 中的一行。如果在 TableB 中没有更多未删除的 TableC 条目引用该行的父项,则此删除还必须触发对另一个服务的调用(需要来自 TableA 和 TableB 的值)。如果外部调用失败,我想回滚软删除。我想以惯用的方式完成所有这些(我实际上是 scala/slick 的新手),并使用事务进行回滚
根据我读过的内容,我需要使用 for comprehension to assemble 查询,但我在让数据库操作与外部服务调用很好地融合方面遇到了问题。我的原计划是:
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 方法。然后我尝试将其分成两部分 - 首先是 SELECT,然后是删除,如下所示:
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
查询 returns 缺少 map
函数的 Rep[Int]。此外,我的每个 table(A|B|C)Row 映射都会引发错误,因为他们期望 slick.lifted.Query[Nothing,Nothing,Seq]
而他们得到 slick.lifted.Query[Nothing,T,Seq]
。最后,db.run
语句不想使用 headOption
,显然 returns Any
不支持 map
哈尔普
通过最终理解 slick 如何以 for-comprehension 将事物放在一起解决了这个问题。我不得不将计数从查询中提取出来,并放入后续的 groupby->map 函数中,该函数累积了我想要计数的事物列表,然后对它们进行计数,而不是将其作为查询的一部分进行计数。这也解决了所有其他问题,因为计数查询抛出了预期的 return 类型的所有其他问题。
基本上,解决方案看起来像(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)
})
给定数据模型(<- 表示外键依赖)
TableA <- TableB <- TableC
^ v
-----------------
我需要执行一个 api DELETE 操作来软删除 TableC 中的一行。如果在 TableB 中没有更多未删除的 TableC 条目引用该行的父项,则此删除还必须触发对另一个服务的调用(需要来自 TableA 和 TableB 的值)。如果外部调用失败,我想回滚软删除。我想以惯用的方式完成所有这些(我实际上是 scala/slick 的新手),并使用事务进行回滚
根据我读过的内容,我需要使用 for comprehension to assemble 查询,但我在让数据库操作与外部服务调用很好地融合方面遇到了问题。我的原计划是:
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 方法。然后我尝试将其分成两部分 - 首先是 SELECT,然后是删除,如下所示:
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
查询 returns 缺少 map
函数的 Rep[Int]。此外,我的每个 table(A|B|C)Row 映射都会引发错误,因为他们期望 slick.lifted.Query[Nothing,Nothing,Seq]
而他们得到 slick.lifted.Query[Nothing,T,Seq]
。最后,db.run
语句不想使用 headOption
,显然 returns Any
不支持 map
哈尔普
通过最终理解 slick 如何以 for-comprehension 将事物放在一起解决了这个问题。我不得不将计数从查询中提取出来,并放入后续的 groupby->map 函数中,该函数累积了我想要计数的事物列表,然后对它们进行计数,而不是将其作为查询的一部分进行计数。这也解决了所有其他问题,因为计数查询抛出了预期的 return 类型的所有其他问题。
基本上,解决方案看起来像(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)
})