2016-11-27 3 views
0

생산성을 높이기 위해 최신 Slick 3.1.1을 사용하여 제네릭 DAO를 구현하는 방법에 대한 다양한 가능성을 모색 중이며 TableQuery에있는 내 Play 웹 응용 프로그램의 서비스 계층을 기반으로하면 상용구 코드가 많아지기 때문에 필요합니다. . 내 일반적인 DAO 구현에서 기능을 사용하려는 방법 중 하나는 findByExample이며 Criteria API을 사용하여 JPA에서 가능합니다. 제 경우에는 Slick Code Generator을 사용하여 SQL 스크립트에서 모델 클래스를 생성합니다.Slick : 예를 들어 findByExample을 일반적으로 구현하는 방법은 무엇입니까?

import scala.reflect.runtime.universe._ 

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

findByExample에 대한 초안 구현은 다음과 같습니다 :

는 동적으로 Scala. Get field names list from case class에서 가져온 속성 이름에 액세스 할 수 있도록 다음 필요

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 
} 

하지만이 작동하지 않습니다 왜냐하면 나는 스칼라 쿵후가 더 필요하기 때문이다. T은 엔티티 테이블 유형이고 R은 사례 클래스로 생성 된 행 유형이므로 유효한 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") 

하지만 분명히 filter 기능을 사용하는 동안 e는 그런 가능성이 없습니까? 아마

답변

0

후.

거기 그들은 모든 모델 엔터티 형식에 대해 작동하는 일반 filter 메서드를 선언하고 구현하므로 더 많은 JPA findByExample에 대한 올바른 기능적 대체품으로 간주됩니다.

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 

여기에 설명 : 더 다음 블로그 게시물 Repository Pattern/Generic DAO Implementation을 발견 연구 할 http://slick.lightbend.com/doc/3.1.1/code-generation.html#customization