版权声明:本文为博主原创文章,欢迎大家转载!
但是转载请标明出处: https://blog.csdn.net/t000818/article/details/84303795 ,本文出自:【唐宏宇的博客】
在应用程序中添加和更改功能时,需要修改数据库实体类以映射这些更改。当用户更新到最新版本的应用程序时,您不希望它们丢失所有现有数据,尤其是在您无法从远程服务器恢复数据时。
Room persistence library 库可以通过编写Migration类以保留用户数据。每个Migration类都指定一个startVersion和endVersion。在运行时,Room会运行每个Migration类的migrate()方法,使用正确的顺序将数据库迁移到更高版本。
Kotlin写法:
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()
Java写法:
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE Book "
+ " ADD COLUMN pub_year INTEGER");
}
};
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();
警告:要使迁移逻辑按预期运行,请使用完整查询,而不是引用表示查询的常量。
迁移过程完成后,Room会验证模型以确保正确进行迁移。如果Room发现问题,则会抛出包含不匹配信息的异常。
测试数据迁移
迁移并不是一件容易的事,但是如果没有正确编写它们可能会导致应用程序出现崩溃循环。为了保持应用程序的稳定性,您应该事先测试迁移。 Room提供测试Maven依赖库以协助此测试过程。但是,要使此依赖库生效,您需要导出数据库的模型。
导出模型
在编译时,Room会将数据库的模型信息导出到JSON文件中。要导出架构,请在build.gradle文件中设置room.schemaLocation解处理器属性,如以下代码段所示:
build.gradle
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
}
}
测试包提供了MigrationTestHelper类,可以读取这些模型文件。它还实现了JUnit4 TestRule接口,因此它可以管理已创建的数据库。
以下代码段中显示了示例迁移测试:
Kotlin写法:
@RunWith(AndroidJUnit4::class)
class MigrationTest {
private val TEST_DB = "migration-test"
@Rule
val helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
MigrationDb::class.java.canonicalName,
FrameworkSQLiteOpenHelperFactory()
)
@Test
@Throws(IOException::class)
fun migrate1To2() {
var db = helper.createDatabase(TEST_DB, 1).apply {
// db has schema version 1. insert some data using SQL queries.
// You cannot use DAO classes because they expect the latest schema.
execSQL(...)
// Prepare for the next version.
close()
}
// Re-open the database with version 2 and provide
// MIGRATION_1_2 as the migration process.
db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)
// MigrationTestHelper automatically verifies the schema changes,
// but you need to validate that the data was migrated properly.
}
}
Java写法:
@RunWith(AndroidJUnit4.class)
public class MigrationTest {
private static final String TEST_DB = "migration-test";
@Rule
public MigrationTestHelper helper;
public MigrationTest() {
helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
MigrationDb.class.getCanonicalName(),
new FrameworkSQLiteOpenHelperFactory());
}
@Test
public void migrate1To2() throws IOException {
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
// db has schema version 1. insert some data using SQL queries.
// You cannot use DAO classes because they expect the latest schema.
db.execSQL(...);
// Prepare for the next version.
db.close();
// Re-open the database with version 2 and provide
// MIGRATION_1_2 as the migration process.
db = helper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2);
// MigrationTestHelper automatically verifies the schema changes,
// but you need to validate that the data was migrated properly.
}
}
优雅地处理丢失的迁移路径
更新数据库的模型后,某些设备上的数据库仍可能使用较旧的模式版本。如果Room无法找到将该设备的数据库从旧版本升级到当前版本的迁移规则,则会发生IllegalStateException。
要防止应用程序在发生这种情况时崩溃,请在创建数据库时调用fallbackToDestructiveMigration()构建器方法:
Kotlin写法:
Room.databaseBuilder(applicationContext, MyDb::class.java, "database-name")
.fallbackToDestructiveMigration()
.build()
Java写法:
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.fallbackToDestructiveMigration()
.build();
通过在应用程序的数据库构建逻辑中加入此子句,可以告知到Room在缺少数据库模型的版本之间迁移路径的情况下,破坏性地重新创建应用程序的数据库表。
警告:通过在应用程序的数据库构建器中配置此选项,Room会在缺少迁移路径时永久删除数据库表中的所有数据。
破坏性回退逻辑包括几个附加选项:
- 如果您使用迁移路径无法解决的架构历史记录的特定版本中发生错误,请使用
fallbackToDestructiveMigrationFrom()
. 此方法表示只有在数据库尝试从其中一个有问题的版本迁移的情况下,您才希望Room使用回退逻辑. - 要仅在尝试模式降级时执行破坏性重新创建,请改用
fallbackToDestructiveMigrationOnDowngrade()