2014-09-20 138 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