Slick:如何实现find by example,即findByExample一般?

时间:2016-11-27 14:14:17

标签: scala slick slick-3.0 play-slick

我正在探索如何使用最新的Slick 3.1.1来实现通用DAO以提高工作效率的不同可能性,是的,因为我的Play Web应用程序的服务层基于{{1}单独导致很多样板代码。我希望在我的通用DAO实现中使用的方法之一是TableQuery,在Criteria API的帮助下可以在JPA中使用。就我而言,我正在使用Slick Code Generator从sql脚本生成模型类。

我需要以下内容才能动态访问取自Scala. Get field names list from case class

的属性名称
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 Kungfu。 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是作为案例类生成的行类型,因此是有效的Scala R类型。

该代码中的第一个问题是效率太低,因为而不是做例如。

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") 函数时没有这种可能性?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

经过进一步研究后发现了以下博文preview

他们声明并实现了适用于任何模型实体类型的通用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)