0

My case class as about 26 database fields, but there is a limitation in scala and/ or in Scala Slick to 22 fields.

Is there a way to break this limitation?

I tried this: Slick codegen & tables with > 22 columns

without any nice result^^

Thanks in advance.

Update:

I tried the example but got errors ...

My code looks like this:

package dto.regulations.models

import ai.x.play.json.Jsonx
import dto.client.models.{PriceCategoriesModel, SlaLevelsModel}
import dto.crawler.models.{CrawlerModel, CrawlerUrlsModel, ResultsCrawlerModel}
import play.api.db.slick.HasDatabaseConfigProvider
import play.api.libs.json._
import slick.jdbc.JdbcProfile
import slick.lifted.ProvenShape

import slick.jdbc.H2Profile.api._
import shapeless._
import slickless._

case class RegulationModel(
                            regulationId: Option[Int] = None,
                            name: String,
                            [.... some other attributes....]
                            // Non Database fields
                            priceCategory: Option[PriceCategoriesModel]  = None,
                            slaLevel: Option[SlaLevelsModel]  = None,

                            markets: Option[Seq[MarketsModel]] = None,
                            tags: Option[Seq[TagsModel]]  = None,
                            crawler: Option[CrawlerModel]  = None,
                            commentCreatedAt: Option[String]  = None,
                            creatorComment: Option[String]  = None,
                            approved: Option[Boolean]  = None,
                            approverComment: Option[String],
                            approvedAt: Option[String],
                            creatorUuid: Option[String],
                            approverUuid: Option[String],

                            regulationPackages: Option[Seq[RegulationPackagesModel]],
                            regulation_Evaluations: Option[Seq[Regulation_EvaluationModel]]
                          ) {
  def toApi: RegulationApi = RegulationApi.fromModel(this)
}


object RegulationModel {
  implicit val regulationFormat: OFormat[RegulationModel] = Jsonx.formatCaseClass[RegulationModel]
}


case class RegulationApi(
                          regulationId: Option[Int] = None,
                          name: String,
                          description: Option[String],

                              [.... some other attributes....]


                          // Non Database fields
                          priceCategory: Option[PriceCategoriesModel],
                          slaLevel: Option[SlaLevelsModel],

                          markets: Option[Seq[MarketsModel]],
                          tags: Option[Seq[TagsModel]],
                          crawler: Option[CrawlerModel],

                          commentCreatedAt: Option[String],
                          creatorComment: Option[String],
                          approved: Option[Boolean],
                          approverComment: Option[String],
                          approvedAt: Option[String],
                          creatorUuid: Option[String],
                          approverUuid: Option[String],

                          regulationPackages: Option[Seq[RegulationPackagesModel]],
                          regulation_Evaluations: Option[Seq[Regulation_EvaluationModel]]
                        ) {
  def toModel: RegulationModel = {
    new RegulationModel(
      regulationId = this.regulationId,
      name = this.name,

          [.... some other attributes....]

      // Non Database fields
      priceCategory = None,
      slaLevel = None,
      markets = None,
      tags = None,
      crawler = None,
      commentCreatedAt = None,
      creatorComment = None,
      approved = None,
      approverComment = None,
      approvedAt = None,
      creatorUuid = None,
      approverUuid = None,
      regulationPackages = None,
      regulation_Evaluations = None
    )
  }
}

object RegulationApi {
  implicit val extendedRegulation_format: OFormat[RegulationApi] = Jsonx.formatCaseClass[RegulationApi]

  def fromModel(other: RegulationModel): RegulationApi = {
    RegulationApi(
      regulationId = other.regulationId,
      name = other.name,
      
          [.... some other attributes....]

      
      // Non Database fields
      priceCategory = None,
      slaLevel = None,
      markets = None,
      tags = None,
      crawler = None,
      commentCreatedAt = None,
      creatorComment = None,
      approved = None,
      approverComment = None,
      approvedAt = None,
      creatorUuid = None,
      approverUuid = None,
      regulationPackages = None,
      regulation_Evaluations = None
    )
  }
}


/**
  * Case Classes for Return types
  */

case class PmsFindingsWithTags(pmsFinding: RegulationModel, tags: Seq[TagsModel])

