【问题标题】:Compare database schema with existing slick tables将数据库模式与现有的光滑表进行比较
【发布时间】:2016-01-09 17:58:09
【问题描述】:

我正在使用ScalaSlickPostgres 来构建应用程序。我使用Slick code generator 来生成光滑的表格。

我想知道是否有任何方法可以验证数据库表架构和 slick 表架构是否匹配,并对我的应用程序中的所有 slick 表执行此操作。

例如:

class DepartmentTable(_tableTag: Tag) extends Table[Department](_tableTag, Some("base"), "Department") {
    val id: Rep[Long] = column[Long]("DepartmentId", O.AutoInc, O.PrimaryKey)
    val name: Rep[String] = column[String]("Name", O.Length(50,varying=true))
    val shortCode: Rep[String] = column[String]("ShortCode", O.Length(50,varying=true))
    def * = ???
    def ? = ???
} 

我更改了数据库表,例如在表中添加一列 parentDepartmentId,然后将其添加到 Slick 表中。很多时候,存在更改脚本未在测试数据库上运行的问题,因此我们将遇到一些运行时异常。

为了避免此类问题,我试图实现一些东西来检查光滑表是否与实际的 postgres 表匹配。可以实现吗?

我尝试了反射,但无法从光滑的桌子上获得所有细节。 eg:实际的列名

Slick Version : 3.0

我想要达到什么目的?

在应用程序启动时,我想将数据库架构与光滑架构进行比较。

我的计划:

  1. 从我的应用程序中获取所有 TableQuery / Slick 表

  2. 使用 Slick Meta 获取实际的数据库架构

  3. 将 slick tablequery 结构与实际 db 进行比较

现在,正如 Maxim 所建议的,我可以创建一个注册表并将每个表添加到注册表中。我只是想看看有没有其他方法。原因是如果我或其他人不小心删除了向注册表添加了几个表查询,则不会对该表进行检查。我只是想更安全,但不确定是否存在这样的方法。

【问题讨论】:

  • 请消除 Slick 的 Table (描述表的结构)和 之间的混淆,让您的问题一清二楚Slick 的TableQuery 类的实例,它们代表Slick 中的实际数据库

标签: scala slick


【解决方案1】:

您可以使用slick.meta 来实现此目的。您并没有说您使用的是哪个版本的 slick,所以我将展示一个使用 slick 3.0 的示例,但是如果您使用 slick 2.x 将 DBIO 替换为旧的 withSession API 和删除对ExecutionContextFuture 的引用。

这是如何打印模式中每个表的所有列的方法,假设您在范围内有一个隐式 ExecutionContext,您导入 YourDriver.api._ 并将 ??? 替换为实际的 Database 实例:

val db: Database = ???

val tablesWithCols = for {
  tables <- slick.jdbc.meta.MTable.getTables
  withCols <- DBIO.sequence(tables.map(t => t.getColumns.map((t, _))))
} yield withCols

val printLines: DBIO[Seq[String]] = tablesWithCols.map {
  _.map {
    case (t, cs) => s"Table: ${t.name.name} - columns: ${cs.map(_.name).mkString(", ")}"
  }
}

val res: Future[Seq[String]] = db.run(printLines)

res.foreach(println)

另外,请不要在 Future 上执行最后一次 foreach 调用,因此您可能希望等待未来完成或(更好)将其与相关计算链接;如果您的程序在没有等待/链接的情况下终止,您可能不会从那里看到任何东西。

令人惊讶的是,从光滑的表定义中获取信息有点复杂。我发现这样做的唯一方法是这样的:

TableQuery[YourTable].toNode.getDumpInfo

这将为您提供一个类似于 AST 的结构,您可以遍历该结构以获取所需的定义;结构本身并不是那么令人愉快,但它应该包含你需要的一切。

您可以探索的另一种避免这种麻烦的方法是创建一个层,该层包含了平滑定义的生成并以更易于访问的方式公开相关元数据;不过,不确定这是否会给您带来更大的麻烦。

