我正在使用Scala
,Slick
和Postgres
来构建应用程序。我使用Slick code generator
生成了光滑的表格。
我想知道是否有任何方法可以验证数据库表架构和光滑表架构是否匹配,并为我的应用程序中的所有光滑表执行此操作。
例如:
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表匹配。它可以实现吗?
我尝试过反射,但无法从光滑的表格中获取所有细节。例如:实际列名
Slick Version : 3.0
我想要实现的目标是什么?
在启动应用程序时,我想将数据库模式与光滑模式进行比较。
我的计划:
从我的应用程序中获取所有TableQuery / Slick表
使用Slick Meta
将光滑的tablequery结构与实际的db
现在,正如Maxim建议的那样,我可以创建一个注册表并将每个表添加到注册表中。我只想检查是否还有其他方法。原因是如果我或其他人不小心删除了向注册表添加几个表查询,则不会对该表进行检查。我只是想更安全,但不确定是否存在任何此类方法。
答案 0 :(得分:3)
您可以使用slick.meta
来实现此目的。你不是说你正在使用哪个版本的光滑,所以我将使用光滑3.0显示一个例子,但是如果你使用光滑的2.x替换DBIO
和旧的{{1}它应该非常相似API并删除对withSession
和ExecutionContext
的引用。
以下是如何打印模式中每个表的所有列,假设您在范围内有隐式Future
,导入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)
另外,请注意不要在foreach
上执行最后一次Future
调用,因此您可能希望等待将来完成或(更好)将其与相关计算链接起来;如果您的程序在没有等待/链接的情况下终止,您将不会从那里看到任何内容。
令人惊讶的是,一个更复杂的问题是从光滑的表定义中获取信息;我发现这样做的唯一方法就是这样:
TableQuery[YourTable].toNode.getDumpInfo
这将为您提供类似AST的结构,您可以遍历以获得所需的定义;结构本身并不那么令人愉快,但它应该包含你需要的一切。
您可以探索以避免这种麻烦的另一种方法是创建一个包含光滑定义生成的层,并以更易于访问的方式公开相关元数据;不确定这是否会让你陷入更大的困境。
答案 1 :(得分:0)
下面是一个示例,说明如何检测给定的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
所做的那样。
现在有了这种方法,您可以轻松检查代码中的所有表格。唯一真正容易的问题是如何获得他们的清单。说实话,我甚至不明白它是如何成为一个问题,因为这只是一个集中的注册表,你可以在每次在你的代码中创建表时登记表。您可以查询存储的对象。我们假设您使用方法registry
和enlistTable
进行listTables
,那么您的工作流程将类似于
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 => ... )
...
默认情况下使用的{Slick代码生成器"generates Table classes, corresponding TableQuery values,..., as well as case classes for holding complete rows of values"。 对应的TableQuery值的格式正好为val someTable = TableQuery[SomeTableStructureClass]
。