object PmsFindingsWithTags {
  implicit val pmsFindingsWithTags: OFormat[PmsFindingsWithTags] = Json.format[PmsFindingsWithTags]
}

case class PmsFindingsWithMarkets(pmsFinding: RegulationModel, markets: Seq[MarketsModel])

object PmsFindingsWithMarkets {
  implicit val pmsFindingsWithMarkets: OFormat[PmsFindingsWithMarkets] = Json.format[PmsFindingsWithMarkets]
}

case class PmsFindingsWithUrls(pmsFinding: RegulationModel, urls: Seq[CrawlerUrlsModel])

object PmsFindingsWithUrls {
  implicit val pmsFindingsWithMarkets: OFormat[PmsFindingsWithUrls] = Json.format[PmsFindingsWithUrls]
}

case class RegulationWithCrawler(regulation: RegulationModel, latestCrawlerResults: Seq[ResultsCrawlerModel], userUuid: Option[String])

object RegulationWithCrawler {
  implicit val regulationWithCrawlers: OFormat[RegulationWithCrawler] = Json.format[RegulationWithCrawler]
}

/**
  * Trait
  */

trait PmsFindingComponent {
  self: HasDatabaseConfigProvider[JdbcProfile] =>

  import profile.api._

  class Regulation(tag: Tag) extends Table[RegulationModel](tag, "Regulation") {
    def regulationId: Rep[Int] = column[Int]("regulationId", O.PrimaryKey, O.AutoInc)

    def name: Rep[String] = column[String]("name")

    def description: Rep[Option[String]] = column[Option[String]]("description")

          [.... some other attributes....]


    def shortDescriptionEn: Rep[Option[String]] = column[Option[String]]("shortDescriptionEn")

    def analysisEn: Rep[Option[String]] = column[Option[String]]("analysisEn")

    // HList-based wide case class mapping
    def * = (
      regulationId ::       name::
      description ::
      
      [.... some other attributes....]
      
      shortDescriptionEn::
      nameEn ::
      descriptionEn ::
      analysisEn :: HNil
      ).mappedWith(Generic[RegulationModel])
  }

  val regulation_query: TableQuery[Regulation] = TableQuery[Regulation] // Query object
}

But I got the following error:

enter image description here

Update enter image description here

Richard Dallaway
  • 4,250
  • 1
  • 28
  • 39
Felix
  • 5,452
  • 12
  • 68
  • 163
  • 1
    You can write mapper manually without using the autogenerated apply/unapply methods of case classes which doesn't generated for >22 field case classes :) – Artem Sokolov Aug 31 '20 at 18:59
  • Can you maybe make an example for me? – Felix Aug 31 '20 at 19:00
  • I don't use the autogenerated stuff – Felix Aug 31 '20 at 19:00
  • I mean that the "limitation" is limitation that Scala compiler doesn't generate apply/unapply methods for case classes with more than 22 fields, and you can't rely on helper function `<>` in Slick that uses those methods. But as always, you can write the final mapping manually. You can see example in this answer https://stackoverflow.com/a/49659436/14044371 – Artem Sokolov Aug 31 '20 at 19:06
  • I added my code above with the error Message I got. May you have a look at it? Thanks in advance – Felix Sep 01 '20 at 07:25

1 Answers1

3

The idea is to use an HList to represent a table with more than 22 columns.

You do not need to use slickless/shapeless for this, nor do you have to use codegen if you don't want to, because the built-in Slick HList works with the mapTo macro.

The steps are:

  1. Add the HList imports (and remove the slickless/shapeless ones):

    import slick.collection.heterogeneous.{HList, HCons, HNil}
    import slick.collection.heterogeneous.syntax._
    
  2. Define your * projection as:

    def * = (regulationId :: name :: etc... :: HNil).mapTo[RegulationModel]
    

There's now a cookbook section on the Slick website to illustate this: https://scala-slick.org/doc/3.3.3/cookbook.html#mapping-more-than-22-fields

If you'd like a little more detail on this 22 limit, there's a longer post here: https://underscore.io/blog/posts/2016/10/11/twenty-two.html

Richard Dallaway
  • 4,250
  • 1
  • 28
  • 39