2013-03-26 4 views
9

Ho classi/oggetti nidificati e voglio memorizzarli (e recuperarli) in un database usando SLICK. Capisco che con la proiezione mappata SLICK sarebbe la chiave. Inoltre, utilizzo un oggetto companion per mappare tra oggetti nidificati e struttura piatta (da memorizzare nella tabella DB). Voglio fare qualcosa di simile (esempio semplificato):proiezione mappata con oggetto companion 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 
    } 
} 

Ma la compilazione fallisce con diversi errori: "Metodo unapply è definito due volte", "riferimento ambiguo per definizione sovraccarico, sia di metodo si applica [...] corrisponde al tipo previsto? " e "Valore metodo di overload <> con alternative"

ho trovato questa spiegazione eccellente di proiezione mappati "scala slick method I can not understand so far" e "Mapped projection with <> to a case class with companion object in Slick", ma nessuna delle soluzioni proposte funziona per me.

risposta

19

Invece di unapply e apply, si può solo passare lambda che fare quello che vuoi:

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

In questo modo, la mappatura da righe della tabella a caso le classi rimane nella definizione della tabella, e le classi case rimanere come semplici case classes, che non è male.

L'altro modo sarebbe stato di non utilizzare una classe case per Foo, ma una classe normale, invece, che vi lascia liberi di definire la propria apply e unapply in un oggetto associato, in questo modo:

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

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

otterrete utilizzo caso di classe simile in una certa misura, ma si potrebbe perdere l'altra roba bella come avere equals e toString gratuitamente come con le classi caso reale. Preferisco mantenere le classi dei casi (e il loro valore predefinito applyunapply) in modo che possano essere trattati come tipi di dati algebrici nella convenzione normale.

Il vero problema qui è che le classi del caso hanno il proprio unapply quindi non è possibile (per quanto ne so) avere un metodo simile (stesso nome e stessi argomenti) nella classe compagno. Potresti semplicemente usare un altro nome di metodo. Dopo tutto, ciò che si vuole fare non è semanticamente equivalenti a unapply comunque:

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

Poi nello schema della tabella:

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

Grazie mille. Anzi preferisco la tua prima soluzione che funziona molto bene. Ma con circa una dozzina di parametri, l'intera mappatura sembra essere un grosso problema. Capisco che l'incorporamento diretto di SLICK sarebbe più compatto ma non consente ancora gli inserimenti. Non vedo l'ora di vedere come SLICK progredirà al riguardo. – jans

+1

Anche se tu fossi in grado di applicare 'apply' e' unpply' come descrivi nella tua domanda, dovresti comunque avere a che fare con le tue dozzine di parametri, giusto? FWIW, guarda la mia ultima modifica. – Faiz

+1

Con una classe case e l'ultima modifica ottengo un errore "riferimento ambiguo alla definizione overload" per il metodo 'apply'. Quindi, ho dovuto rinominare il metodo in oggetto companion su 'fromRow'. – jans