9

我有一个关于 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)
  ... 
} 

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

有没有办法做到这一点?

提前致谢

4

1 回答 1

17

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

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 标准库中的元组无法做到这一点。您需要使用HListsshapeless 提供的示例来执行此操作。

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

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

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. 通过使用此技术ElList从中提取来避免显式定义Common.apply
于 2013-06-21T22:24:44.463 回答