如何在函数中包含光滑的API含义?

时间:2015-10-22 09:19:49

标签: scala slick

我正在使用光滑的3.1.0实现一个根据需要创建表的函数,如下所示:

  def ensureTables(db: backend.DatabaseDef, ts: Seq[TableQuery[_]]) {
    val ts0 = Await.result(db.run(MTable.getTables), Duration.Inf)
    val ns = Set() ++ ts0.map(t => t.name.name)

    for {
      t <- ts
    } Await.result(db.run(t.schema.create), Duration.Inf)
  }

由于编译错误,它还没有完成(我想在for表达式中添加过滤器)。错误发生在第七行:value schema is not a member of slick.driver.SQLiteDriver.api.TableQuery[_$1]。这是因为SQLiteDriver.API(在这种情况下为tableQueryToTableQueryExtensionMethods)中定义的隐式函数未包括在内。

如何正确包含这些隐式功能?

2 个答案:

答案 0 :(得分:2)

当我想使用不同的驱动程序插入我的db代码时,我通常使用配置文件类型参数化我的函数(或traits / classes)并提供配置文件作为参数;在你的情况下,函数看起来像这样:

def ensureTables[P <: JdbcProfile](profile: P)(db: profile.api.Database, ts: Seq[TableQuery[_]]) {
  import profile.api._
  ...
}

如果函数位于聚合数据库相关函数的特征或类中,则可以在类级别提供配置文件并在那里导入。

顺便说一句,请注意您每次使用Await时都会阻塞两个线程并可能导致死锁;使用像flatMap这样的未来组合器代替编写异步代码。

答案 1 :(得分:1)

<强> TL;博士

注意:这不是一个新的答案,它只详细阐述了Aldo Stracquadanio提供的答案。这是一个答案,以便以友好的方式显示更多细节。

首先,我尝试了以下版本,这是简单应用Aldo提示的结果:

def ensureTables[P <: JdbcProfile]
                (profile: P)
                (db: profile.api.Database, ts: Seq[TableQuery[_]]) {
  import profile.api._
  val ts0 = Await.result(db.run(MTable.getTables), Duration.Inf)
  val ns = Set() ++ ts0.map(t => t.name.name)
  for (t <- ts) Await.result(db.run(t.schema.create), Duration.Inf)
}

错误仍然存​​在,因为ts的类型错误:tableQueryToTableQueryExtensionMethods所需的类型为Query[T, U, Seq] with TableQuery[T],而不是TableQuery[_],因此没有任何含义符合条件对于上述定义。

ensureTables的正确版本应实现为:

def ensureTables[P <: RelationalProfile, T <: RelationalProfile#Table[_], C[_]]
                (profile: P)
                (db: profile.api.Database,
                 ts: Seq[Query[T, _, C] with TableQuery[T]]) {
  import profile.api._
  val ts0 = Await.result(db.run(MTable.getTables), Duration.Inf)
  val ns = Set() ++ ts0.map(t => t.name.name)
  for (t <- ts) Await.result(db.run(t.schema.create), Duration.Inf)
}

此函数本身编译。但是,当我尝试将多个TableQuery值放在一起并使用它时,我得到了两个新错误:

ensureTables(profile)(db, Seq(circles, rectangles))

导致

[error] SlickProg.scala:40: no type parameters for method ensureTables: (db: slick.driver.SQLiteDriver.profile.api.Database, ts: Seq[slick.driver.SQLiteDriver.api.Query[T, _, C] with slick.driver.SQLiteDriver.api.TableQuery[T]])Unit exist so that it can be applied to arguments (slick.driver.SQLiteDriver.backend.DatabaseDef, Seq[slick.lifted.TableQuery[_ >: slab.Rectangles with slab.Circles <: slick.driver.SQLiteDriver.Table[_ >: (Int, Double, Double, Double, Double, Double) with (Int, Double, Double, Double) <: Product with Serializable]]])
[error]  --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error]  found   : Seq[slick.lifted.TableQuery[_ >: slab.Rectangles with slab.Circles <: slick.driver.SQLiteDriver.Table[_ >: (Int, Double, Double, Double, Double, Double) with (Int, Double, Double, Double) <: Product with Serializable]]]
[error]  required: Seq[slick.driver.SQLiteDriver.api.Query[?T, _, ?C] with slick.driver.SQLiteDriver.api.TableQuery[?T]]
[error]     (which expands to)  Seq[slick.lifted.Query[?T, _, ?C] with slick.lifted.TableQuery[?T]]
[error]     ensureTables(profile)(db, Seq(circles, rectangles))
[error]                 ^
[error] SlickProg.scala:40: type mismatch;
[error]  found   : Seq[slick.lifted.TableQuery[_ >: slab.Rectangles with slab.Circles <: slick.driver.SQLiteDriver.Table[_ >: (Int, Double, Double, Double, Double, Double) with (Int, Double, Double, Double) <: Product with Serializable]]]
[error]  required: Seq[slick.driver.SQLiteDriver.api.Query[T, _, C] with slick.driver.SQLiteDriver.api.TableQuery[T]]
[error]     (which expands to)  Seq[slick.lifted.Query[T, _, C] with slick.lifted.TableQuery[T]]
[error]     ensureTables(profile)(db, Seq(circles, rectangles))
[error]                                  ^
[error] two errors found

这是由于将不同类型的值放入单个序列中导致意外类型的序列。编译器为序列的元素计算了一个公共类型(_ >: slab.Rectangles with slab.Circles <: slick.driver.SQLiteDriver.Table[_ >: (Int, Double, Double, Double, Double, Double) with (Int, Double, Double, Double) <: Product with Serializable])。

我最终得到以下实现,从ensureTable函数外部迭代表:

def ignore[T](x: T): Unit = ()

def valueOf[T](f: Future[T]): T = Await.result(f, Duration.Inf)

def ensureTable[P <: RelationalProfile, T <: RelationalProfile#Table[_], C[_]]
               (p: P)
               (db: p.api.Database, ns: Set[String],
                t: Query[T, _, C] with TableQuery[T]) {
  import p.api._
  val n = t.shaped.value.tableName
  if (!ns.contains(n))
    ignore(valueOf(db.run(t.schema.create)))
}

def tablesOf[P <: RelationalProfile](p: P)(db: p.api.Database): Seq[MTable] =
   valueOf(db.run(MTable.getTables))

val names = Set() ++ tablesOf(profile)(db).map(_.name.name)
for (t <- Seq(circles, rectangles)) ensureTable(profile)(db, names, t)

谢谢Aldo Stracquadanio。