scala - 如何使用 Scala Quill.io 库编写泛型函数

标签 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))

如何修改我的代码以让它工作?

最佳答案

正如我在 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))
  }
}

附言因为 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))
          })
      }
      ()
    """
}

关于scala - 如何使用 Scala Quill.io 库编写泛型函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44784310/

相关文章:

scala - 使用 spark scala 在 solr 中获取数据

java - 继承的一般混淆——Java

c# - 仿函数什么时候应该使用它们它们的预期用途是什么

Swift:引用泛型类

list - 组成所有对的列表

scala - 回调中的 future 在同一线程中运行

scala - 在 Scala Spark 和 PySpark 之间传递 sparkSession

mysql - 在 scala 中编写脚本以连接两个 mysql 表并创建一个对象(quill)

mysql - 无法在 Quill 中使用事务插入一对多关系对象