Slick 3.0 - обновить столбцы в таблице и вернуть весь объект таблицы?

Вот реализация для Slick 2.

Slick 2 - Обновление столбцов в таблице и вернуть весь объект таблицы

У кого-нибудь есть идеи, как это реализовать в Slick 3?


person Hanfei Sun    schedule 13.05.2015    source источник


Ответы (2)


Я смог выполнить эту работу, расширив потрясающую работу Тима Харпера в https://stackoverflow.com/a/28148606/310275< /а>

Вот что я сделал сейчас:

package utils

import scala.language.existentials

import slick.ast._
import slick.driver.PostgresDriver._
import slick.driver.PostgresDriver.api._
import slick.jdbc.{GetResult, JdbcResultConverterDomain, SetParameter, StaticQuery ⇒ Q, StaticQueryInvoker, StreamingInvokerAction}
import slick.profile.SqlStreamingAction
import slick.relational.{CompiledMapping, ResultConverter}
import slick.util.SQLBuilder

object Slick {
  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 db: Database) = {
        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 pconvUnit = pconv.applied(v)
        val sql = sres.sql + s" RETURNING ${fieldsExp}"
        val unboundQuery = Q.query[U, F](sql)
        val boundQuery = unboundQuery(v)

        new StreamingInvokerAction[Vector[F], F, Effect] {
          def statements = List(boundQuery.getStatement)
          protected[this] def createInvoker(statements: Iterable[String]) = new StaticQueryInvoker[Unit, F](statements.head, pconvUnit, (), rconv)
          protected[this] def createBuilder = Vector.newBuilder[F]
        }.asInstanceOf[SqlStreamingAction[Vector[F], F, Effect]]
      }
    }
  }

}

Он берет его код, а затем преобразует StaticQuery в SqlStreamingAction.

import utils.Slick.UpdateReturning._

val update = Users.filter(_.id === 1).map(_.firstName).
updateReturning(Users.map(_.firstName), ("Jane"))

val firstName = db.run(update.headOption).futureValue.get
firstName must === ("Jane")
person jpfuentes2    schedule 21.07.2015
comment
StaticQuery не работает в версии 3.1.1. Есть ли обновление для этого? - person norcalli; 14.06.2016

Вот решение jpfuentes2, адаптированное к slick 3.1.1:

package utils

import scala.language.existentials
import slick.ast._
import slick.jdbc.PostgresProfile._
import slick.jdbc.PostgresProfile.api._
import slick.jdbc._
import slick.relational.{CompiledMapping, ResultConverter}
import slick.util.SQLBuilder

object Slick {
  object UpdateReturning {

    // see https://stackoverflow.com/questions/30212785/slick-3-0-update-columns-in-a-table-and-return-whole-table-object/31545270#31545270

    implicit class UpdateReturningInvoker[E, U, C[_]](updateQuery: Query[E, U, C]) {
      def updateReturning[A, F](returningQuery: Query[A, F, C], v: U)(implicit db: Database) = {
        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 }.toSeq
          case Bind(_, _, TableExpansion(_, _, TypeMapping(ProductNode(children), _, _))) =>
            children.map { case Select(_, col) => col.name }.toSeq

          case Pure(Select(_, col), _) =>
            List(col.name)
          case Pure(ProductNode(children), _) =>
            children.map { case Select(_, col) => col.name }.toSeq
          case TableExpansion(_, _, TypeMapping(ProductNode(children), _, _)) =>
            children.map {
              case Select(_, col) => col.name
              case OptionApply(col) =>
                col match {
                  case Select(_, childCol) => childCol.name
                }
            }.toSeq
        }

        implicit val pconv: SetParameter[U] = {
          val ResultSetMapping(_, _, CompiledMapping(converter: ResultConverter[JdbcResultConverterDomain, U], _)) =
            updateCompiler.run(updateQuery.toNode).tree
          SetParameter[U] { (value, params) =>
            converter.set(value, params.ps)
          }
        }

        implicit val rconv: GetResult[F] = {
          val ResultSetMapping(_, _, CompiledMapping(converter: ResultConverter[JdbcResultConverterDomain, F], _)) =
            queryCompiler.run(returningNode).tree
          GetResult[F] { p => converter.read(p.rs) }
        }

        val fieldsExp = fieldNames.map(quoteIdentifier).mkString(", ")
        val pconvUnit = pconv.applied(v)
        val sql = sres.sql + s" RETURNING $fieldsExp"

        SQLActionBuilder(List(sql), pconvUnit).as[F]
      }
    }

  }
}

Есть пример проекта:

https://github.com/shiraeeshi/slick-update-returning-example

person Minoru Shiraeeshi    schedule 01.10.2018