【问题标题】:Generic Macros with Quill带有 Quill 的通用宏
【发布时间】:2018-11-06 21:39:34
【问题描述】:

您好,我一直在尝试使用宏和 Quill 创建一些通用函数。

这是我对宏的实现:

class Impl(val c: Context) {
  import c.universe._
  def all[T](tblName: Tree, ctx: Tree)(implicit t: WeakTypeTag[T]): Tree =
    q"""
      import ${ctx}._
      implicit val schema = schemaMeta[$t](${tblName})
      run(quote {
        query[$t]
      }) 
    """
}

object Macros {
  def all[T](tblName: String, ctx: MysqlAsyncContext[Literal.type]): Future[List[T]] = macro Impl.all[T]
}

我已经尝试在下面的代码中使用它:

case class Language(id: Short, iso639_1: String, name: String) 

object Language {
    val tableName = "Languages"
    def all()(implicit mysql: MysqlAsyncContext[Literal.type], ec: ExecutionContext): Future[List[Language]] =
        Macros.all[Language](tableName, mysql)
}

然后我收到以下错误:

Language.scala:99:25: type mismatch;
[error]  found   : mysql.Quoted[mysql.EntityQuery[Language]]{def quoted: io.getquill.ast.Entity; def ast: io.getquill.ast.Entity; def id1101286793(): Unit; val liftings: Object}
[error]  required: io.getquill.MysqlAsyncContext[io.getquill.Literal.type]#EntityQuery[com.github.pokeboston.libghpagesapi.normalized.Language]
[error]     Macros.all[Language]("Languages", mysql)
[error]                         ^

但是我知道传递给宏的 ctx 确实是 MysqlAsyncContext 因为当我将宏代码更改为:

class Impl(val c: Context) {
  import c.universe._
  def all[T](tblName: Tree, ctx: Tree)(implicit t: WeakTypeTag[T]): Tree =
    q"""
      import ${ctx}._
      implicit val schema = schemaMeta[$t](${tblName})
      $ctx
    """
}

它给了我以下错误:

Language.scala:99:25: type mismatch;
[error]  found   : io.getquill.MysqlAsyncContext[io.getquill.Literal.type]
[error]  required: scala.concurrent.Future[List[Language]]
[error]     Macros.all[Language]("Languages", mysql)
[error]                         ^

我猜我从根本上误解了宏。任何启示将不胜感激!

谢谢!

