In Slick 2, we can map tables like this:
case class Cooler(id: Option[Int], minTemp: Option[Double], maxTemp: Option[Double])
/**
* Define table "cooler".
*/
class Coolers(tag: Tag) extends Table[Cooler](tag, "cooler") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def minTemp = column[Double]("min_temp", O.Nullable)
def maxTemp = column[Double]("max_temp", O.Nullable)
def * = (id.?, minTemp.?, maxTemp.?) <> (Cooler.tupled, Cooler.unapply _)
}
object Coolers {
val tableQuery = TableQuery[Coolers]
}
because I have a lot of tables, I want to define generic methods for them, like find
, delete
, update
so I have to define these methods in a super class from where to extend my objects (object Coolers extends TableUtils[Coolers, Cooler]
). In order to define those methods, I need tableQuery
to move out of my object in this super class, so I tried it like:
abstract class TableUtils[T <: Table[A] , A] {
val tableQuery = TableQuery[T]
}
but I receive an error on tableQuery
definition:
class type required but T found
Does anybody know what I am doing wrong?
Here is one solution:
At first, define this to avoid class type issue..
Then use it this way, Post is sub class of Table:
This solution tested ok in slick 3.x, and Play slick 1.x, since the slick 2.0 Query.scala comply to slick 3.0 Query.scala, this might work at 2 too.
When you do
TableQuery[T]
you are in fact callingTableQuery.apply
, which is actually a macro.The body of this macro tries to instantiate
T
, but in your case T has become an (unknown) type parameter that the compiler does not know how to instantiate. The problem is similar to trying to compile this:The net effect is that
TableQuery.apply
can only be used on concrete types.You could work around that using a type class to capture the call to
TableQuery.apply
(at the point where the concrete type is known) along with an implicit macro to provide an instance of this type class. Then you would have something like:Where
TableQueryBuilder
is the type class andBuildTableQuery
is an alternate version ofTableQuery.apply
that will forward to theTableQueryBuilder
instance to perform the actual instantiation.I've added an implementation as part of another answer here.
It will be much easier (if less convenient) to just declare
tableQuery
as an abstract value and define it in every concrete derived class ofTableUtils
: