2016-11-27 10 views
0

Я изучаю различные возможности по внедрению общего DAO с использованием новейшего Slick 3.1.1 для повышения производительности и да, это необходимо для него, потому что базовый уровень сервиса моего приложения Play Web на TableQuery приводит только к большому шаблону кода , Одним из методов, которые я хотел бы включить в мою общую реализацию DAO, является findByExample, возможный в JPA с помощью Criteria API. В моем случае я использую Slick Code Generator для генерации классов модели из SQL-скрипта.Слайк: как реализовать поиск по примеру i.e. findByExample в общем?

мне нужно следующее, чтобы иметь возможность динамического доступа имена атрибутов, взятые из Scala. Get field names list from case class:

import scala.reflect.runtime.universe._ 

def classAccessors[T: TypeTag]: List[MethodSymbol] = typeOf[T].members.collect { 
    case m: MethodSymbol if m.isCaseAccessor => m 
}.toList 

Проект реализации для findByExample будет:

def findByExample[T, R](example: R) : Future[Seq[R]] = { 
    var qt = TableQuery[T].result 
    val accessors = classAccessors[R] 
    (0 until example.productArity).map { i => 
    example.productElement(i) match { 
     case None => // ignore 
     case 0 => // ignore 
     // ... some more default values => // ignore 
     // handle a populated case 
     case Some(x) => { 
     val columnName = accessors(i) 
     qt = qt.filter(_.columnByName(columnName) == x) 
     } 
    } 
    } 
    qt.result 
} 

Но это не работает потому что мне нужно лучше Scala Kungfu. T - тип таблицы сущностей, а R - тип строки, который генерируется как класс case и, следовательно, действительный тип Scala Product.

Первой проблемой в этом коде является то, что было бы слишком неэффективно, потому что вместо того, чтобы делать, например,

qt.filter(_.firstName === "Juan" && _.streetName === "Rosedale Ave." && _.streetNumber === 5) 

делает:

// find all 
var qt = TableQuery[T].result 
// then filter by each column at the time 
qt = qt.filter(_.firstName === "Juan") 
qt = qt.filter(_.streetName === "Rosedale Ave.") 
qt = qt.filter(_.streetNumber === 5) 

Во-вторых, я не могу видеть, как динамически получать доступ к имени столбца в методе фильтра т.е.

qt.filter(_.firstName == "Juan") 

мне нужно вместо этого

qt.filter(_.columnByName("firstName") == "Juan") 

, но, по-видимому, e нет такой возможности при использовании функции filter?

ответ

0

После дальнейшего исследующих нашел следующий блог Repository Pattern/Generic DAO Implementation.

Там они объявляют и реализуют общий метод filter, который работает для любого типа модели Entity, и поэтому на мой взгляд действительная функциональная замена для более JPA findByExample.

i.e.

T <: Table[E] with IdentifyableTable[PK] 
E <: Entity[PK] 
PK: BaseColumnType 

def filter[C <: Rep[_]](expr: T => C)(implicit wt: CanBeQueryCondition[C]) : Query[T, E, Seq] = tableQuery.filter(expr) 
1

Вероятно, лучшие способы реализации фильтров и сортировки по динамически предоставленных имен столбцов будет либо простой SQL или расширения генератор кода для генерации методов расширения, что-то вроде этого:

implicit class DynamicPersonQueries[C[_]](q: Query[PersonTable, PersonRow, C]){ 
    def dynamicFilter(column: String, value: String) = column { 
    case "firstName" => q.filter(_.firstName === value) 
    case "streetNumber" => q.filter(_.streetNumber === value.toInt) 
    ... 
    } 
} 

Вы, возможно, придется возиться с типы немного, чтобы заставить его скомпилировать (и в идеале обновить этот пост потом :)).

Вы можете фильтровать все предоставленные ценности, как это:

val examples: Map[String, String] = ... 
val t = TableQuery[PersonTable] 
val query = examples.foldLeft(t){case (t,(column, value)) => t.dynamicFilter(column, value) 
query.result 

Расширение генератора кода объясняется здесь: http://slick.lightbend.com/doc/3.1.1/code-generation.html#customization