2013-03-26 4 views
9

Ich habe Klassen/Objekte verschachtelt und möchten sie in einer Datenbank speichern (und abrufen) mit SLICK. Ich verstehe, dass mit SLICK gemappte Projektion der Schlüssel wäre. Außerdem verwende ich ein Begleitobjekt, um zwischen verschachtelten Objekten und einer flachen Struktur (die in der DB-Tabelle gespeichert werden soll) zu mappen. Ich möchte so etwas wie dieses (vereinfachtes Beispiel) tun:gemappte Projektion mit Companion-Objekt in 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 
    } 
} 

Aber die Zusammenstellung nicht mit mehreren Fehlern: „Methode Unapply definiert zweimal“, „mehrdeutig Verweis auf überlasteten Definition, die beiden Verfahren anwenden [...] Übereinstimmung mit dem erwarteten Typ? " und „überladene Methode Wert <> mit Alternativen“

fand ich diese ausgezeichnete Erklärung der abgebildeten Projektion „scala slick method I can not understand so far“ und „Mapped projection with <> to a case class with companion object in Slick“, aber keine der vorgeschlagenen Lösungen arbeitet für mich.

Antwort

19

Statt unapply und apply, könnten Sie einfach in lambdas übergeben, die tun, was Sie wollen:

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

diese Weise die Zuordnung von Tabellenzeilen Klassen Fall bleibt in der Tabellendefinition und die Fallklassen bleib als einfache Fallklassen, was nicht zu schlecht ist.

Der andere Weg wäre gewesen, keinen Fall Klasse für Foo zu verwenden, sondern eine reguläre Klasse statt, die läßt Sie Ihre eigenen apply und unapply in einem Begleitobjekt definieren, etwa so:

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

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

tun wollen Sie werden Fall-Klasse ähnliche Nutzung in ein Ausmaß, aber man könnte die anderen netten Sachen wie mit equals und toString kostenlos als mit dem tatsächlichen Fallklassen verpassen. Ich würde lieber die Fallklassen (und ihre Standard applyunapply), so dass sie als algebraische Datentypen in der normalen Konvention behandelt werden können.

Das eigentliche Problem hier ist, dass Fallklassen ihre eigenen unapply haben, so dass Sie (soweit ich weiß) keine ähnliche Methode (gleicher Name und dieselben Argumente) in Ihrer Begleitklasse haben können. Sie könnten einfach einen anderen Methodennamen verwenden. Nach allem, was wollen Sie nicht semantisch äquivalent zu unapply trotzdem tun:

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

Dann Schema in der Tabelle:

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

Vielen Dank. In der Tat bevorzuge ich Ihre erste Lösung, die sehr gut funktioniert. Aber mit ungefähr einem Dutzend Parametern scheint das gesamte Mapping eine große Rolle zu spielen. Ich verstehe, dass die direkte Einbettung von SLICK kompakter wäre, aber noch keine Einfügungen erlaubt. Ich freue mich zu sehen, wie SLICK in dieser Hinsicht voranschreiten wird. – jans

+1

Selbst wenn Sie "apply" und "apply" haben könnten, so wie Sie es in Ihrer Frage beschreiben, müssten Sie trotzdem mit Ihren Dutzenden von Parametern umgehen, oder? FWIW, schau dir meinen letzten Schnitt an. – Faiz

+1

Mit einer Fallklasse und Ihrer letzten Bearbeitung erhalte ich einen "mehrdeutigen Verweis auf überladene Definition" -Fehler für die Methode "anwenden". Daher musste ich die Methode im Begleitobjekt in 'fromRow' umbenennen. – jans