【讨论】:

  • 这样我就可以从数据库中获取实际的架构细节。我已经在做。但是,我不知道如何获取我的代码中已经存在的 Slick Tables。我需要将 MTable 与我的代码中已经存在的表进行比较。
  • 我扩展了答案,添加了一些关于从巧妙的定义中获取数据的内容,如果我一开始没有理解这个问题,请见谅。
【解决方案2】:

这是一个示例,说明如何检测给定 Slick 表的数据库架构中应该与表对应的所有列的数量、名称和 SQL 类型是否等于列的数量、名称和 SQL 类型在表的 Slick 表描述中

def ?[AT <: AbstractTable[_]](tableQuery: profile.api.TableQuery[AT])
                             (implicit ec: ExecutionContext) = {
  val table = tableQuery.baseTableRow.create_*.map(c =>
    (c.name, profile.jdbcTypeFor(c.tpe).sqlType)).toSeq.sortBy(_._1)
  MTable.getTables(tableQuery.baseTableRow.tableName).headOption.map(
    _.map{_.getColumns.map(
      _.sortBy(_.name).map(c => (c.name, c.sqlType)) == table
    )}
  ) flatMap (_.head)
}

您还可以检测索引、主键和外键是否在某种程度上相同。为此,您可以相应地组合

tableQuery.baseTableRow.indexes    
tableQuery.baseTableRow.primaryKeys
tableQuery.baseTableRow.foreignKeys

用MTable的以下方法

getIndexInfo    
getPrimaryKeys
getImportedKeys

就像我在摘录中对 tableQuery.baseTableRow.create_*getColumns 所做的那样。

现在有了这个方法,您可以轻松检查代码中的所有表。唯一真正简单的问题是如何获得他们的名单。说实话,我什至不明白这怎么会是一个问题,因为这只是保持一个集中式注册表的问题,您可以在每次使用 您的 代码创建表时登记一个表,并且您可以查询存储的对象。假设您有这样的 registryenlistTablelistTables 方法,那么您的工作流程将类似于

val departmentTable = TableQuery[DepartmentTable]
regsitry.enlistTable(departmentTable)
...
val someTable = TableQuery[SomeTableStructureClass]
regsitry.enlistTable(someTable)   
...
val anotherTable = TableQuery[AnotherTableStructureClass]
regsitry.enlistTable(anotherTable)   
...
for(table <- regsitry.listTables)
  db run ?(table) map ( columnsAndTypesAreIdentical => ... ) 
...

.

你默认使用"generates Table classes, corresponding TableQuery values,..., as well as case classes for holding complete rows of values" 的 Slick 代码生成器。 对应的 TableQuery 值的格式正好是 val someTable = TableQuery[SomeTableStructureClass]

【讨论】:

  • 谢谢。我能够得到这个。但问题是我不知道光滑的表都是什么,所以我不能做TableQuery[Department]。我应该能够为我的应用程序中的所有 Slick 表获取该 TableQuery。但我无法做到这一点。尝试了一点反思,但还是一样。
  • 反思?你可能在开玩笑。这只是分解您的代码以便使用旧的注册表/存储库模式的问题。
  • 假设我的代码中有 100 个光滑的表。如何动态获取这 100 个表的“TableQuery[MyTable1]”?
  • 我不懂你。你说你有 100 张光滑的桌子是什么意思。表(而不是表存根)是根据您的表描述创建的一些实例。例如。您的 DepartmentTable 是 Departments 表和其他元数据的描述,TableQuery[DepartmentTable] 是您实际使用的该表的代理)。所以你有 100 个对“表”的引用,看起来像 val table = TableQuery[...]。分解您的代码,以便在每个 vals 定义之后或任何地方立即调用 registry.enlist(table) ,然后按照我在答案中的描述继续。
  • 你说你使用了默认的"generates Table classes, corresponding TableQuery values,..., as well as case classes for holding complete rows of values"的Slick代码生成器。那些对应的TableQuery值就是我说的,也是你要注册的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-23
  • 2011-04-02
  • 1970-01-01
  • 2014-09-05
  • 1970-01-01
相关资源
最近更新 更多