Slick codegen & tables with > 22 columns

后端 未结 4 927
囚心锁ツ
囚心锁ツ 2020-12-09 13:28

I\'m new to Slick. I\'m creating a test suite for a Java application with Scala, ScalaTest and Slick. I\'m using slick to prepare data before the test and to do assertions o

4条回答
  •  鱼传尺愫
    2020-12-09 14:13

    Update 2019-02-15: *with the release of Slick 3.3.0, as answered by @Marcus there's built-in support for code generation of tables with > 22 columns.

    As of Slick 3.2.0, the simplest solution for >22 param case class is to define the default projection in the * method using mapTo instead of the <> operator (per documented unit test):

    case class BigCase(id: Int,
                       p1i1: Int, p1i2: Int, p1i3: Int, p1i4: Int, p1i5: Int, p1i6: Int,
                       p2i1: Int, p2i2: Int, p2i3: Int, p2i4: Int, p2i5: Int, p2i6: Int,
                       p3i1: Int, p3i2: Int, p3i3: Int, p3i4: Int, p3i5: Int, p3i6: Int,
                       p4i1: Int, p4i2: Int, p4i3: Int, p4i4: Int, p4i5: Int, p4i6: Int)
    
    class bigCaseTable(tag: Tag) extends Table[BigCase](tag, "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")
    
          // HList-based wide case class mapping
          def m3 = (
            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 :: HNil
          ).mapTo[BigCase]
    
          def * = m3
    }
    

    EDIT

    So, if you then want the slick-codegen to produce huge tables using the mapTo method described above, you override the relevant parts to the code generator and add in a mapTo statement:

    package your.package
    import slick.codegen.SourceCodeGenerator
    import slick.{model => m}
    
    
    class HugeTableCodegen(model: m.Model) extends SourceCodeGenerator(model) with GeneratorHelpers[String, String, String]{
    
    
      override def Table = new Table(_) {
        table =>
    
        // always defines types using case classes
        override def EntityType = new EntityTypeDef{
          override def classEnabled = true
        }
    
        // allow compound statements using HNil, but not for when "def *()" is being defined, instead use mapTo statement
        override def compoundValue(values: Seq[String]): String = {
          // values.size>22 assumes that this must be for the "*" operator and NOT a primary/foreign key
          if(hlistEnabled && values.size > 22) values.mkString("(", " :: ", s" :: HNil).mapTo[${StringExtensions(model.name.table).toCamelCase}Row]")
          else if(hlistEnabled) values.mkString(" :: ") + " :: HNil"
          else if (values.size == 1) values.head
          else s"""(${values.mkString(", ")})"""
        }
    
        // should always be case classes, so no need to handle hlistEnabled here any longer
        override def compoundType(types: Seq[String]): String = {
          if (types.size == 1) types.head
          else s"""(${types.mkString(", ")})"""
        }
      }
    }
    

    You then structure the codegen code in a separate project as documented so that it generates the source at compile time. You can pass your classname as an argument to the SourceCodeGenerator you're extending:

    lazy val generateSlickSchema = taskKey[Seq[File]]("Generates Schema definitions for SQL tables")
    generateSlickSchema := {
    
      val managedSourceFolder = sourceManaged.value / "main" / "scala"
      val packagePath = "your.sql.table.package"
    
      (runner in Compile).value.run(
        "slick.codegen.SourceCodeGenerator", (dependencyClasspath in Compile).value.files,
        Array(
          "env.db.connectorProfile",
          "slick.db.driver",
          "slick.db.url",
          managedSourceFolder.getPath,
          packagePath,
          "slick.db.user",
          "slick.db.password",
          "true",
          "your.package.HugeTableCodegen"
        ),
        streams.value.log
      )
      Seq(managedSourceFolder / s"${packagePath.replace(".","/")}/Tables.scala")
    }
    

提交回复
热议问题