我正在尝试使用Quill.io库在数据库上运行Scala中实现泛型方法。类型T
将只是与Quill.io一起使用的案例类。
def insertOrUpdate[T](inserting: T, equality: (T,T) => Boolean)(implicit ctx: Db.Context): Unit = {
import ctx._
val existingQuery = quote {
query[T].filter { dbElement: T =>
equality(dbElement, inserting)
}
}
val updateQuery = quote {
query[T].filter { dbElement =>
equality(dbElement, lift(inserting))
}.update(lift(inserting))
}
val insertQuery = quote { query[T].insert(lift(inserting)) }
val existing = ctx.run(existingQuery)
existing.size match {
case 1 => ctx.run(updateQuery)
case _ => ctx.run(insertQuery)
}
}
但我得到两种类型的编译错误
Error:(119, 12) Can't find an implicit `SchemaMeta` for type `T`
query[T].filter { dbElement: T =>
Error:(125, 33) Can't find Encoder for type 'T'
equality(dbElement, lift(inserting))
如何修改代码以使其正常工作?
答案 0 :(得分:2)
正如我在issue中所说@VojtechLetal在他的回答中提到的,你必须使用宏。
我添加了在example Quill project中实现通用插入或更新的代码。
它定义了trait Queries
的mixed into context:
trait Queries {
this: JdbcContext[_, _] =>
def insertOrUpdate[T](entity: T, filter: (T) => Boolean): Unit = macro InsertOrUpdateMacro.insertOrUpdate[T]
}
此特征使用macro来实现您的代码并进行微小更改:
import scala.reflect.macros.whitebox.{Context => MacroContext}
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
val updateQuery = ${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}
val insertQuery = quote {
query[$t].insert(lift($entity))
}
run(${c.prefix}.query[$t].filter($filter)).size match {
case 1 => run(updateQuery)
case _ => run(insertQuery)
}
()
"""
}
用法examples:
import io.getquill.{PostgresJdbcContext, SnakeCase}
package object genericInsertOrUpdate {
val ctx = new PostgresJdbcContext[SnakeCase]("jdbc.postgres") with Queries
def example1(): Unit = {
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == "")
}
def example2(): Unit = {
import ctx._
val inserting = Person(1, "")
ctx.insertOrUpdate(inserting, (p: Person) => p.name == lift(inserting.name))
}
}
P.S。由于update()
返回更新记录的数量,因此您的代码可以简化为:
class InsertOrUpdateMacro(val c: MacroContext) {
import c.universe._
def insertOrUpdate[T](entity: Tree, filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
if (run(${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}) == 0) {
run(quote {
query[$t].insert(lift($entity))
})
}
()
"""
}
答案 1 :(得分:0)
正如其中一位主播贡献者所说:issue:
如果您想使解决方案通用,则必须使用宏,因为Quill会在编译时生成查询,并且
T
类型必须在那个时候。
TL; DR 以下也不起作用,只是玩
无论如何......出于好奇,我试图通过遵循你提到的错误来解决问题。我将函数的定义更改为:
def insertOrUpdate[T: ctx.Encoder : ctx.SchemaMeta](...)
产生以下日志
[info] PopulateAnomalyResultsTable.scala:71: Dynamic query
[info] case _ => ctx.run(insertQuery)
[info]
[error] PopulateAnomalyResultsTable.scala:68: exception during macro expansion:
[error] scala.reflect.macros.TypecheckException: Found the embedded 'T', but it is not a case class
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:34)
[error] at scala.reflect.macros.contexts.Typers$$anonfun$typecheck$2$$anonfun$apply$1.apply(Typers.scala:28)
它开始很有希望,因为quill明显放弃了静态编译并使查询动态化。我检查了失败的macro的源代码,似乎quill正在尝试获取T
的构造函数,这在当前上下文中是未知的。
答案 2 :(得分:0)
有关更多详细信息,请参见我的答案Generic macro with quill或实现: AllAsyncMacro:
package io.getquill.example.genericAllAsync
import scala.reflect.macros.whitebox.{Context => MacroContext}
class AllAsyncMacro(val c: MacroContext) {
import c.universe._
def all[T](tblName: Tree)(ex: Tree)(t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
run(quote {
query[$t]
})
"""
def insertOrUpdate[T](entity: Tree, filter: Tree)(ex: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
if (run(${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).update(lift($entity))
}) == 0) {
run(quote {
query[$t].insert(lift($entity))
})
}
()
"""
def create[T](entity: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
run(quote {
query[$t].insert(lift($entity))
})
"""
def merge[T](entity: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
run(${c.prefix}.quote {
${c.prefix}.query[$t].update(lift($entity))
})
"""
def deleteByFilter[T](filter: Tree)(implicit t: WeakTypeTag[T]): Tree =
q"""
import ${c.prefix}._
run(${c.prefix}.quote {
${c.prefix}.query[$t].filter($filter).delete
})
"""
}