【问题标题】:Slick - Update full object or more than 22 columnsSlick - 更新完整对象或超过 22 列
【发布时间】:2015-09-28 00:27:19
【问题描述】:

我有一个表 user_permissions,它有 46 个权限列以及 id 和 created_date。这个表有一个对应的UserPermissions类:

class UserPermission(val id: Long,
  val createdDate: Option[Timestamp],
  val permission1: Boolean,
  val permission2: Boolean,
  ...
  val permission46: Boolean)

和光滑的映射表

class UserPermissions(tag: Tag) extends Table[UserPermission](tag, "users_permissions") {
  def * = (
    id ::
    createdDate ::
    permission1 ::
    permission2 ::
    ...
    permission46 ::
    HNil).shaped <> (
    { case x => UserPermission(
         x(0), x(1), x(2), ... x(47))
    },
    {
       UserPermission.unapply _
    }
  }
  ... <columns defined here>
)

现在我想更新由id 标识的UserPermission 集。我的功能是:

object UserPermissions {
  val userPermissions = TableQuery[UserPermissions]

  def update(userPermission: UserPermission)(implicit session: Session) = {
    userPermissions.filter(_.id === userPermission.id.get).update(userPermission)
  }
}

这不起作用并抛出异常:

play.api.Application$$anon$1: Execution exception[[SQLServerException: Cannot update identity column 'id'.]]

这是有道理的,因为 Slick 生成的 SQL 是:

update "users_permissions" set "id" = ?, "created_date" = ?, ...

问题 1 所以我的第一个问题是 我无法使用 slick 更新完整的 UserPermission 对象。 如果我能解决这个问题,那就太好了。


由于我无法更新完整对象,所以我想yield 我要更新的列然后触发更新查询。代码如下所示:

def update(obj: UserPermission)(implicit session: Session) = {
    val query = for {
      p <- userPermissions
      if p.id === obj.id.get
    } yield (p.permission1, p.permission2, ... p.permission46)
    query.update(obj.permission1, obj.permission2, ... obj.permission46)
}

问题 2 现在 slick 不会更新 query.update() 函数中的 46 列。它一次只能处理 22 列。如何更新我的 UserPermissions 对象?

我能想到的一个不好的解决方案是第一次更新 22,然后更新 22 第二,然后在第三次查询中更新 2。这将是 3 个我不想要的 db 更新查询。

我的问题有什么解决方案吗?


依赖项是:


scalaVersion := "2.11.4"

"com.typesafe.play" %% "play-slick" % "0.8.1"
"com.typesafe.slick" %% "slick-extensions" % "2.1.0"

