【问题标题】:How can I use the new Slick 2.0 HList to overcome 22 column limit?如何使用新的 Slick 2.0 HList 来克服 22 列限制?
【发布时间】:2013-12-31 13:27:38
【问题描述】:

我目前正在编写 Slick 代码以针对具有两个表 > 22 列的旧模式。我如何use the new HList code?在 Scala 2.10.3 下,我的 2.0-M3 在其他方面工作正常。 这是我目前在案例类/元组中使用的语法。我会怎么做use the new HLists mentioned in the docs?

  case class Joiner(
      id: Int,
      name: Option[String],
      contact: Option[String]
  )

  class Joiners(tag: Tag) extends Table[Joiner](tag, "joiner") {
    def id = column[Int]("id", O.PrimaryKey, O.AutoInc, O.DBType("int(11)"))
    def name = column[Option[String]]("name", O.DBType("varchar(255)"))
    def contact = column[Option[String]]("contact", O.DBType("text"))
    def * = (id, name.?, contact.?) <> (Joiner.tupled, Joiner.unapply)
  }
  val joiners = TableQuery[Joiners]

我在示例中看不到任何内容,仅在新更新的文档中简要提及。我是 Scala 和 Slick 的新手。

【问题讨论】:

  • 最初我使用的是 O.Nullable 而不是 Option[String]。根据 Vogt 先生的建议进行了更正。
  • 也发在这里:groups.google.com/d/msg/scalaquery/xNtPT6sexXI/DgW5CQkfgaMJ以后请交叉链接。
  • 抱歉,我收到一条消息说我的 Google 网上论坛帖子被拒绝了,所以我认为它不起作用。
  • 注意:如果有人试图从 Slick 2.0-M3 开始遵循此解决方案(如下),则 HList 代码不起作用——编译超过 22 列将需要数小时和千兆字节的 RAM(它呈指数增长)groups.google.com/forum/#!topic/scalaquery/xNtPT6sexXI
  • 只是为了确定,实现是正确的(据我们所知)。然而,它目前似乎会在 Scala 编译器中触发指数编译时间,这对 25 及以上的大小会非常不利。我们会研究解决这个问题。

标签: scala tuples slick hlist


【解决方案1】:

定义

使用 Scala >= 2.10.4-RC2(也由 Slick 2.0.0 代码生成器发出):

import scala.slick.collection.heterogenous._
import syntax._
class Joiners(tag: Tag) extends Table[
    Int :: Option[String] :: Option[String] :: HNil
](tag, "joiner") {
  ...
  def * = id :: name :: contact :: HNil
}

上述情况导致 Scala 2.10.3 / 2.10.4-RC1 中的编译时间呈指数增长。由于编译时间过长,超过 26 列不可行。

Scala

import scala.slick.collection.heterogenous._
import syntax._
class Joiners(tag: Tag) extends Table[
    HCons[Int, HCons[Option[String], HCons[Option[String], HNil]]]
](tag, "joiner") {
  ...
  def * = id :: name :: contact :: HNil
}

我们测试了 30-40 列没有问题。

目前在 Scala 2.10.4-RC2 中似乎仍然存在偶发性编译错误的问题,看起来它将在即将到来的 2.10.4-RC3 中修复。见https://issues.scala-lang.org/browse/SI-8146

使用示例

Joiners.run.map( r => r(2) ) // Gets column contact. It's typesafe. .apply is a macro. Only works for literals not for variables as positions.

使用 22 而不映射到案例类(Scala 2.10 中的最大字段限制为 22)。

另外:不要使用 O.Nullable。请改用column[Option[String]]。它推断可空性。

【讨论】:

  • 如果我不使用case类,我可以定义一个常规类,还是需要在某种列表中列出所有类型?
  • 另外,我还使用尾随的 '.?'对于“*”投影中的选项列还是推断出来的?
  • .?可以这么说也是推断出来的。你不需要它们。不确定列出所有类型的含义,但您可以将行映射到任何内容。概念是&lt;&gt; (factoryFunction, extractorFunction)
  • 我应该用什么来代替 case 类,因为在 Scala 2.11 之前它被限制为 22 列?如何将以前的案例类声明为 HList 并将其作为类型参数传递给表类?
  • 我更新示例以显示类型。关于案例类,您可以简单地省略它。您将无法按名称访问结果中的列,只能按位置r(2)。如果你真的想要命名访问,你可以通过编写适当的工厂和提取器函数映射到&lt;&gt; 中的普通类。
【解决方案2】:

这段代码是为了演示在Scala v2.10.4_RC1中列数超过26时仍然影响编译器的性能问题(它只是卡住了)

import java.sql.Timestamp
import scala.slick.driver.MySQLDriver.simple._
import scala.slick.collection.heterogenous._
// **** Uncomment this ****
//import scala.slick.collection.heterogenous.syntax._  



object DealSlick {


