Slick 3 可重用通用存储库
Slick 3 reusable generic repository
我在以通用方式使用 Slick 的 TableQuery 时遇到问题。
观察常规情况:
class AccountRepository {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
val accounts = TableQuery[Accounts]
def all = db.run(accounts.result)
...
我们的想法是将所有可能的内容提取到通用特征或抽象中 class 以避免重复。为了简单起见,我只包含了有问题的代码。
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] {
但是,这会产生编译错误:
type arguments [T] conform to the bounds of none of the overloaded alternatives of value apply: [E <: slick.lifted.AbstractTable[]]=> slick.lifted.TableQuery[E] [E <: slick.lifted.AbstractTable[]](cons: slick.lifted.Tag => E)slick.lifted.TableQuery[E]
尝试通过设置边界来解决问题也无济于事。
abstract class GenericRepository[T <: slick.lifted.AbstractTable[T]] extends HasDatabaseConfig[JdbcProfile] {
但是,我们最终遇到了不同的错误:
class type required but T found
在以下位置:
val table = TableQuery[T]
知道解决方案吗?
我猜如果你能解决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结合使用,您的问题可能会得到解决。
总之,希望对你有所帮助。
您必须手动传递 table 查询,
abstract class GenericRepository[T <: slick.lifted.AbstractTable[_]](query: TableQuery[T])
并在实施中,
class AccountRepository extends GenericRepository[Accounts](TableQuery[Accounts])
希望这能解决您的问题。
我在以通用方式使用 Slick 的 TableQuery 时遇到问题。
观察常规情况:
class AccountRepository {
override protected val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
val accounts = TableQuery[Accounts]
def all = db.run(accounts.result)
...
我们的想法是将所有可能的内容提取到通用特征或抽象中 class 以避免重复。为了简单起见,我只包含了有问题的代码。
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] {
但是,这会产生编译错误:
type arguments [T] conform to the bounds of none of the overloaded alternatives of value apply: [E <: slick.lifted.AbstractTable[]]=> slick.lifted.TableQuery[E] [E <: slick.lifted.AbstractTable[]](cons: slick.lifted.Tag => E)slick.lifted.TableQuery[E]
尝试通过设置边界来解决问题也无济于事。
abstract class GenericRepository[T <: slick.lifted.AbstractTable[T]] extends HasDatabaseConfig[JdbcProfile] {
但是,我们最终遇到了不同的错误:
class type required but T found
在以下位置:
val table = TableQuery[T]
知道解决方案吗?
我猜如果你能解决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结合使用,您的问题可能会得到解决。
总之,希望对你有所帮助。
您必须手动传递 table 查询,
abstract class GenericRepository[T <: slick.lifted.AbstractTable[_]](query: TableQuery[T])
并在实施中,
class AccountRepository extends GenericRepository[Accounts](TableQuery[Accounts])
希望这能解决您的问题。