【问题标题】:Slick 2 - Update columns in a table and return whole table objectSlick 2 - 更新表中的列并返回整个表对象
【发布时间】:2014-05-09 10:10:59
【问题描述】:

在使用 slick 时,如何在返回整个更新后的表的同时更新表中的几列?

假设SomeTables 是某个TableQuery,例如,如果您想向表中添加一个项目(并返回新添加的项目),您通常会编写这样的查询

val returnedItem = SomeTables returning SomeTables += someTable

如果你想更新一个项目并将整个项目全部退回,你会如何做同样的事情,我怀疑你会做这样的事情

val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables
val returnedItem = q.update((3,"test"))

但以下代码不起作用,我看不到任何有关如何执行此操作的文档

请注意,我知道您可以事先查询该项目,更新它,然后在原始对象上使用副本,但这需要大量样板文件(以及 DB 行程)

【问题讨论】:

  • 我不明白您是要更新一个对象并返回它,还是要更新一个对象,然后将 TableQuery 与您刚刚所做的更新一起使用,如果第二个可能只有您必须将.run 附加到您正在执行的查询中。
  • 假设我有一个从case class 到表列的完整投影,我想更新这些列的子集(即.map(x => (x.someColumn,x.anotherColumn)) 并返回整个更新的对象(返回SomeTables)。所以在上面的例子中,SomeTables 可能有一个默认的 * 投影,它有 5 个列,我正在更新其中的两个列(someColumnanotherColumn),但是我想返回整个table(或对象)将包含 5 列
  • 从我的初学者经验来看,除了更新,我没有其他出路,然后选择行,您正在调用的 update 方法返回 Int(即更新是否成功与否)。抱歉帮不上忙,希望有经验的大侠解答一下。
  • 您看到的错误是什么?编译时还是运行时?并非所有 DMBS 都支持您的想法并且会抛出异常。试试returning SomeTables.map(_.yetAnotherColumn)returning SomeTables.map(x=>x)

标签: sql scala slick


【解决方案1】:

Slick(v2 或 v3-M1)不支持此功能;虽然我没有看到任何具体原因禁止它的实现,UPDATE ... RETURNING 不是标准的 SQL 功能(例如,H2 不支持它:http://www.h2database.com/html/grammar.html#update)。我将留给读者作为练习,探索如何安全有效地模拟缺少 UDPATE ... RETURNING 的 RDBMS 的功能。

当您在 scala.slick.lifted.Query 上调用“返回”时,它会给您一个 JdbcInsertInvokerComponent$ReturningInsertInvokerDef。你会发现没有update 方法,尽管有insertOrUpdate 方法;但是,insertOrUpdate 只在插入发生时返回 returning 表达式结果,None 被返回用于更新,所以这里没有帮助。

由此我们可以得出结论,如果您想使用UPDATE ... RETURNING SQL 功能,您要么需要使用StaticQuery,要么将您自己的补丁发布到 Slick。您可以手动编写查询(并将表投影重新实现为 GetResult / SetParameter 序列化程序),或者您可以尝试以下代码:

package com.spingo.slick

import scala.slick.driver.JdbcDriver.simple.{queryToUpdateInvoker, Query}
import scala.slick.driver.JdbcDriver.{updateCompiler, queryCompiler, quoteIdentifier}
import scala.slick.jdbc.{ResultConverter, CompiledMapping, JdbcBackend, JdbcResultConverterDomain, GetResult, SetParameter, StaticQuery => Q}
import scala.slick.util.SQLBuilder
import slick.ast._

object UpdateReturning {
  implicit class UpdateReturningInvoker[E, U, C[_]](updateQuery: Query[E, U, C]) {
    def updateReturning[A, F](returningQuery: Query[A, F, C], v: U)(implicit session: JdbcBackend#Session): List[F] = {
      val ResultSetMapping(_,
        CompiledStatement(_, sres: SQLBuilder.Result, _),
        CompiledMapping(_updateConverter, _)) = updateCompiler.run(updateQuery.toNode).tree

      val returningNode = returningQuery.toNode
      val fieldNames = returningNode match {
        case Bind(_, _, Pure(Select(_, col), _)) =>
          List(col.name)
        case Bind(_, _, Pure(ProductNode(children), _)) =>
          children map { case Select(_, col) => col.name } toList
        case Bind(_, TableExpansion(_, _, TypeMapping(ProductNode(children), _, _)), Pure(Ref(_), _)) =>
          children map { case Select(_, col) => col.name } toList
      }

      implicit val pconv: SetParameter[U] = {
        val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = updateCompiler.run(updateQuery.toNode).tree
        val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, U]]
        SetParameter[U] { (value, params) =>
          converter.set(value, params.ps)
        }
      }

      implicit val rconv: GetResult[F] = {
        val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = queryCompiler.run(returningNode).tree
        val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, F]]
        GetResult[F] { p => converter.read(p.rs) }
      }

      val fieldsExp = fieldNames map (quoteIdentifier) mkString ", "
      val sql = sres.sql + s" RETURNING ${fieldsExp}"
      val unboundQuery = Q.query[U, F](sql)
      unboundQuery(v).list
    }
  }
}

我确信以上可以改进;我是根据我对 Slick 内部的有限理解编写的,它对我有用,并且可以利用您已经定义的投影/类型映射。

用法:

import com.spingo.slick.UpdateReturning._
val tq = TableQuery[MyTable]
val st = tq filter(_.id === 1048003) map { e => (e.id, e.costDescription) }
st.updateReturning(tq map (identity), (1048003, Some("such cost")))

【讨论】:

  • 谢谢!如果有 Slick 3.0 的更新版本会很棒:-)
  • 在 Slick 3 中这有什么不同吗?
  • 我们这里还没有更新到 Slick 3;我确信通用方法会起作用,但 API 和数据结构可能会有所变化。
  • 我在stackoverflow.com/questions/30212785/… 中更新了@TimHarper 为Slick 3.0 编写的代码。非常感谢,蒂姆!
猜你喜欢
  • 2015-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-09
  • 2015-09-28
  • 1970-01-01
相关资源
最近更新 更多