【问题标题】:Scala reflection to instantiate scala.slick.lifted.TableQueryScala 反射实例化 scala.slick.lifted.TableQuery
【发布时间】:2015-07-22 20:41:48
【问题描述】:

我有这个基本特征

trait MyBase {
  type M
  type T <: Table[M]
  val query: TableQuery[T]
}

TableQueryscala.slick.lifted.TableQuery

我的子类像这样实例化TableQuery

type M = Account
type T = AccountsTable
val query = TableQuery[T]

我想在基本特征中实例化TableQuery,可能使用lazy val,即

lazy val query: TableQuery[T] = {
  ...
}

我一直在玩反射,但运气不佳。

【问题讨论】:

  • 也许这是一个愚蠢的问题,但是在 trait 中实例化 TableQuery 的目的是什么(很难理解它的值,因为每个使用 trait 的类型都会有所不同) - 也许添加这个可能会为某人添加更多上下文以提供更好的答案。

标签: scala reflection slick


【解决方案1】:

如果我理解正确,您想要的是能够扩展 MyBase 只需定义 MT,但不必在每个派生类中显式实例化 TableQuery

使用反射并不是一个真正的选择,因为通常你使用TableQuery.apply 为此(如val query = TableQuery[MyTable]),这是通过宏实现的, 所以你有一个“运行时与编译时”的问题。

如果您绝对需要 MyBase 作为特征(而不是类),那么我看不到任何可行的解决方案。 但是,如果您可以将MyBase 转换为类MT 转换为类型参数(而不是抽象类型),那么至少有一个解决方案。 正如我在另一个相关问题 (How to define generic type in Scala?) 中所暗示的,您可以 定义一个类型类(比如TableQueryBuilder)来捕获对TableQuery.apply(在具体类型已知的地方)的调用以及一个隐式宏(比如TableQueryBuilder.builderForTable)来提供 该类型类的一个实例。然后,您可以定义一个方法(例如 TableQueryBuilder.build)来实际实例化 TableQuery,它只会将作业委托给类型类。

// NOTE: tested with scala 2.11.0 & slick 3.0.0
import scala.reflect.macros.Context
import scala.language.experimental.macros
object TableQueryBuilderMacro {
  def createBuilderImpl[T<:AbstractTable[_]:c.WeakTypeTag](c: Context) = {
    import c.universe._
    val T = weakTypeOf[T]
    q"""new TableQueryBuilder[$T]{
      def apply(): TableQuery[$T] = {
        TableQuery[$T]
      }
    }"""
  }
}
trait TableQueryBuilder[T<:AbstractTable[_]] {
  def apply(): TableQuery[T]
}
object TableQueryBuilder {
  implicit def builderForTable[T<:AbstractTable[_]]: TableQueryBuilder[T] = macro TableQueryBuilderMacro.createBuilderImpl[T]
  def build[T<:AbstractTable[_]:TableQueryBuilder](): TableQuery[T] = implicitly[TableQueryBuilder[T]].apply()
}

最终效果是您不再需要知道T 类型的具体值,以便能够实例化TableQuery[T], 前提是您在范围内有 TableQueryBuilder[T] 的隐式实例。也就是说,你可以转移需要知道T的具体值 直到你真正知道为止。

MyBase(现在是一个类)可以这样实现:

class MyBase[M, T <: Table[M] : TableQueryBuilder] {
  lazy val query: TableQuery[T] = TableQueryBuilder.build[T]
}

然后您可以扩展它而无需显式调用TableQuery.apply

class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
  def name = column[String]("COF_NAME")
  def price = column[Double]("PRICE")
  def * = (name, price)
}

class Derived extends MyBase[(String, Double), Coffees] // That's it!

这里发生的是,在Derived 的构造函数中,TableQueryBuilder[Coffees] 的隐含值是隐含的 传递给MyBase的构造函数。

如果MyBase 是一个特征,你不能应用这个模式的原因很普通:特征构造函数不能有参数,更不用说隐式参数,所以没有隐式方法 传递TableQueryBuilder 实例。

【讨论】:

  • 感谢您花时间整理这个答案。这里有很多很棒的信息。
  • 虽然这是一个很好的解决方案,但我仍然在努力寻找如何抽象出一个简单的db.run(query.result),因为结果无法解决。反正有没有用 Slick 实现某种数据映射器?谢谢
  • 我不确定我是否理解您的问题。您可能想用相关的代码 sn-p 发布一个正确的问题来证明问题。
  • 太棒了!这个答案对我很有帮助!
猜你喜欢
  • 2020-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多