Slick 3 многоразового универсального хранилища

У меня возникают проблемы с использованием 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] не соответствуют границам ни одной из перегруженных альтернатив значения: 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] {

Однако в результате мы получаем другую ошибку:

тип класса требуется, но T найдено

в следующем месте:

val table = TableQuery[T]

Есть идеи по поводу решения?

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, и ваша проблема может быть решена.

В любом случае, надеюсь, это поможет.

Вы должны передать запрос таблицы вручную,

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

и в реализации,

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

Я надеюсь, что это решит вашу проблему.

Другие вопросы по тегам