我们正在构建数据结构迁移系统,该系统应该能够从任何版本的案例类迁移到最新版本;以及从最新版本到以前任何一个版本。 概念如下:
我们有一个案例类和迁移类的结构
v1/SchemaV1 <= Case class V1
v2/SchemaV2 <= Case class V2
v2/MigrationV2 <= Migration Class from 1->2 and back
v3/SchemaV3 <= Case class V3
v3/MigrationV3 <= Migration Class from 2->3 and back
...
其中:
Schema
目前,迁移特征如下所示:
trait TypedMigration[F <: Schema, T <: Schema] {
type FromSchema = F
type ToSchema = T
/**
* This method is responsible for migrating one version of a schema 'F'(From) to another 'T' (To)
*
* @param from
* @return
*/
def up(from: FromSchema): ToSchema
/**
* This method is responsible for migrating one version of a schema down one level
*
* @param from
* @return
*/
def down(from: ToSchema): FromSchema
}
这里的想法是,每个迁移类的实现都会强制执行from
和to
类型。
举个例子:
class V2Migration extends TypedMigration[v1.SchemaV1, v2.SchemaV2] {
...
}
如何,有一个服务类负责管理整个迁移过程。这个类有两种处理迁移'up'的方法。分别“下来”
举个例子:
protected[versioning] def doMigrateUp(schema: Schema): ChecklistSchema = {
// load all required migration classes
val migrations = loadMigrationObjects[Schema, Schema](resolveSchemaVersion(schema), targetVersion)
var migratedSchema = schema
// apply all migrations up
migrations.foreach { migration =>
migratedSchema = migration.up(migratedSchema)
}
migratedSchema.asInstanceOf[ChecklistSchema]
}
其中:
ChecklistSchema
是最新版本的类型别名。所以我最后可以安全地投入它。直到现在都很好......
我的问题始于'doMigrateDown`方法:
protected[versioning] def doMigrateDown[S <: Schema](schema: ChecklistSchema, targetVersion: Int): S = {
val migrations = loadMigrationObjects[Schema, ChecklistSchema](resolveSchemaVersion(schema), targetVersion)
var migratedSchema = schema
// apply all migrations down
migrations.foreach { migration =>
migratedSchema = migration.down(migratedSchema)
}
migratedSchema.asInstanceOf[S]
}
这里有两个问题:
我不确定这个演员是否真的有效。
我这一点我完全被卡住了......
作为参考,这是我们构建迁移对象列表的方式:
protected[versioning] def loadMigrationObjects[F <: Schema, T <: Schema](currentVersion: Int, targetVersion: Int): List[TypedMigration[F, T]] = {
val migrationsRange = if (currentVersion <= targetVersion) {
currentVersion + 1 to targetVersion
} else {
currentVersion to targetVersion + 1 by -1
}
val migrations = List[TypedMigration[F, T]]()
migrationsRange.foldLeft(migrations) { case (acc, versionNumber) =>
Try {
val migrationClassName = MigrationClassNameFormat.format(versionNumber, versionNumber)
Class.forName(migrationClassName).newInstance().asInstanceOf[TypedMigration[F, T]]
} match {
case Success(m) => acc :+ m
case Failure(e: ClassNotFoundException) =>
logger.info("migration class was not found", e)
acc
case Failure(e) => throw e
}
}
}