迁移 Room 数据库
当您在应用中添加和更改功能时,需要修改 Room 实体类和底层数据库表以反映这些更改。如果应用更新更改了数据库架构,那么保留设备内置数据库中已有的用户数据就非常重要。
Room 同时支持以自动和手动方式进行的增量迁移。自动迁移适用于大多数基本架构更改,不过对于更复杂的更改,您可能需要手动定义迁移路径。
自动迁移
⭐ 注意
Room 在 2.4.0-alpha01 及更高版本中支持自动迁移。 如果您的应用使用的是较低版本的 Room,则必须手动定义迁移。
如需声明两个数据库版本之间的自动迁移,请将 @AutoMigration 注解添加到 @Database 中的 autoMigrations 属性:
// Database class before the version update.
@Database(
version = 1,
entities = [User::class]
)
abstract class AppDatabase : RoomDatabase() {
...
}
// Database class after the version update.
@Database(
version = 2,
entities = [User::class],
autoMigrations = [
AutoMigration(from = 1, to = 2)
]
)
abstract class AppDatabase : RoomDatabase() {
...
}⭐ 注意
Room 自动迁移依赖于为旧版和新版数据库生成的数据库架构。 如果 exportSchema 被设为 false,或者如果您尚未使用新版本号编译数据库,自动迁移将会失败。
自动迁移规范
如果 Room 检测到架构更改不明确,并且无法在没有更多输入的情况下生成迁移计划,则会抛出编译时间错误并要求您实现 AutoMigrationSpec。 这往往出现在迁移涉及以下情形之一时:
- 删除或重命名表。
- 删除或重命名列。
您可以使用 AutoMigrationSpec 为 Room 提供正确生成迁移路径所需的额外信息。定义一个在 RoomDatabase 类中实现 AutoMigrationSpec 的静态类,并为其添加以下一项或多项注解:
@DeleteTable@RenameTable@DeleteColumn@RenameColumn
如需使用 AutoMigrationSpec 实现进行自动迁移,请在相应的 @AutoMigration 注解中设置 spec 属性:
@Database(
version = 2,
entities = [User::class],
autoMigrations = [
AutoMigration(
from = 1,
to = 2,
spec = AppDatabase.MyAutoMigration::class
)
]
)
abstract class AppDatabase : RoomDatabase() {
@RenameTable(fromTableName = "User", toTableName = "AppUser")
class MyAutoMigration : AutoMigrationSpec
...
}如果您的应用在自动化迁移完成后需要执行更多工作,您可以实现 onPostMigrate()。 如果您在 AutoMigrationSpec 中实现此方法,Room 将在自动迁移完成后调用该方法。
⭐ 注意
使用 Kotlin 时,如果您有多项属于同一类型的迁移任务,则必须针对多个注解使用一个容器, 例如 @RenameTable.Entries。
手动迁移
如果迁移涉及复杂的架构更改,Room 可能无法自动生成适当的迁移路径。 例如,如果您决定将表中的数据拆分到两个表中,Room 无法确定应如何执行此拆分。 在这类情况下,您必须通过实现 Migration 类来手动定义迁移路径。
Migration 类会通过替换 Migration.migrate() 方法明确定义 startVersion 和 endVersion 之间的迁移路径。 使用 addMigrations() 方法将您的 Migration 类添加到数据库构建器:
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE `Fruit` (`id` INTEGER, `name` TEXT, " +
"PRIMARY KEY(`id`))"
)
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
}
}
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build()⚠️ 注意
为使迁移逻辑正常工作,请使用完整查询,而不要引用表示查询的常量。
定义迁移路径后,您可以对某些版本使用自动迁移,而对另一些版本使用手动迁移。 如果您为同一版本同时定义了自动迁移和手动迁移,则 Room 会使用手动迁移。
测试迁移
迁移通常十分复杂,迁移定义错误可能会导致应用崩溃。
为了保持应用的稳定性,请测试迁移。