2014-09-20 13 views
5

ich etwas versuchen, dass ich in verschiedenen Formen in verschiedenen Kontexten gesehen habe vor: mit filterById(id: Id) scala Abfrage Erweiterungen erstreckenScala Compiler folgert Nichts für generische Argumente

Dies ist, was ich versucht habe:

trait TableWithId { self: Profile => 

    import profile.simple._ 

    trait HasId[Id] { self: Table[_] => 
     def id: Column[Id] 
    } 

    implicit class HasIdQueryExt[Id: BaseColumnType, U] 
    (query: Query[Table[U] with HasId[Id], U]) { 
     def filterById(id: Id)(implicit s: Session) = query.filter(_.id === id) 
     def insertReturnId(m: U)(implicit s: Session): Id = query.returning(query.map(_.id)) += m 
    } 
} 

Das funktioniert gut, keine echte Magie dort. Aber da es für den Tabellentyp keine Typbeschränkung gibt, verliert jede Abfrage, auf die ich filterById anwende, ihre Spezifität (es ist jetzt ein generischer Table with HasId[Id]), und ich kann ihre Spalten nicht mehr erreichen (außer natürlich _.id).

Ich weiß nicht, wie Sie diese implizite Konvertierung eingeben, so dass dies verhindert wird. Ist es möglich? Die folgende „naieve“ Lösung funktioniert nicht, weil Scala Nichts für den Id-Typen folgert nun:

implicit class HasIdQueryExt[Id: BaseColumnType, U, T <: Table[U] with HasId[Id]] 
(query: Query[T, U]) { ... } 

Ich finde es irgendwie seltsam, dass plötzlich der Id-Typ wie nichts zu entnehmen ist. Wie kann ich dem Compiler mitteilen, wo er nach diesem ID-Typ suchen soll?

+0

Ich bin nicht mit Slick vertraut, aber meine Vermutung ist, dass Ihre Lösung einen generischen Parameter erfordern würde das nimmt den tatsächlichen Typ deiner Tabelle an ... etwas wie 'HasId [Id, T <: HasId [Id, T]]'. Dann können Sie den eigentlichen Typ der Tabelle mit HasId als Parameter T übergeben. Edit: Haben Sie auch versucht, Strukturtypen zu verwenden? http://java.dzone.com/articles/duck-typing-scala-structural Dies wird Reflexion verwenden, aber geben Sie noch statische Sicherheit. –

+0

Ich sehe nicht, wie das anders ist als der Ansatz, den ich zuletzt aufgeschrieben habe. Sind das nicht gleichwertig? Und ich glaube nicht, dass strukturelle Typisierung in diesem Fall hilft. Das Problem behält seine Struktur bei. –

Antwort

0

Dies ist meine Lösung für ein ähnliches Problem. Ich habe zwar bestimmte Art für ID verwenden .:

trait GenericComponent { this: Profile => 
    import profile.simple._ 

    abstract class TableWithId[A](tag:Tag, name:String) extends Table[A](tag:Tag, name) { 
    def id = column[Option[UUID]]("id", O.PrimaryKey) 
    } 

    abstract class genericTable[T <: Table[A] , A] { 
    val table: TableQuery[T] 

    /** 
    * generic methods 
    */ 

    def insert(entry: A)(implicit session:Session): A = session.withTransaction { 
     table += entry 
     entry 
    } 

    def insertAll(entries: List[A])(implicit session:Session) = session.withTransaction { table.insertAll(entries:_*) } 

    def all: List[A] = database.withSession { implicit session => 
     table.list.map(_.asInstanceOf[A]) 
    } 
    } 

    /** 
    * generic queries for any table which has id:Option[UUID] 
    */ 
    abstract class genericTableWithId[T <: TableWithId[A], A <:ObjectWithId ] extends genericTable[T, A] { 

    def forIds(ids:List[UUID]): List[A] = database.withSession { implicit session => 
     ids match { 
     case Nil => Nil 
     case x::xs =>table.filter(_.id inSet(ids)).list.map(_.asInstanceOf[A]) 
     } 
    } 

    def forId(id:UUID):Option[A] = database.withSession { implicit session =>table.filter(_.id === id).firstOption } 

    } 
} 

und dann für Ihre konkrete Komponente:

case class SomeObjectRecord(
    override val id:Option[UUID] = None, 
    name:String) extends ObjectWithId(id){ 
    // your function definitions there 
} 

trait SomeObjectComponent extends GenericComponent { this: Profile => 
    import profile.simple._ 

    class SomeObjects(tag: Tag) extends TableWithId[SomeObjectRecord](tag, "some_objects") { 
    def name = column[String]("name", O.NotNull) 

    def * = (id, name) <> (SomeObjectRecord.tupled, SomeObjectRecord.unapply) 
    def nameIdx = index("u_name", (name), unique = true) 
    } 

    object someobjects extends genericTableWithId[SomeObjects, SomeObjectRecord] { 
    val table = TableQuery[Units] 

    // your additional methods there; you get insert and insertAll from the parent  
    } 
} 
+0

Danke. Aber das beantwortet natürlich meine Frage nicht; Es funktioniert um das Problem herum. Was Sie vorschlagen, ist: Entfernen Sie den generischen Id-Parameter. Außerdem bevorzuge ich QueryExtensions gegenüber Ihrem Companion-Objekt mit Methoden, da Erweiterungen in Queries überall dort wiederverwendet werden können, wo eine Abfrage eine bestimmte Ergebnisform ergibt, während Ihre zusätzlichen Methoden nur an-sich verwendet werden können. –

Verwandte Themen