Расширение таблиц SLICK сухим способом

У меня есть интересный вопрос о Slick/ Scala, что я надеюсь, что с кем-нибудь из вас могут помочь мои друзья.

У меня есть несколько таблиц и расширение в классах классов SLICK

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

которые разделяют эти общие поля

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

Поскольку эти поля повторяются в каждом классе case, я хотел бы изучить возможность их извлечения в один объект или тип.

Однако при создании объектов таблицы 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)
  ... 
} 

Фактически, конечный результат, который я ищу, - это позволить мне вносить изменения в общие поля, не обновляя каждую таблицу.

Есть ли способ сделать это?

Заранее спасибо

Ответ 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
} 

Единственное, что вам нужно будет повторить сейчас, это тип элементов.

UPDATE

Если вы хотите выполнить сопоставление объектов и:

  • Вы сопоставляете классы case
  • Поля в классах вашего случая находятся в том же порядке

Просто выполните:

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, если ваши поля изменились.

Мы могли бы попытаться смягчить это, используя композицию в классах case:

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)

To:

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

Чтобы передать их подпрограммам, индивидуально преобразующим Common и A в и из их кортежей.

Мы должны сделать это, не зная числа или точные типы либо 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]

}

Используя еще одну магию типа, вы можете решить две последние проблемы:

  • Поместите toA и fromA в базовый признак (используя параметры типа в признаке или используя элементы абстрактного типа)
  • Избегайте определения ElList явно, извлекая его из Common.apply с помощью этой техники