2014-09-20 141 views
5

我想要的東西,我已經看到了在不同環境下不同的形狀之前沒有: 延長Scala的查詢擴展與filterById(id: Id)Scala編譯器推斷泛型參數

這是我已經試過:

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

這工作正常,沒有真正的魔法。但是因爲Table類型沒有類型約束,所以我應用filterById的任何查詢都失去了特定性(現在是一個通用的Table with HasId[Id]),我無法再訪問它的列(除了_.id ofcourse)。

我不知道如何輸入這種隱式轉換,以防止這種情況發生。可能嗎?下面的「naieve」的解決方案不起作用,因爲Scala現在的ID類型推斷沒有:

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

我覺得有種奇怪的是,突然ID類型推斷爲無。如何提示編譯器在哪裏查找該Id類型?

+0

我不熟悉的油滑,但我的猜測是,您的解決方案將需要一個泛型參數它採用你的表的實際類型......像'HasId [Id,T <:HasId [Id,T]]'。然後你可以用HasId作爲參數T傳遞表的實際類型。編輯:另外,你有沒有嘗試過使用結構類型? http://java.dzone.com/articles/duck-typing-scala-structural這將使用反射,但仍然給你靜態安全。 – 2014-09-20 16:08:21

+0

我不明白這與我上次寫下的方法有什麼不同。這些不相等嗎?在這種情況下,我認爲結構打字不會有幫助。問題保留了它的結構。 – 2014-09-22 08:39:01

回答

0

這是我針對類似問題的解決方案。我雖然沒有使用特定類型的ID:

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 } 

    } 
} 

,然後你的混凝土構件:

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

謝謝。但是這並不能回答我的問題。它可以解決這個問題。因爲你建議的是:刪除Id泛型參數。另外,我更喜歡使用方法的伴侶對象上的QueryExtensions,因爲查詢上的擴展可以在查詢給出某個結果形狀的任何地方重用,而您的其他方法只能用於an-sich。 – 2014-10-02 11:48:48