9

Slick/Scala に関して興味深い質問があります。あなたの素敵なチャップの 1 人が私を助けてくれることを願っています。

私はいくつかのテーブルを持っており、拡張により 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)

それらをサブルーチンに個別に変換し、タプルとの間で渡すCommonためA

CT(で実装する場合AsTable) またはST(で実装する場合)の数または正確なタイプを知らなくても、これを行う必要がありますCommonFieldsScala 標準ライブラリのタプルではそれができませんこれを行うには、たとえば shapelessHListsによって提供されるasを使用する必要があります。

これが努力する価値があるかどうかは疑問です。

基本的なアウトラインは次のようになります (必要となる暗黙の混乱はすべて除きます)。このコードはこのようにはコンパイルされません。

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]

}

型マジックをさらに使用すると、最後の 2 つの問題を解決できる可能性があります。

  1. toAandをベーストレイトに入れfromAます (トレイトで型パラメーターを使用するか、抽象型メンバーを使用します)。
  2. この手法を使用しElListてから抽出して明示的に定義することは避けてくださいCommon.apply
于 2013-06-21T22:24:44.463 に答える