当使用平滑功能时,如何在返回一个更新的表的同时更新一个表中的几列?
假设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行程)
Slick(v2或v3-M1)不支持此功能。尽管我看不到任何禁止它实现的特定原因,但这UPDATE … RETURNING不是标准的SQL功能)。我将作为练习供读者阅读,以探讨如何安全有效地模拟缺少RDBMS的功能UDPATE … RETURNING。
当您在上调用“ returning”时scala.slick.lifted.Query,它会为您提供JdbcInsertInvokerComponent $ ReturningInsertInvokerDef。update尽管有一种insertOrUpdate方法,但您找不到任何方法。但是,insertOrUpdate仅在returning发生插入时才返回表达式结果,None并且返回表达式以进行更新,因此这里没有帮助。
JdbcInsertInvokerComponent $ ReturningInsertInvokerDef
由此可以得出结论,如果要使用UPDATE … RETURNINGSQL功能,则需要使用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")))