2014-05-09 64 views
7

如何在更新表格中的几列时返回整个更新的表格?油滑2 - 更新表中的列并返回整个表对象

假设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旅行也一样)

+0

我不明白你是否要更新一个对象,并返回,或要更新的对象,然后有'TableQuery'你只是做了更新,如果第二,你可能只需要在正在执行的查询中追加'.run'。 –

+0

假设我有一个从case类到表格列的完整投影,我想更新这些列的一个子集(即'.map(x =>(x.someColumn,x.anotherColumn)) '返回整个更新的对象(返回'SomeTables')。所以在上面的例子中,SomeTables可以有一个默认的'*'投影,它将有5个列,我正在更新其中的两列('someColumn'和'anotherColumn' ),但是我想返回包含5列的整个'table'(或object) – mdedetrich

+0

从我的初学者经验来看,除了更新之外,我没有看到出路,然后选择行,'update'方法,重新调用返回一个'Int'(即更新是否成功)。抱歉,我无法帮助,希望有更多经验的人会回答。 –

回答

4

此功能在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"))) 
+2

谢谢!如果Slick 3.0有更新的版本,那将是非常好的:-) –

+0

Slick 3中有关于此的任何区别吗? – Ixx

+0

我们还没有更新到Slick 3。我敢肯定,一般的方法是可行的,但API和数据结构可能会有所变化。 –