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")))