1

我在以通用方式使用 Slick 的 TableQuery 时遇到问题。

观察常规情况:

class AccountRepository {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
val accounts = TableQuery[Accounts]
def all = db.run(accounts.result)
...

这个想法是将所有可能的东西提取到通用特征或抽象类中以避免重复。为了简单起见,我只包含有问题的代码。

abstract class GenericRepository[T] extends HasDatabaseConfig[JdbcProfile] {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile(Play.current)
val table = TableQuery[T]
}

并像这样使用它:

class AccountRepository extends GenericRepository[Accounts] {

但是,这会产生编译错误:

类型参数 [T] 不符合 value apply 的任何重载替代项的边界: [E <: slick.lifted.AbstractTable[ ]]=> slick.lifted.TableQuery[E] [E <: slick.lifted.AbstractTable [ ]](缺点:slick.lifted.Tag => E)slick.lifted.TableQuery[E]

尝试通过设置边界来解决问题也无济于事。

abstract class GenericRepository[T <: slick.lifted.AbstractTable[T]] extends HasDatabaseConfig[JdbcProfile] {

然而,我们最终得到一个不同的错误:

需要类类型但找到 T

在以下地方:

val table = TableQuery[T]

关于解决方案的任何想法?

4

2 回答 2

2

您必须手动传递表查询,

 abstract class GenericRepository[T <: slick.lifted.AbstractTable[_]](query: TableQuery[T])

并在实施中,

class AccountRepository extends GenericRepository[Accounts](TableQuery[Accounts])

我希望这能解决你的问题。

于 2015-09-11T13:08:05.200 回答
1

我想如果你能解决 tableQuery 的初始化,那么你可以继续你的 GenericRepository。我正在使用带有 PostgreSQL 的 Slick 3.0。

在 中slick.lifted.TableQuery,有如下方法

// object TableQuery
def apply[E <: AbstractTable[_]](cons: Tag => E): TableQuery[E] =
    new TableQuery[E](cons)

因此,如果我们可以instance of E即时获得,那么我们可以获得创建 TableQuery 的通用方法。所以反射似乎是一种可能的解决方法。

 import scala.reflect.runtime.{ universe => ru }
 import slick.lifted.{ AbstractTable, ProvenShape, Tag }
 import slick.driver.PostgresDriver.api._


  object Reflection {
    val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)

    def getTypeTag[T: ru.TypeTag] = ru.typeTag[T]

    def createClassByConstructor[T: ru.TypeTag](args: Any*) =
      runtimeMirror.reflectClass(getTypeTag[T].tpe.typeSymbol.asClass)  
       .reflectConstructor(ru.typeOf[T].declaration(ru.nme.CONSTRUCTOR)
       .asMethod)(args: _*).asInstanceOf[T]
  }


  // context bound here is for createClassByConstructor to use
  abstract class GenericTableQuery[U, T <: AbstractTable[U]: ru.TypeTag] {

    import Reflection._

    // look at following code: Students, if you want to initialize Students
    // you're gonna need a tag parameter, that's why we pass tag here
    val tableQuery = TableQuery.apply(tag => createClassByConstructor[T](tag))

  }

 // Sample Table
 case class Student(name: String, age: Int)
 class Students(tag: Tag) extends Table[Student](tag, "students") {
    def name = column[String]("name")
    def age = column[Int]("age")
    override def * : ProvenShape[Student] = (name, age) 
      <> (Student.tupled, Student.unapply _)
 }

 // get TableQuery
 object TestGenericTableQuery extends GenericTableQuery[Student, Students] {
    val studentQuery = tableQuery
 }

上面提到的代码只是针对泛型 TableQuery 的问题,尝试将它与您的 GenericRepository 结合起来,您的问题可能会得到解决。

无论如何,希望它有所帮助。

于 2015-09-11T12:57:00.667 回答