Slick 2.10-RC1, Scala 2.11.x, bypassing 22 arity l

2019-03-20 16:46发布

问题:

I am having issues in mapping a Table that has > 22 columns specifically into a case class, assuming you have the following code

import slick.driver.PostgresDriver
import scala.slick.collection.heterogenous._
import syntax._
import shapeless.Generic

case class TwentyThreeCaseClass(
    val id:Option[Long],
    val one:String,
    val two:String,
    val three:String,
    val four:String,
    val five:String,
    val six:String,
    val seven:String,
    val eight:String,
    val nine:String,
    val ten:String,
    val eleven:String,
    val twelve:String,
    val thirteen:String,
    val fourteen:String,
    val fifteen:String,
    val sixteen:String,
    val seventeen:String,
    val eighteen:String,
    val nineteen:String,
    val twenty:String,
    val twentyOne:String,
    val twentyTwo:String,
    val twentyThree:String,
    val twentyFour:String
)

class TwentyThreeTable(tag:Tag) extends Table[TwentyThreeCaseClass](tag,"twenty_three_table") {
    def id = column[Long]("id",O.PrimaryKey,O.AutoInc)
    def one = column[String]("one")
    def two = column[String]("two")
    def three = column[String]("three")
    def four = column[String]("four")
    def five = column[String]("five")
    def six = column[String]("six")
    def seven = column[String]("seven")
    def eight = column[String]("eight")
    def nine = column[String]("nine")
    def ten = column[String]("ten")
    def eleven = column[String]("eleven")
    def twelve = column[String]("twelve")
    def thirteen = column[String]("thirteen")
    def fourteen = column[String]("fourteen")
    def fifteen = column[String]("fifteen")
    def sixteen = column[String]("sixteen")
    def seventeen = column[String]("seventeen")
    def eighteen = column[String]("eighteen")
    def nineteen = column[String]("nineteen")
    def twenty = column[String]("twenty")
    def twentyOne = column[String]("twentyOne")
    def twentyTwo = column[String]("twentyTwo")
    def twentyThree = column[String]("twentyThree")
    def twentyFour = column[String]("twentyFour")

    private def iso[L <: HList, M <: HList](l: L)
                                 (implicit iso: Generic.Aux[TwentyThreeCaseClass, M], eq: L =:= M): TwentyThreeCaseClass = iso.from(l)

    def * =
        id.? ::
        one ::
        two ::
        three ::
        four ::
        five ::
        six ::
        seven ::
        eight ::
        nine ::
        ten ::
        elven ::
        twelve ::
        thirteen ::
        fourteen ::
        fifteen ::
        sixteen ::
        seventeen ::
        eighteen ::
        nineteen ::
        twenty ::
        twentyOne ::
        twentyTwo ::
        twentyThree ::
        twentyFour ::
        HNil
        // Do stuff here to map to a case class

}

How exactly would you go about constructing/extracting the table into the TwentyThreeCaseClass. Example code is given on how to make a slick Table map to a HList, but code is not given on how to map a Table to a case class > 22 parameters via a HList (you can't use tuples, because the arity limit in Scala still applies for tuples, you can't make a tuple with more then twenty two elements)

The iso is there, because we use this generic iso code to map from a HList to a case class with the same shape in our shapeless code outside of slick, so theoretically speaking you should be able to use iso to construct the case class from the HList, i just have no idea how to use iso in the context of slick shapes

EDIT: There is the same question asked on the slick github as an issue here https://github.com/slick/slick/issues/519#issuecomment-48327043

回答1:

Figured it out, its pretty ugly though since its not generic.

def * =
    (id.? ::
    one ::
    two ::
    three ::
    four ::
    five ::
    six ::
    seven ::
    eight ::
    nine ::
    ten ::
    elven ::
    twelve ::
    thirteen ::
    fourteen ::
    fifteen ::
    sixteen ::
    seventeen ::
    eighteen ::
    nineteen ::
    twenty ::
    twentyOne ::
    twentyTwo ::
    twentyThree ::
    twentyFour ::
    HNil).shaped <>
          ({case x => TwentyThreeCaseClass(
            x(0),
            x(1),
            x(2),
            x(3),
            x(4),
            x(5),
            x(6),
            x(7),
            x(8),
            x(9),
            x(10),
            x(11),
            x(12),
            x(13),
            x(14),
            x(15),
            x(16),
            x(17),
            x(18),
            x(19),
            x(20),
            x(21),
            x(22),
            x(23),
            x(24)
            )}, ({x:TwentyThreeCaseClass =>
            Option((
              x.id ::
              x.one ::
              x.two ::
              x.three ::
              x.four ::
              x.five ::
              x.six ::
              x.seven ::
              x.eight ::
              x.nine ::
              x.ten ::
              x.eleven ::
              x.twelve ::
              x.thirteen ::
              x.fourteen ::
              x.fifteen ::
              x.sixteen ::
              x.seventeen ::
              x.eighteen ::
              x.nineteen ::
              x.twenty ::
              x.twentyOne ::
              x.twentyTwo ::
              x.twentyThree ::
              x.twentyFour ::
              HNil
            ))
          }))

It turns out there are a few things

  1. This has nothing to do with shapeless, Slick uses its own HList implementation (which has the exact same syntax as shapeless!)
  2. As far as I am aware, Slick HList doesn't appear to have any generic methods for dealing with stuff like mapping to a case class (and going from a case class to a Slick `HLIst)
  3. Eiher a library that converts Slick Hlist to a shapeless HList would be handy, or generic abilities for Slick Hlist. The former may be a better option because shapeless already does generic stuff much better than Slick, and it may be out of Slicks scope

Doing something like

def gen = Generic[TwentyThreeCaseClass]
...
.shaped <>
      ({case x => gen.from(x)}, {TwentyThreeCaseClass => Option(gen.to(x))})

Would be much more ideal

here is another example as well

https://github.com/playframework/play-slick/issues/214