2013-03-26 14 views
9

Mam zagnieżdżone klasy/obiekty i chcę je przechowywać (i pobierać) w bazie danych za pomocą SLICK. Rozumiem, że przy rzutowaniu SLICK odwzorowanie będzie kluczowe. Ponadto używam obiektu towarzyszącego do mapowania pomiędzy obiektami zagnieżdżonymi a strukturą płaską (przechowywaną w tabeli DB). Chcę zrobić coś takiego (uproszczony przykład):odwzorowana projekcja z obiektem towarzyszącym w 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 
    } 
} 

Ale kompilacja nie powiedzie się z wieloma błędami: „metoda Wycofywanie jest zdefiniowane dwa razy”, „niejednoznaczne odniesienie do przeciążonej definicji, stosuje się zarówno metody [...] dopasować oczekiwany typ? " i „wartość przeciążona metoda <> z alternatyw”

Znalazłem to doskonałe wyjaśnienie odwzorowanym projekcji „scala slick method I can not understand so far” i „Mapped projection with <> to a case class with companion object in Slick”, ale żadne z proponowanych rozwiązań dla mnie pracować.

Odpowiedz

19

Zamiast unapply i apply, można po prostu przejść w lambdy, że to, co chcesz:

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 */) 

ten sposób odwzorowanie z wierszy tabeli do przypadku zajęcia pozostaje w definicji tabeli, a zajęcia case pozostańcie jak proste przypadki, co nie jest złe.

Innym sposobem byłoby nie wykorzystać klasę case dla Foo, ale regularne klasa zamiast które pozwala użytkownikowi zdefiniować własne apply i unapply w obiekcie towarzysz, tak:

// 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)) 
} 

Jeśli chcesz zrobić def * = id ~ myInt ~ myString <> (Foo.apply _, Foo.unapply _)

Dostaniesz case-klasy jak wykorzystanie do pewnego stopnia, ale można przegapić inne miłe rzeczy jakby equals i toString za darmo jako rzeczywistych klas przypadków. Wolałbym zachować klasy przypadków (i ich domyślne applyunapply), aby mogły być traktowane jako algebraiczne typy danych w normalnej konwencji.

Prawdziwym problemem jest to, że klasy przypadków mają własne unapply, więc nie możesz (o ile mi wiadomo) mieć podobną metodę (ta sama nazwa i te same argumenty) w twojej klasie towarzyszącej. Można po prostu użyć innej nazwy metody. Po tym wszystkim, co chcesz zrobić, to nie semantycznie równoważna unapply anyway:

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)) 
} 

Następnie w schemacie tabeli:

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

dzięki. Rzeczywiście wolę twoje pierwsze rozwiązanie, które działa bardzo dobrze. Ale przy kilkunastu parametrach całe mapowanie wydaje się być dużą liczbą elementów. Rozumiem, że Direct Embedding SLICKa jest bardziej kompaktowy, ale nie pozwala jeszcze na wstawianie. Nie mogę się doczekać, aby zobaczyć postępy SLICK w tym zakresie. – jans

+1

Nawet gdybyś był w stanie "zastosować" i "anulować", tak jak opisałeś to w swoim pytaniu, czy mimo to masz do czynienia z dziesiątkami parametrów? FWIW, spójrz na moją ostatnią edycję. – Faiz

+1

Z klasą przypadku i twoją ostatnią edycją otrzymuję "niejednoznaczne odniesienie do przeciążonej definicji" błąd dla metody "zastosuj". W związku z tym musiałem zmienić nazwę metody w obiekcie towarzyszącym na "odRow". – jans

Powiązane problemy