    class Deals(tag: Tag) extends Table[
      Long :: String :: String :: Option[String] :: Option[String] :: Option[String] ::
      // urlKeywords
      Option[String] :: Option[String] :: Option[String] :: Option[String] :: Option[String] ::
      // extTags
      Option[String] :: Option[String] :: Option[String] :: Option[String] :: Option[String] :: 
      // currency
      Option[String] :: Option[String] :: 
      // price
      Option[Double] :: Option[Double] :: Option[Double] :: Option[Double] ::
      // extStatus
      Option[String] :: Option[String] :: Option[Int] :: Option[Int] ::

     /* If you add more columns the compiler get stuck in a never-ending
      * compilation possibly related to 
      * https://github.com/slick/slick/issues/577
      */

     // endAt
     Option[Timestamp] :: /*Option[Timestamp] :: Option[Timestamp] :: Option[Timestamp] ::
     // timeZoneOffset
     Option[Int] :: Option[String] :: Option[Timestamp] :: Option[String] ::
     // locationName
     Option[String] :: Option[String] :: Option[String] :: Option[String] ::
     // city
     Option[String] :: Option[String] :: Option[String] :: Option[String] :: Option[String] :: 
     // latitude
     Option[Double] :: Option[Double] :: 
     // merchantTitle
     Option[String] :: */
     // End of list
     HNil
     ](tag, "deal") {
     def                id = column[Long]("id", O.PrimaryKey)
     def          siteName = column[String]("partner_site_name", O.NotNull)
     def        siteDomain = column[String]("partner_site_domain", O.NotNull)
     def    localeLanguage = column[Option[String]]("deal_language") 
     def     localeCountry = column[Option[String]]("deal_country") 
     def             extId = column[Option[String]]("deal_ext_id") 
     def       urlKeywords = column[Option[String]]("deal_url_keywords") 
     def          keywords = column[Option[String]]("deal_keywords") 
     def     extCategories = column[Option[String]]("deal_ext_categories") 
     def      categoryText = column[Option[String]]("deal_category_text") 
     def          coverage = column[Option[String]]("deal_coverage") 
     def           extTags = column[Option[String]]("deal_ext_tags") 
     def             title = column[Option[String]]("deal_title") 
     def       description = column[Option[String]]("deal_description")
     def          extImage = column[Option[String]]("deal_ext_image") 
     def               url = column[Option[String]]("deal_url") 
     def          currency = column[Option[String]]("deal_currency") 
     def       currencySym = column[Option[String]]("deal_currency_sym") 
     def             price = column[Option[Double]]("deal_price") 
     def            saving = column[Option[Double]]("deal_saving") 
     def          discount = column[Option[Double]]("deal_discount") 
     def            dvalue = column[Option[Double]]("deal_value") 
     def         extStatus = column[Option[String]]("deal_ext_status") 
     def            status = column[Option[String]]("deal_status") 
     def           soldQty = column[Option[Int]]("deal_sold_qty") 
     def           leftQty = column[Option[Int]]("deal_left_qty") 
     def             endAt = column[Option[Timestamp]]("deal_end_at") 
 /*    def          endAtUtc = column[Option[Timestamp]]("deal_end_at_utc") 
     def         expiresAt = column[Option[Timestamp]]("deal_expires_at") 
     def      expiresAtUtc = column[Option[Timestamp]]("deal_expires_at_utc") 
     def    timeZoneOffset = column[Option[Int]]("time_zone_offset") 
     def      timeZoneName = column[Option[String]]("time_zone_name")
     def       timeGrabbed = column[Option[Timestamp]]("time_grabbed") 
     def  timeRemainingStr = column[Option[String]]("time_remaining_str") 
     def      locationName = column[Option[String]]("location_name")
     def           address = column[Option[String]]("location_address") 
     def            street = column[Option[String]]("location_street") 
     def        postalCode = column[Option[String]]("location_postalcode")
     def              city = column[Option[String]]("location_city") 
     def          province = column[Option[String]]("location_province") 
     def            region = column[Option[String]]("location_region") 
     def             state = column[Option[String]]("location_state") 
     def           country = column[Option[String]]("location_country") 
     def          latitude = column[Option[Double]]("location_latitude") 
     def         longitude = column[Option[Double]]("location_longitude") 
     def     merchantTitle = column[Option[String]]("merchant_title") 

*/
def * = (id :: siteName :: siteDomain :: localeLanguage :: localeCountry :: extId :: 
         urlKeywords :: keywords :: extCategories :: categoryText :: coverage :: 
         extTags :: title :: description :: extImage :: url ::
         currency :: currencySym :: price :: saving :: discount :: dvalue ::
         extStatus :: status :: soldQty :: leftQty ::
         endAt :: /*endAtUtc :: expiresAt :: expiresAtUtc ::
         timeZoneOffset :: timeZoneName :: timeGrabbed :: timeRemainingStr ::
         locationName :: address :: street :: postalCode :: 
         city :: province :: region :: state :: country ::
         latitude :: longitude ::
         merchantTitle :: */
         HNil )  
   }

}

** 更新 **

更新到 Scala 2.10.4-RC2 后,编译器在编译过程中又走了几步,但又卡住了:

这是编译器输出仅定义几个表列时

[信息] [加载类文件/Users/max/.ivy2/cache/com.typesafe.slick/slick_2.10/jars/slick_2.10-2.0.0.jar(scala/slick/backend/DatabaseComponent.类)在 1ms]

[信息] [加载类文件/Users/max/.ivy2/cache/com.typesafe.slick/slick_2.10/jars/slick_2.10-2.0.0.jar(scala/slick/lifted/ShapedValue.类)在 2 毫秒内]

[info] [loaded package loader util in 2ms] 当使用超过 26 列时,此输出永远不会打印在屏幕上

【讨论】:

  • 这可能自 RC1 以来已修复,但我不确定。我试图追踪我看到的提交和错误跟踪消息,但没有找到。如果你想自己修补 Scala 或 Slick,这里有修复。
  • Christopher,感谢您的提示,但我已经尝试过 Scala v2.10.4-RC2,我必须说我刚刚注意到编译器在这一点上不再阻塞,但它卡住了更进一步。如果您和您的团队认为值得对此问题进行调查,您可以使用我发布的代码进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-07
  • 2013-08-05
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 2014-04-15
  • 1970-01-01
相关资源
最近更新 更多