回答

收藏

Slick 2-更新表中的列并返回整个表对象

技术问答 技术问答 82 人阅读 | 0 人回复 | 2023-09-13

当使用平滑功能时,如何在返回一个更新的表的同时更新一个表中的几列?
' [1 o0 }$ @1 `& |" G6 V8 ^' H假设SomeTables有一些TableQuery,例如,如果您想向表中添加一个项目(并返回新添加的项目),通常会编写这样的查询
- X3 L% x( `( zval returnedItem = SomeTables returning SomeTables += someTable4 X4 b! i, {) s6 s7 w4 X
/ m& \; R2 v5 M. w) h+ i
如果您要更新商品并将整个商品退回整个商品,该怎么做,我怀疑您会做这样的事情+ _2 a" R* k- k5 a
val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables
! P- B! D+ m8 @- Aval returnedItem = q.update((3,"test"))
  z9 j1 O; O4 v* i3 r
4 u: T1 _$ H, b3 u+ J  k但是,以下代码不起作用,并且我看不到任何有关如何执行此操作的文档) _- `& V9 e( \  ^3 {+ u8 P" T$ C
请注意,我知道您可以事先查询该项目,对其进行更新,然后在原始对象上使用复制,但是这需要大量的样板(以及DB行程): p1 R$ n9 E: u/ T( p# x
               
- Y5 B8 r) N) ^$ f- j2 L0 m$ |解决方案:; m+ D* z& E/ T9 A- j( n4 f, n
               
5 O0 \9 ]6 |, o1 ^' Z( P4 G: V6 F/ _! i5 H" I0 H/ W

3 Y) q" U  r/ G: L4 p. C                Slick(v2或v3-M1)不支持此功能。尽管我看不到任何禁止它实现的特定原因,但这UPDATE … RETURNING不是标准的SQL功能)。我将作为练习供读者阅读,以探讨如何安全有效地模拟缺少RDBMS的功能UDPATE … RETURNING。
- [8 z* r, U" N1 J/ o2 X当您在上调用“ returning”时scala.slick.lifted.Query,它会为您提供JdbcInsertInvokerComponent $ ReturningInsertInvokerDef。update尽管有一种insertOrUpdate方法,但您找不到任何方法。但是,insertOrUpdate仅在returning发生插入时才返回表达式结果,None并且返回表达式以进行更新,因此这里没有帮助。
% l  m8 k: h1 `* }* S8 _# W由此可以得出结论,如果要使用UPDATE … RETURNINGSQL功能,则需要使用StaticQuery或将自己的补丁滚动到Slick。您可以手动编写查询(并以GetResult / SetParameter序列化器的形式重新实现表投影),也可以尝试以下代码段:
* f( v. K8 c  }4 ppackage com.spingo.slick0 [  S/ N$ N  a) K# `/ K/ B$ z
import scala.slick.driver.JdbcDriver.simple.{queryToUpdateInvoker, Query}5 ^& e0 x7 p7 G# ]( f: ^
import scala.slick.driver.JdbcDriver.{updateCompiler, queryCompiler, quoteIdentifier}, }% s/ F# _$ R2 G
import scala.slick.jdbc.{ResultConverter, CompiledMapping, JdbcBackend, JdbcResultConverterDomain, GetResult, SetParameter, StaticQuery => Q}
1 J& w4 E2 k: m/ a0 a# Q$ A4 v' Kimport scala.slick.util.SQLBuilder
/ ^; N' Y6 Y+ ?: y7 S1 Pimport slick.ast._* o" n% m! [. k  `# Q; d
object UpdateReturning {1 Z5 \$ `' v2 m$ ^
  implicit class UpdateReturningInvoker[E, U, C[_]](updateQuery: Query[E, U, C]) {; N3 [3 T0 R9 A2 u# b6 k7 E- ^
    def updateReturning[A, F](returningQuery: Query[A, F, C], v: U)(implicit session: JdbcBackend#Session): List[F] = {
) O" ~% w+ j2 Y7 K. k      val ResultSetMapping(_,) W4 [/ g/ D1 O# N
        CompiledStatement(_, sres: SQLBuilder.Result, _),; P$ A/ F) f1 K; S& E) f
        CompiledMapping(_updateConverter, _)) = updateCompiler.run(updateQuery.toNode).tree
# _) A4 t0 x9 r4 d      val returningNode = returningQuery.toNode3 c" s# y; r) @- m+ t8 Y+ k4 v
      val fieldNames = returningNode match {2 R; C% \# }9 y1 z- J
        case Bind(_, _, Pure(Select(_, col), _)) =>4 [4 a% m- |  v* G) o$ b
          List(col.name)
( D- g& J( {" D( U7 E! y; F        case Bind(_, _, Pure(ProductNode(children), _)) =>
4 P9 S3 s' Y5 }0 n- i* n( ]          children map { case Select(_, col) => col.name } toList! S6 c9 p# s9 o0 ]1 r9 m. _) x2 a6 |
        case Bind(_, TableExpansion(_, _, TypeMapping(ProductNode(children), _, _)), Pure(Ref(_), _)) =>0 z- T, c; S; j0 J: j; k! F& T% p
          children map { case Select(_, col) => col.name } toList
9 l7 I- W$ @6 b) [      }
5 v+ M8 D4 n8 X, N6 G+ L      implicit val pconv: SetParameter[U] = {
/ E9 q6 d. A2 Q+ F+ Z        val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = updateCompiler.run(updateQuery.toNode).tree0 B4 q' n! }7 `
        val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, U]]
4 X/ B, Y* C1 Z, Q  T/ O+ w1 `+ S        SetParameter[U] { (value, params) =>
0 W' k/ J! s5 _# [* ]$ D0 e- t          converter.set(value, params.ps)
8 l( X; D2 E4 @        }8 V$ [6 J' n/ [
      }
: l' w5 B4 X, S& H: F) V0 h0 M      implicit val rconv: GetResult[F] = {
5 y5 u1 h; N5 c& d1 A        val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = queryCompiler.run(returningNode).tree
# M- j! U2 q& v) t2 n. @        val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, F]]
# l$ v/ E  {, N3 |        GetResult[F] { p => converter.read(p.rs) }7 Y' @/ w& _' s3 X* @3 S! g1 A* x
      }9 n! f' \+ n7 j2 [
      val fieldsExp = fieldNames map (quoteIdentifier) mkString ", "% k" C' J9 E8 v( R+ O+ Y; ]( Q  q. Q
      val sql = sres.sql + s" RETURNING ${fieldsExp}"
: C0 O. q0 |: i1 q      val unboundQuery = Q.query[U, F](sql)# B3 `6 [0 J: L, W9 p  M" ]6 i0 S
      unboundQuery(v).list7 {; m- Z  F, h7 y/ Z
    }- B* V( \5 r, X, a; T, f7 g
  }; g6 a7 F  C) C
}; w( \& i5 s; M9 H. u1 D" a
$ O4 Q7 ^4 Y$ u0 d
我敢肯定,上述情况可以改善;我是基于对Slick内部原理的有限了解而编写的,它对我有用,并且可以利用您已经定义的投影/类型映射。3 u  K7 N' y& D, u% x$ l
用法:- h, V) j  f% o; h/ \
import com.spingo.slick.UpdateReturning._
* w8 b# l5 z8 z, [8 l% Tval tq = TableQuery[MyTable]+ D. [0 _0 m1 p9 o5 Y
val st = tq filter(_.id === 1048003) map { e => (e.id, e.costDescription) }
9 p( \3 V8 {1 J, n/ h: G3 J" l+ Wst.updateReturning(tq map (identity), (1048003, Some("such cost")))
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则