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")))
Другие вопросы по тегам