Slick 3.0 - Update columns in a table and return whole table object?

后端 未结 2 1129
借酒劲吻你
借酒劲吻你 2020-12-21 21:56

Here is the implementation for Slick 2.

Slick 2 - Update columns in a table and return whole table object

Does anyone have ideas about how to implement this

相关标签:
2条回答
  • 2020-12-21 22:15

    Here is jpfuentes2's solution adapted to 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]
          }
        }
    
      }
    }
    

    There is an example project:

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

    0 讨论(0)
  • 2020-12-21 22:21

    I was able to make this work by extending the awesome work by Tim Harper in https://stackoverflow.com/a/28148606/310275

    Here's what I've done now:

    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]]
          }
        }
      }
    
    }
    

    It takes his code then transforms the StaticQuery into a 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")
    
    0 讨论(0)
提交回复
热议问题