在 Slick 3 的事务中执行非数据库操作

Posted

技术标签:

【中文标题】在 Slick 3 的事务中执行非数据库操作【英文标题】:Executing non-database actions in a transaction in Slick 3 【发布时间】:2015-09-15 05:10:54 【问题描述】:

我无法理解新的 Slick DBIOAction API,文档中似乎没有很多示例。我使用的是 Slick 3.0.0,我需要执行一些数据库操作以及一些从数据库接收到的数据的计算,但是所有这些操作都必须在单个事务中完成。我正在尝试执行以下操作:

    对数据库(types 表)执行查询。 对查询结果进行一些聚合和过滤(无法在数据库上进行此计算)。 根据第 2 步的计算结果执行另一个查询(messages 表 - 由于某些限制,此查询必须使用原始 SQL)。 将第 2 步和第 3 步中的数据连接到内存中。

我希望在事务中执行第 1 步和第 3 步中的查询,因为它们的结果集中的数据必须是一致的。

我尝试过以单子连接方式进行此操作。这是我的代码的过度简化版本,但我什至无法编译:

  val compositeAction = (for 
    rawTypes <- TableQuery[DBType].result
    (projectId, types) <- rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10)))
    counts <- DBIO.sequence(types.map(aType => sql"""select count(*) from messages where type_id = $aType.id""".as[Int]))
   yield (projectId, types.zip(counts))).transactionally
    for 理解的第一行从types 表中选择数据。 for 理解的第二行应该对结果进行一些分组和切片,导致Seq[(Option[String], Seq[String])] for 理解的第三行必须对上一步中的每个元素执行一组查询,特别是,它必须对Seq[String] 中的每个值执行单个 SQL 查询。所以在第三行我构建了一个DBIOActions 的序列。 第二步中的yield 子句zips types 和第三步中的counts

但是,这种构造不起作用,并给出了两个编译时错误:

Error:(129, 16) type mismatch;
 found   : slick.dbio.DBIOAction[(Option[String], Seq[(com.centreit.proto.repiso.storage.db.models.DBType#TableElementType, Vector[Int])]),slick.dbio.NoStream,slick.dbio.Effect]
    (which expands to)  slick.dbio.DBIOAction[(Option[String], Seq[(com.centreit.proto.repiso.storage.db.models.TypeModel, Vector[Int])]),slick.dbio.NoStream,slick.dbio.Effect]
 required: scala.collection.GenTraversableOnce[?]
        counts <- DBIO.sequence(types.map(aType => sql"""select count(*) from messages where type_id = $aType.id""".as[Int]))
               ^
Error:(128, 28) type mismatch;
 found   : Seq[Nothing]
 required: slick.dbio.DBIOAction[?,?,?]
        (projectId, types) <- rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10)))
                           ^

我尝试使用DBIO.successful 将第二行包装在DBIOAction 中,这应该将一个常量值提升到DBIOAction monad:

(projectId, types) <- DBIO.successful(rawTypes.groupBy(_.projectId).toSeq.map(group => (group._1, group._2.slice(0, 10))))

但在这段代码中,types 变量被推断为Any,因此代码无法编译。

【问题讨论】:

看看this example,看看对你有没有帮助 @FelipeAlmeida 谢谢,但这不是我所需要的。在这个例子中,所有的推导都是更新/删除/插入,它们的结果没有相互关联,也没有任何结果(所有左边的部分都是_)。就我而言,我的一项操作与数据库完全无关,所有操作都返回用于其他理解或yield 的结果。 【参考方案1】:

试试这个方法:

val compositeAction = (for 
  rawTypes <- TableQuery[DBType].result
  pair <- DBIO.sequence(rawTypes.groupBy(_.projectId).toSeq.map(group => DBIO.successful(group)))
  counts <- DBIO.sequence(pair.head._2.map(aType => sql"""select count(*) from messages where type_id = $aType.id""".as[Int]))
 yield (pair.head._1, pair.head._2.zip(counts))).transactionally

【讨论】:

谢谢!这似乎就是我想要的。

以上是关于在 Slick 3 的事务中执行非数据库操作的主要内容,如果未能解决你的问题,请参考以下文章

在 Slick 3 中以事务方式使用

使用 Slick 3.0 在同一事务中进行多次插入

在 Slick 中等待语句执行完成

在 Play framework 2.4 中使用 Slick 3 在哪里初始化数据库?

jdbc笔记3

Slick教程