如何使用Scala Quill.io库编写通用函数

时间:2017-06-27 15:31:21

标签: scala generics quill.io

我正在尝试使用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))

如何修改代码以使其正常工作?

3 个答案:

答案 0 :(得分:2)

正如我在issue中所说@VojtechLetal在他的回答中提到的,你必须使用宏。

我添加了在example Quill project中实现通用插入或更新的代码。

它定义了trait Queriesmixed 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
      })
    """
}