Sql Slick 2-更新表中的列并返回整个表对象

Sql Slick 2-更新表中的列并返回整个表对象,sql,scala,slick,Sql,Scala,Slick,在使用slick时,如何在返回整个更新表的同时更新表中的几列 假设SomeTables是某个TableQuery,如果您想向表中添加一个项(并返回新添加的项),通常会编写这样的查询 如果你想更新一个项目并返回整个项目,你会怎么做?我想你会这样做 val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables val returnedItem = q.up

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

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

如果你想更新一个项目并返回整个项目,你会怎么做?我想你会这样做

val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables
val returnedItem = q.update((3,"test"))
但是,下面的代码不起作用,我看不到任何关于如何执行此操作的文档


请注意,我知道您可以事先查询该项,对其进行更新,然后在原始对象上使用copy,但是这需要大量的样板文件(以及DB trips)

此功能在Slick(v2或v3-M1)中不受支持;虽然我没有看到任何具体的原因禁止它的实现,
UPDATE。。。返回
不是标准的SQL功能(例如,H2不支持它:)。我将留给读者作为练习,探索如何安全有效地模拟缺少
UDPATE。。。返回

当您在scala.slick.lifted.Query上调用“returning”时,它会给您一个。您将找不到
update
方法,尽管有一个
insertOrUpdate
方法;但是,
insertOrUpdate
仅返回
返回的
表达式结果。如果发生插入,则会返回
进行更新,因此此处没有帮助

由此我们可以得出结论,如果您想使用
更新。。。返回
SQL特性,您需要使用或滚动您自己的补丁到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")))

我不明白您是想更新一个对象并返回它,还是想更新一个对象,然后用刚才的更新让
TableQuery
,如果是第二个,您可能只需要将
.run
附加到正在执行的查询。假设我有一个从
案例类
到表列的完整投影,我想更新这些列的子集(即
.map(x=>(x.someColumn,x.anotherColumn))
并返回整个更新的对象(返回
SomeTables
)。因此,在上面的示例中,一些表可能有一个默认的
*
投影,该投影将有5列,我正在更新其中的两列(
someColumn
anotherColumn
),但是我想返回整个
表(或对象)这将包含5列从我的初学者经验中我看不到出路除了更新,然后选择行,您调用的
update
方法返回
Int
(即更新是否成功)。抱歉,我帮不上忙,希望有经验的人能回答。您看到的错误是什么?编译时还是运行时?并非所有的DMB都支持您所想的,并且会引发异常。请尝试
返回SomeTables.map(uu.yetAnotherColumn)
返回SomeTables.map(x=>x)
。谢谢!如果有Slick 3.0的更新版本那就太好了:-)Slick 3在这方面有什么不同吗?我们还没有更新到Slick 3;我相信一般的方法会起作用,但很可能API和数据结构有所改变。我在中更新了@TimHarper为Slick 3.0编写的代码。非常感谢Tim!
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")))