2013-03-26 4 views
9

У меня есть вложенные классы/объекты и вы хотите сохранить (и получить) их в базе данных с помощью SLICK. Я понимаю, что с SLICK отображаемая проекция была бы ключевой. Кроме того, я использую объект-компаньон для сопоставления между вложенными объектами и плоской структурой (для хранения в таблице БД). Я хочу сделать что-то вроде этого (упрощенного примера):сопоставленная проекция со спутником в SLICK

case class Foo(id: Int, myBar: Bar) 

case class Bar(myInt: Int, myString: String) 

object Foo { 
    def apply(id: Int, myInt: Int, myString: String): Foo = Foo(id, Bar(myInt, myString)) 

    override def unapply(f: Foo) = (f.id, f.myBar.myInt, f.myBar.myString) 
} 

object TTable extends Table[Foo]("FOO") { 
    def id = column[Int]("id", O.PrimaryKey) 
    def myInt = column[Int]("myInt", O NotNull) 
    def myString = column[String]("myString", O NotNull) 

    def * = id ~ myInt ~ myString <> (Foo.apply _, Foo.unapply _) 

    def query(db: Database, id: Int): Option[Foo] = db withSession { //s: Session => 
     (for { b <- TTable if b.id is id} yield b).firstOption 
    } 
} 

Но компиляция завершается с несколькими ошибками: «метод определяется исключить его дважды», «неоднозначную ссылку на перегруженное определение, как метод применяется [...] соответствовать ожидаемому типу? " и «перегружено значение методы <> с альтернативами»

Я нашел это превосходное объяснение отображенной проекции «scala slick method I can not understand so far» и «Mapped projection with <> to a case class with companion object in Slick», но ни один из предложенных решений не работает для меня.

ответ

19

Вместо unapply и apply, вы могли бы просто передать лямбды, что делать то, что вы хотите:

def * = id ~ myInt ~ myString <> (
    (id,myInt,myString) => Foo(id, Bar(myInt, myString)), /* from a row to a Foo */ 
    (f:Foo) => Some((f.id, f.myBar.myInt, f.myBar.myString)) /* and back */) 

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

Другой путь был бы не использовать класс случай для Foo, но обычный класс, а не который дает вам свободу, чтобы определить ваш собственный apply и unapply в объекте компаньона, например, так:

// untested code 
class Foo private (val id: Int, val myBar: Bar) 
case class Bar(myInt: Int, myString: String) 
object Foo { 
    def apply(id: Int, myInt: Int, myString: String): Foo = new Foo(id, Bar(myInt, myString)) 
    def unapply(f: Foo) = Some((f.id, f.myBar.myInt, f.myBar.myString)) 
} 

Если вы хотите сделать def * = id ~ myInt ~ myString <> (Foo.apply _, Foo.unapply _)

вы получите случай-класса, как использование в определенной степени, но вы можете пропустить другой хороший материал как с equals и toString бесплатно как с реальными классами дела. Я бы предпочел сохранить классы case (и их по умолчанию applyunapply), поэтому они могут рассматриваться как алгебраические типы данных в обычном соглашении.

Настоящая проблема заключается в том, что классы case имеют свои собственные unapply, поэтому вы не можете (насколько мне известно) иметь аналогичный метод (одно и то же имя и те же аргументы) в вашем классе-компаньоне. Вы можете просто просто использовать другое имя метода. В конце концов, что вы хотите сделать, это не семантически эквивалентно unapply все равно:

object Foo { 
    def fromRow(id: Int, myInt: Int, myString: String): Foo = Foo(id, Bar(myInt, myString)) 
    def toRow(f: Foo) = Some((f.id, f.myBar.myInt, f.myBar.myString)) 
} 

Затем в таблице схеме:

def * = id ~ myInt ~ myString <> (Foo.fromRow _, Foo.toRow _) 
+1

Спасибо большое. Действительно, я предпочитаю ваше первое решение, которое работает очень хорошо. Но с примерно дюжиной параметров все сопоставление, похоже, много шаблонов. Я понимаю, что Direct Embedding от SLICK будет более компактным, но пока не позволяет вставлять. Я с нетерпением жду, как SLICK будет развиваться в этом отношении. – jans

+1

Даже если бы вы смогли «применить» и «не использовать», как они описывают ваш вопрос, вам все равно придется иметь дело со своими десятками параметров в любом случае? FWIW, посмотрите мое последнее изменение. – Faiz

+1

С классом case и вашим последним правлением я получаю сообщение об ошибке «неоднозначная ссылка на перегруженное определение» для метода «apply». Следовательно, мне пришлось переименовать метод в сопутствующем объекте в 'fromRow'. – jans

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