Slick 2 - обновить столбцы в таблице и вернуть весь объект таблицы
Как бы вы обновили несколько столбцов в таблице таблицы, возвращая всю обновленную таблицу при использовании 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"))
Следующий код, однако, не работает, и я не вижу документации о том, как это сделать
Обратите внимание, что я знаю, что вы можете просто запросить элемент заранее, обновить его, а затем использовать копию оригинального объекта, однако это требует большого количества шаблонов (и отключений БД)
1 ответ
Эта функция не поддерживается в Slick (v2 или v3-M1); хотя я не вижу какой-либо конкретной причины, запрещающей его реализацию, UPDATE ... RETURNING
не является стандартной функцией SQL (например, H2 не поддерживает ее: http://www.h2database.com/html/grammar.html). Я оставлю в качестве упражнения читателя, чтобы узнать, как можно безопасно и эффективно эмулировать функцию для СУРБД, которой не хватает UDPATE ... RETURNING
,
Когда вы называете "возвращение" на 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")))