2013-06-21 5 views
10

У меня есть интересный вопрос вокруг 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) 
    ... 
} 

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

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

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

ответ

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 
} 

Единственное, что вы» Теперь нужно повторить, это тип элементов.

UPDATE

Если вы хотите сделать объект-отображение и:

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

Мы могли бы попытаться смягчить это с помощью состава на случай классов:

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] 

} 

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

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

Спасибо. Раньше я думал об этом подходе. Но как бы вы справились с определением таблицы. Таблица [(String, Boolean, DateTime, Account, String)] Есть ли способ избежать прикосновения к ней при изменении общих полей? Что делать, если я также привязываю отображение как требование? –

+0

Практически там. Я могу подтвердить, что этот шаблон работает до некоторой степени. Итак, как насчет классов случаев? Предполагая, что мой класс case определен как ... A (foo: Int, B: String, C: String) Для использования сопоставления объектов в его определении должны присутствовать общие поля. Есть ли способ избежать этого? –

+0

@RasputinJones, пожалуйста, см. Обновленный ответ. Отвечая на ваш последний комментарий: вам нужны общие поля где-то в объекте, чтобы снова заполнить их в таблице. – gzm0

Смежные вопросы