【问题讨论】:

    标签: scala playframework-2.0 slick slick-2.0


    【解决方案1】:

    Slick 的领导者 Stefan Zeiger,said 我们不能。然而,他建议我们在 22 列以上的平面表上嵌套投影:

    // 2 classes for the nested structure
    case class Part(i1: Int, i2: Int, i3: Int, i4: Int, i5: Int, i6: Int)
    case class Whole(id: Int, p1: Part, p2: Part, p3: Part, p4: Part)
    
    // Note that it's a Table[Int] -- we only map the primary key in *
    object T extends Table[Int]("t_wide") {
      def id = column[Int]("id", O.PrimaryKey)
      def p1i1 = column[Int]("p1i1")
      def p1i2 = column[Int]("p1i2")
      def p1i3 = column[Int]("p1i3")
      def p1i4 = column[Int]("p1i4")
      def p1i5 = column[Int]("p1i5")
      def p1i6 = column[Int]("p1i6")
      def p2i1 = column[Int]("p2i1")
      def p2i2 = column[Int]("p2i2")
      def p2i3 = column[Int]("p2i3")
      def p2i4 = column[Int]("p2i4")
      def p2i5 = column[Int]("p2i5")
      def p2i6 = column[Int]("p2i6")
      def p3i1 = column[Int]("p3i1")
      def p3i2 = column[Int]("p3i2")
      def p3i3 = column[Int]("p3i3")
      def p3i4 = column[Int]("p3i4")
      def p3i5 = column[Int]("p3i5")
      def p3i6 = column[Int]("p3i6")
      def p4i1 = column[Int]("p4i1")
      def p4i2 = column[Int]("p4i2")
      def p4i3 = column[Int]("p4i3")
      def p4i4 = column[Int]("p4i4")
      def p4i5 = column[Int]("p4i5")
      def p4i6 = column[Int]("p4i6")
      // This is just the default projection -- It doesn't have to contain all columns
      def * = id
      // Instead, we use nested tuples for a full projection:
      def all = (
        id,
        (p1i1, p1i2, p1i3, p1i4, p1i5, p1i6),
        (p2i1, p2i2, p2i3, p2i4, p2i5, p2i6),
        (p3i1, p3i2, p3i3, p3i4, p3i5, p3i6),
        (p4i1, p4i2, p4i3, p4i4, p4i5, p4i6)
      )
      // And override create_* to get the DDL for all columns.
      // Yeah, this is ugly. It used to be much simpler in ScalaQuery.
      // We can add a helper method to simplify it.
      override def create_* =
        all.shaped.packedNode.collect {
          case Select(Ref(IntrinsicSymbol(in)), f: FieldSymbol) if in == this => f
        }.toSeq.distinct
    }
    
    T.ddl.create
    // Insert into T.all. The extra ".shaped" call is needed because we cannot
    // get the types in an implicit conversion due to SI-3346
    T.all.shaped.insert(
      0,
      (11, 12, 13, 14, 15, 16),
      (21, 22, 23, 24, 25, 26),
      (31, 32, 33, 34, 35, 36),
      (41, 42, 43, 44, 45, 46)
    )
    
    // Get the nested tuples in a query
    val q1 = T.map(_.all)
    println(q1.first)
    
    // Map the result to the case classes
    val i2 = q1.mapResult { case (id, p1, p2, p3, p4) =>
      Whole(id, Part.tupled.apply(p1), Part.tupled.apply(p2), Part.tupled.apply(p3), Part.tupled.apply(p4))
    }
    println(i2.first)
    

    现在是 Slick 的 test,包括 version 3。至于更新:

    val oData = Whole(0,
      Part(11, 12, 13, 14, 15, 16),
      Part(21, 22, 23, 24, 25, 26),
      Part(31, 32, 33, 34, 35, 36),
      Part(41, 42, 43, 44, 45, 46)
    )
    val oData2 = Whole(10,
      Part(111, 12, 13, 14, 15, 16),
      Part(121, 22, 23, 24, 25, 26),
      Part(131, 32, 33, 34, 35, 36),
      Part(141, 42, 43, 44, 45, 46)
    )
    
    ts.ddl.create
    
    ts.insert(oData)
    assertEquals(oData, ts.first)
    
    ts.filter(_.p1i2 === 12).update(oData2)
    assertEquals(oData2, ts.first)
    

    具有 Slick 投影的嵌套对象可以为您带入或带走的单个对象展平。

    【讨论】:

      【解决方案2】:

      对问题2的一些建议:

      • 可以使用 Slick 3.0 吗?这个版本好像有solution
      • 您能否更改数据库的布局,使权限是行而不是列?无论如何,这似乎更具可扩展性

      【讨论】:

      • 感谢您提供建议。我在发布之前已经考虑过它们,但我无法实现其中任何一个。由于升级 play 和 slick 是未来 sprint 的一部分,并且表格布局无法更改,因为它在生产系统中以及许多应用程序与当前表格设计紧密耦合,因此它们都会受到影响。
      【解决方案3】:

      此问题由slick-2.0 标记,但如果您 google “slick update more than 22”,它是第一个链接,因此我认为值得详细描述解决方法。

      如前所述,从 v3 开始,Slick 拥有自己的异构列表实现。

      请在下面找到整个Slick 3.3.3 示例:

      import slick.collection.heterogeneous.HNil
      import slick.jdbc.H2Profile.api._
      import slick.jdbc.JdbcBackend.Database
      
      import scala.concurrent.Await
      import scala.concurrent.duration._
      import scala.concurrent.ExecutionContext.Implicits.global
      
      object HelloWorld {
      
        def main(args: Array[String]): Unit = {
          val db = Database.forURL("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", driver="org.h2.Driver")
      
          val table = new TableQuery(tag => new ManyColumnTable(tag))
      
          val insertAction = table += Row(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)
      
          val updateAction = table
            .map { r => r.c1 :: r.c2 :: r.c3 :: r.c4 :: r.c5 :: r.c6 :: r.c7 :: r.c8 :: r.c9 :: r.c10 :: r.c11 :: r.c12 ::
                        r.c13 :: r.c14 :: r.c15 :: r.c16 :: r.c17 :: r.c18 :: r.c19 :: r.c20 :: r.c21 :: r.c22 :: r.c23 ::
                        r.c24 :: HNil }
            .update { 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 :: 0 ::
                      0 :: 0 :: 0 :: 0 :: HNil }
      
          val f = for {
            _ <- db.run(table.schema.create)
            insertedRows <- db.run(insertAction)
            updatedRows <- db.run(updateAction)
          } yield {
            println(s"There were $insertedRows rows inserted")
            println(s"There were $updatedRows rows updated")
          }
      
          Await.ready(f, 10.seconds)
        }
      
        case class Row(c1:Int,c2:Int,c3:Int,c4:Int,c5:Int,c6:Int,c7:Int,c8:Int,c9:Int,c10:Int,c11:Int,c12:Int,c13:Int,
                       c14:Int,c15:Int,c16:Int,c17:Int,c18:Int,c19:Int,c20:Int,c21:Int,c22:Int,c23:Int,c24:Int)
        class ManyColumnTable(tag: Tag) extends Table[Row](tag, "many_columns_table") {
      
          def * = (c1 :: c2 :: c3 :: c4 :: c5 :: c6 :: c7 :: c8 :: c9 :: c10 :: c11 :: c12 :: c13 :: c14 :: c15 :: c16 ::
                   c17 :: c18 :: c19 :: c20 :: c21 :: c22 :: c23 :: c24 :: HNil).mapTo[Row]
      
          def c1 = column[Int]("c1")
          def c2 = column[Int]("c2")
          def c3 = column[Int]("c3")
          def c4 = column[Int]("c4")
          def c5 = column[Int]("c5")
          def c6 = column[Int]("c6")
          def c7 = column[Int]("c7")
          def c8 = column[Int]("c8")
          def c9 = column[Int]("c9")
          def c10 = column[Int]("c10")
          def c11 = column[Int]("c11")
          def c12 = column[Int]("c12")
          def c13 = column[Int]("c13")
          def c14 = column[Int]("c14")
          def c15 = column[Int]("c15")
          def c16 = column[Int]("c16")
          def c17 = column[Int]("c17")
          def c18 = column[Int]("c18")
          def c19 = column[Int]("c19")
          def c20 = column[Int]("c20")
          def c21 = column[Int]("c21")
          def c22 = column[Int]("c22")
          def c23 = column[Int]("c23")
          def c24 = column[Int]("c24")
      
        }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2015-07-24
        • 1970-01-01
        • 2014-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多