【问题标题】:Extending SLICK Tables in a DRY manner以 DRY 方式扩展 SLICK 表
【发布时间】:2013-06-21 21:23:16
【问题描述】:

我有一个关于 Slick/Scala 的有趣问题,我希望你们中的一个好人能够帮助我。

我有几个表,并在 SLICK 案例类中扩展

case class A(...)
case class B(...)
case class C(...)

共享这些公共字段

(id: String, livemode: Boolean, created: DateTime, createdBy : Option[Account]) . 

因为这些字段在每个案例类中都重复出现,所以我想探索将它们提取到单个对象或类型中的可能性。

但是,在创建 SLICK 表对象时,我希望最终也包含这些公共字段,这样我就可以在每个表中保留它们各自的值。

object AsTable extends Table[A]("a_table") { 
  ...
  def id = column[String]("id", O.PrimaryKey)
  def livemode = column[Boolean]("livemode", O.NotNull)
  def created = column[DateTime]("created", O.NotNull)
  def createdBy = column[Account]("created_by", O.NotNull)
  ... 
} 

实际上,我正在寻找的最终结果是允许我更改公共字段,而无需更新每个表。

有没有办法做到这一点?

提前致谢

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    我还没有尝试过,但是你混合的特征怎么样:

    trait CommonFields { this: Table[_] =>
      def id = column[String]("id", O.PrimaryKey)
      def livemode = column[Boolean]("livemode", O.NotNull)
      def created = column[DateTime]("created", O.NotNull)
      def createdBy = column[Account]("created_by", O.NotNull)
    
      protected common_* = id ~ livemode ~ created ~ createdBy 
    }
    

    那么你可以这样做:

    object AsTable extends Table[(String,Boolean,DateTime,Account,String)]("a_table") 
        with CommonFields { 
      def foo = column[String]("foo", O.NotNull)
      def * = common_* ~ foo
    } 
    

    您现在唯一需要重复的是元素的类型。

    更新

    如果你想做对象映射并且:

    1. 您映射到案例类
    2. 案例类中的字段顺序相同

    只要做:

    case class A(
        id: String,
        livemode: Boolean,
        created: DateTime,
        createdBy: Account,
        foo: String)
    
    object AsTable extends Table[A]("a_table") with CommonFields { 
      def foo = column[String]("foo", O.NotNull)
      def * = common_* ~ foo <> (A.apply _, A.unapply _)
    }
    

    这似乎是最经济的解决方案(而不是尝试在CommonFields 中定义* 并添加类型参数)。但是,如果您的字段发生变化,则需要您更改所有案例类别。

    我们可以尝试通过在案例类上使用组合来缓解这种情况:

    case class Common(
        id: String,
        livemode: Boolean,
        created: DateTime,
        createdBy: Account)
    
    case class A(
        common: Common,
        foo: String)
    

    但是,在构造映射器函数时,我们将(在某处)最终不得不转换以下形式的元组:

    (CT_1, CT_2, ... CT_N, ST_1, ST_2, ..., ST_M)
    

    CT 通用类型(已知于CommonFields
    ST 特定类型(已知于AsTable

    收件人:

    (CT_1, CT_2, ... CT_N), (ST_1, ST_2, ..., ST_M)
    

    为了将它们传递给子例程,将CommonA 单独转换为它们的元组。

    我们必须在不知道CT(在AsTable 中实现时)或ST(在CommonFields 中实现时)的数量或确切类型的情况下执行此操作。 Scala 标准库中的元组无法做到这一点。您需要使用HLists 提供的示例shapeless 来执行此操作。

    这是否值得努力值得怀疑。

    基本轮廓可能看起来像这样(不需要所有隐含的混乱)。这段代码不会这样编译。

    trait CommonFields { this: Table[_] =>
      // like before
    
      type ElList = String :: Boolean :: DateTime :: Account :: HNil
    
      protected def toCommon(els: ElList) = Common.apply.tupled(els.tupled)
      protected def fromCommon(c: Common) = HList(Common.unapply(c))
    }
    
    object AsTable extends Table[A] with CommonFields {
      def foo = column[String]("foo", O.NotNull)
    
      def * = common_* ~ foo <> (x => toA(HList(x)), x => fromA(x) tupled)
    
      // convert HList to A
      protected def toA[L <: HList](els: L) = {
        // Values for Common
        val c_els = els.take[Length[ElList]]
        // Values for A
        val a_els = toCommon(c_els) :: els.drop[Length[ElList]]
    
        A.apply.tupled(a_els.tupled)
      }
    
      // convert A to HList
      protected def fromA(a: A) =
        fromCommon(a.common) :: HList(A.unapply(a)).drop[One]
    
    }
    

    使用更多的类型魔法,您可能可以解决最后两个问题:

    1. toAfromA 放入基本特征(通过在特征中使用类型参数,或使用抽象类型成员)
    2. 避免通过使用this techniqueCommon.apply中提取ElList来显式定义它

    【讨论】:

    • 谢谢。我之前确实考虑过这种方法。但是您将如何处理表定义。 Table[(String,Boolean,DateTime,Account,String)] 有什么办法可以避免在更改公共字段时也必须触摸那里?如果我也将对象映射作为要求呢?
    • 快到了。我可以确认这种模式在一定程度上有效。那么案例类呢?假设我的案例类被定义为.... A(foo: Int, B: String, C: String) 为了使用对象映射,公共字段必须出现在其定义中。有没有办法避免这种情况?
    • @RasputinJones 请查看更新后的答案。回答您的最后一条评论:您需要对象中某处的公共字段才能再次在表格中填写它们。
    • >> 这是否值得努力值得怀疑。对。我不得不同意。谢谢你走了多远。我想阅读这些指针,所以请发送过来。赞一个!
    • @RasputinJones 你去。欢迎使用 Scala 的图灵完备类型系统:P
    猜你喜欢
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 2021-04-12
    • 1970-01-01
    相关资源
    最近更新 更多