如何在函数中包含 slick API 隐式?
How to include slick API implicits in a function?
我正在使用 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[_]
。这是因为 SQLiteDriver.API
中定义的隐式函数(在本例中为 tableQueryToTableQueryExtensionMethods
)未包含在内。
如何正确地包含那些隐式函数?
当我想让我的数据库代码可插入 slick 中的不同驱动程序时,我通常使用配置文件类型对我的函数(或 traits/classes)进行参数化,并将配置文件作为参数提供;在你的情况下,函数看起来像这样:
def ensureTables[P <: JdbcProfile](profile: P)(db: profile.api.Database, ts: Seq[TableQuery[_]]) {
import profile.api._
...
}
如果函数在特征或 class 中,它聚合了与数据库相关的函数,您可以在 class 级别提供配置文件并导入到那里。
顺便说一句,请注意每次使用 Await
; 时都会阻塞两个线程并可能导致死锁;使用像 flatMap
这样的未来组合器来编写异步代码。
tl;dr
注意:这不是一个新答案,它只是详细阐述了 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。
我正在使用 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[_]
。这是因为 SQLiteDriver.API
中定义的隐式函数(在本例中为 tableQueryToTableQueryExtensionMethods
)未包含在内。
如何正确地包含那些隐式函数?
当我想让我的数据库代码可插入 slick 中的不同驱动程序时,我通常使用配置文件类型对我的函数(或 traits/classes)进行参数化,并将配置文件作为参数提供;在你的情况下,函数看起来像这样:
def ensureTables[P <: JdbcProfile](profile: P)(db: profile.api.Database, ts: Seq[TableQuery[_]]) {
import profile.api._
...
}
如果函数在特征或 class 中,它聚合了与数据库相关的函数,您可以在 class 级别提供配置文件并导入到那里。
顺便说一句,请注意每次使用 Await
; 时都会阻塞两个线程并可能导致死锁;使用像 flatMap
这样的未来组合器来编写异步代码。
tl;dr
注意:这不是一个新答案,它只是详细阐述了 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。