Slick 3可重用的通用存储库

时间:2015-09-11 11:14:34

标签: scala playframework slick-3.0

我遇到了使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]符合任何值的重载替代值的界限:[E&lt ;: slick.lifted.AbstractTable []] => slick.lifted.TableQuery [E] [E&lt ;: slick.lifted.AbstractTable []](缺点:slick.lifted.Tag => E)slick.lifted.TableQuery [E]

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

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

但是,我们最终会遇到不同的错误:

  

需要类类型但是找到了

在以下地方:

val table = TableQuery[T]

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

2 个答案:

答案 0 :(得分:3)

您必须手动传递表查询,

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

并在实施中,

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

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

答案 1 :(得分:2)

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

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结合起来,你的问题可能会得到解决。

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