【问题讨论】:

    标签: scala scala-macros quill.io


    【解决方案1】:

    后面跟着quill-example我在github上创建了项目,见quill-generic

    同步实现示例:

    AbstractCrudMacro.scala

    package pl.jozwik.quillgeneric.quillmacro
    import scala.reflect.macros.whitebox.{ Context => MacroContext }
    
    trait FilterCrudMacro {
      val c: MacroContext
      import c.universe._
      import pl.jozwik.quillgeneric.quillmacro.Keys._
    
      def callFilterOnIdTree[K: c.WeakTypeTag](id: Tree)(dSchema: c.Expr[_]): Tree =
        callFilterOnId[K](c.Expr[K](q"$id"))(dSchema)
    
      protected def callFilterOnId[K: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
        val t = weakTypeOf[K]
    
        t.baseClasses.find(c => compositeSet.contains(c.asClass.fullName)) match {
          case None =>
            q"$dSchema.filter(_.id == lift($id))"
          case Some(base) =>
            val query = q"$dSchema.filter(_.id.fk1 == lift($id.fk1)).filter(_.id.fk2 == lift($id.fk2))"
            base.fullName match {
              case `compositeKey4Name` =>
                q"$query.filter(_.id.fk3 == lift($id.fk3)).filter(_.id.fk4 == lift($id.fk4))"
              case `compositeKey3Name` =>
                q"$query.filter(_.id.fk3 == lift($id.fk3))"
              case `compositeKey2Name` =>
                query
              case x =>
                c.abort(NoPosition, s"$x not supported")
    
            }
        }
      }
    
      protected def callFilter[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val id = c.Expr[K](q"$entity.id")
        callFilterOnId[K](id)(dSchema)
      }
    }
    
    trait AbstractCrudMacro extends FilterCrudMacro {
      val c: MacroContext
      import c.universe._
    
      def all[T: c.WeakTypeTag](dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run($dSchema)
        """
    
      def createAndGenerateId[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run($dSchema.insertValue($entity).returningGenerated(_.id))
        """
    
      def update[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val q = $filter
          run(q.updateValue($entity))
        """
      }
    
      def delete[K: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
        val filter = callFilterOnId(id)(dSchema)
        q"""
          import ${c.prefix}._
          val q = $filter
          run(
             q.delete
          )
        """
      }
    
      def deleteByFilter(filter: Tree)(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run(
             $dSchema.filter($filter).delete
          )
        """
    
      def deleteAll[T: c.WeakTypeTag](dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run($dSchema.delete)
        """
    
      def searchByFilter[T: c.WeakTypeTag](filter: Tree)(offset: c.Expr[Int], limit: c.Expr[Int])(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run(
            $dSchema.filter($filter).drop(lift($offset)).take(lift($limit))
          )
        """
    
      def count(filter: Tree)(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run(
             $dSchema.filter($filter).size
          )
        """
    }
    

    CrudMacro.scala

    package pl.jozwik.quillgeneric.quillmacro
    
    import scala.reflect.macros.whitebox.{ Context => MacroContext }
    
    class CrudMacro(val c: MacroContext) extends AbstractCrudMacro {
    
      import c.universe._
    
      def createAndGenerateIdOrUpdate[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val id = $entity.id
          val q = $filter
          val result = run(
            q.updateValue($entity)
          )
          if (result == 0) {
            run($dSchema.insertValue($entity).returningGenerated(_.id))
          } else {
            id
          }
        """
      }
    
      def createWithGenerateIdOrUpdateAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val id = $entity.id
          val q = $filter
          val result = run(
            q.updateValue($entity)
          )
          val newId =
            if (result == 0) {
              run($dSchema.insertValue($entity).returningGenerated(_.id))
            } else {
              id
            }
          run($dSchema.filter(_.id == lift(newId)))
          .headOption
          .getOrElse(throw new NoSuchElementException(s"$$newId"))
        """
      }
    
      def createWithGenerateIdAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          val newId = run($dSchema.insertValue($entity).returningGenerated(_.id))
          val q = $dSchema.filter(_.id == lift(newId))
          run(q)
          .headOption
          .getOrElse(throw new NoSuchElementException(s"$$newId"))
        """
    
      def createOrUpdate[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val id = $entity.id
          val q = $filter
          val result = run(q.updateValue($entity))
          if(result == 0){
              run($dSchema.insertValue($entity))
          } 
          id
        """
      }
    
      def createOrUpdateAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val id = $entity.id
          val q = $filter
          val result = run(
              q.updateValue($entity)
           )
           if(result == 0){
             run($dSchema.insertValue($entity))
           }
           run(q)
           .headOption
           .getOrElse(throw new NoSuchElementException(s"$$id"))
        """
      }
    
      def create[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree =
        q"""
          import ${c.prefix}._
          run(
            $dSchema.insertValue($entity)
          )
          $entity.id
        """
    
      def createAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val id = $entity.id
          run($dSchema.insertValue($entity))
          val q = $filter
          run(q)
          .headOption
          .getOrElse(throw new NoSuchElementException(s"$$id"))
        """
      }
    
      def updateAndRead[K: c.WeakTypeTag, T: c.WeakTypeTag](entity: Tree)(dSchema: c.Expr[_]): Tree = {
        val filter = callFilter[K, T](entity)(dSchema)
        q"""
          import ${c.prefix}._
          val q = $filter
          run(q.updateValue($entity))
          run(q)
          .headOption
          .getOrElse{
            val id = $entity.id
            throw new NoSuchElementException(s"$$id")
           }
        """
      }
    
      def read[K: c.WeakTypeTag, T: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
        val filter = callFilterOnId[K](id)(dSchema)
        q"""
          import ${c.prefix}._
          val q = $filter
          run(q)
          .headOption
        """
      }
    
      def readUnsafe[K: c.WeakTypeTag, T: c.WeakTypeTag](id: c.Expr[K])(dSchema: c.Expr[_]): Tree = {
        val filter = callFilterOnId[K](id)(dSchema)
        q"""
          import ${c.prefix}._
          val q = $filter
          run(q)
          .headOption
          .getOrElse(throw new NoSuchElementException(s"$$id"))
        """
      }
    
    }
    

    CrudWithContext.scala

    package pl.jozwik.quillgeneric.quillmacro.sync
    
    import io.getquill.context.Context
    import pl.jozwik.quillgeneric.quillmacro.{ CrudMacro, DateQuotes, WithId }
    
    import scala.language.experimental.macros
    
    object CrudWithContext {
      type CrudWithContextDateQuotesUnit = CrudWithContextDateQuotes[Unit]
      type CrudWithContextDateQuotesLong = CrudWithContextDateQuotes[Long]
    }
    
    trait CrudWithContextDateQuotes[U] extends CrudWithContext[U] with DateQuotes {
      this: Context[_, _] =>
    }
    
    trait CrudWithContext[U] {
      this: Context[_, _] =>
      type dQuery[T] = this.DynamicEntityQuery[T]
    
      def all[T](implicit dSchema: dQuery[T]): Seq[T] = macro CrudMacro.all[T]
    
      def create[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): K = macro CrudMacro.create[K, T]
    
      def createAndRead[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): T = macro CrudMacro.createAndRead[K, T]
    
      def createAndGenerateId[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): K = macro CrudMacro.createAndGenerateId[K, T]
    
      def createWithGenerateIdAndRead[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): T = macro CrudMacro.createWithGenerateIdAndRead[K, T]
    
      def createOrUpdate[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): K = macro CrudMacro.createOrUpdate[K, T]
    
      def createOrUpdateAndRead[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): T = macro CrudMacro.createOrUpdateAndRead[K, T]
    
      def createAndGenerateIdOrUpdate[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): K = macro CrudMacro.createAndGenerateIdOrUpdate[K, T]
    
      def createWithGenerateIdOrUpdateAndRead[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): T =
        macro CrudMacro.createWithGenerateIdOrUpdateAndRead[K, T]
    
      def update[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): U = macro CrudMacro.update[K, T]
    
      def updateAndRead[K, T <: WithId[K]](entity: T)(implicit dSchema: dQuery[T]): T = macro CrudMacro.updateAndRead[K, T]
    
      def read[K, T <: WithId[K]](id: K)(implicit dSchema: dQuery[T]): Option[T] = macro CrudMacro.read[K, T]
    
      def readUnsafe[K, T <: WithId[K]](id: K)(implicit dSchema: dQuery[T]): T = macro CrudMacro.readUnsafe[K, T]
    
      def delete[K, T <: WithId[K]](id: K)(implicit dSchema: dQuery[T]): U = macro CrudMacro.delete[K]
    
      def deleteAll[T](implicit dSchema: dQuery[T]): U = macro CrudMacro.deleteAll[T]
    
      def deleteByFilter[T](filter: T => Boolean)(implicit dSchema: dQuery[T]): Long = macro CrudMacro.deleteByFilter
    
      def searchByFilter[T](filter: T => Boolean)(offset: Int, limit: Int)(implicit dSchema: dQuery[T]): Seq[T] = macro CrudMacro.searchByFilter[T]
    
      def count[T](filter: T => Boolean)(implicit dSchema: dQuery[T]): Long = macro CrudMacro.count
    }
    

    实现示例: ConfigurationRepository.scala

    package pl.jozwik.quillgeneric.sync.jdbc.repository
    
    import io.getquill.NamingStrategy
    import io.getquill.context.sql.idiom.SqlIdiom
    import pl.jozwik.quillgeneric.model.{ Configuration, ConfigurationId }
    import pl.jozwik.quillgeneric.quillmacro.sync.JdbcRepository
    import pl.jozwik.quillgeneric.quillmacro.sync.JdbcRepository.JdbcContextDateQuotes
    
    import scala.util.Try
    
    class ConfigurationRepository[D <: SqlIdiom, N <: NamingStrategy](
        protected val context: JdbcContextDateQuotes[D, N],
        protected val tableName: String = "Configuration"
    ) extends JdbcRepository[ConfigurationId, Configuration, D, N] {
    
      protected def dynamicSchema: context.DynamicEntityQuery[Configuration] = dSchema
    
      private val aliases = {
        import context._
        Seq(
          alias[Configuration](_.id, "key"),
          alias[Configuration](_.value, "value")
        )
      }
      private implicit val dSchema: context.DynamicEntityQuery[Configuration] =
        context.dynamicQuerySchema[Configuration](tableName, aliases: _*)
    
      override def all: Try[Seq[Configuration]] = Try {
        context.all[Configuration]
      }
    
      override def create(entity: Configuration): Try[ConfigurationId] = Try {
        context.create[ConfigurationId, Configuration](entity)
      }
    
      override def createAndRead(entity: Configuration): Try[Configuration] =
        Try {
          context.transaction {
            context.createAndRead[ConfigurationId, Configuration](entity)
          }
        }
    
      override def createOrUpdate(entity: Configuration): Try[ConfigurationId] = Try {
        context.transaction {
          context.createOrUpdate[ConfigurationId, Configuration](entity)
        }
      }
    
      override def createOrUpdateAndRead(entity: Configuration): Try[Configuration] = Try {
        context.transaction {
          context.createOrUpdateAndRead[ConfigurationId, Configuration](entity)
        }
      }
    
      override def read(id: ConfigurationId): Try[Option[Configuration]] =
        Try {
          context.read[ConfigurationId, Configuration](id)
        }
    
      override def readUnsafe(id: ConfigurationId): Try[Configuration] =
        Try {
          context.readUnsafe[ConfigurationId, Configuration](id)
        }
    
      override def update(entity: Configuration): Try[Long] = Try {
        context.update[ConfigurationId, Configuration](entity)
      }
    
      override def updateAndRead(entity: Configuration): Try[Configuration] = Try {
        context.transaction {
          context.updateAndRead[ConfigurationId, Configuration](entity)
        }
      }
      override def delete(id: ConfigurationId): Try[Long] =
        Try {
          context.delete[ConfigurationId, Configuration](id)
        }
    
      override def deleteAll: Try[Long] =
        Try {
          context.deleteAll
        }
    
    }
    

    您将在 github 存储库 quill-generic 上找到的缺失类型

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多