Room数据库迁移实战

一,修改Entity类,升级database的version,添加Migration对象用于升级

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
  @Override
  public void migrate(@NonNull SupportSQLiteDatabase database) {
    database.execSQL("alter table MptModel add column isInternal INTEGER not null default 0");
    database.execSQL("alter table MptModel add column internalScopes TEXT");
    database.execSQL("delete from MptModel");
  }
};
ScopeAuthorizeDatabase database = Room
    .databaseBuilder(ContextUtils.getApplicationContext(),
        ScopeAuthorizeDatabase.class, dbPath)
    .addMigrations(MIGRATION_1_2)
    .build();

二,为确保迁移成功,必须要做单元测试

根据本人亲身体验,强烈建议编写单元测试
单元测试可以尽可能多地测试各种场景,而且及时反馈,不仅降低出bug的可能,还提升了开发效率。

1,导出架构,添加以下配置,在编译时room会导出数据库架构信息到json文件;
将架构信息位置添加为资源文件夹

// module的build.gradle
android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }
    sourceSets {
        androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
    }
}
dependencies {
    androidTestImplementation "androidx.room:room-testing:2.2.1"
}

2,编写单元测试
首先创建一个helper类
测试时先创建低版本的数据库,插入数据,关闭数据库。然后执行迁移命令,检查数据库数据是否正常

public class ScopeAuthorizeDatabaseTest {
  private static final String TEST_DB = "migration-scope-authorize-db-test";
  @Rule
  public MigrationTestHelper helper;
  public ScopeAuthorizeDatabaseTest() {
    helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
        ScopeAuthorizeDatabase.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(
        "insert into MptModel (miniAppId,miniAppMpt,openId) values ('ks001','hello','world')");
    Cursor query1 = db.query("select * from MptModel");
    query1.moveToFirst();
    assertEquals(1, query1.getCount());
    assertEquals(3, query1.getColumnCount());
    // 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.
    Cursor query2 = db.query("select * from MptModel");
    query2.moveToFirst();
    assertEquals(0, query2.getCount());// 升级策略是丢弃旧数据
    assertEquals(5, query2.getColumnCount());
    // 插入新数据
    db.execSQL(
        "insert into MptModel (miniAppId,miniAppMpt,openId) values ('ks001','hello','world')");
    Cursor query3 = db.query("select * from MptModel");
    assertEquals(1, query3.getCount());
    query3.moveToFirst();
    // 验证column
    String miniAppId = query3.getString(query3.getColumnIndex("miniAppId"));
    String miniAppMpt = query3.getString(query3.getColumnIndex("miniAppMpt"));
    String openId = query3.getString(query3.getColumnIndex("openId"));
    int isInternal = query3.getInt(query3.getColumnIndex("isInternal"));
    String internalScopes = query3.getString(query3.getColumnIndex("internalScopes"));
    assertEquals("ks001", miniAppId);
    assertEquals("hello", miniAppMpt);
    assertEquals("world", openId);
    assertEquals(0, isInternal);// 默认为false
    assertNull(internalScopes);// 默认为null
  }
}

三,兜底策略
有时候可能缺失了部分升级路径,这时会发生IllegalStateException,为防止这种情况,需要使用兜底策略。

ScopeAuthorizeDatabase database = Room
    .databaseBuilder(ContextUtils.getApplicationContext(),
        ScopeAuthorizeDatabase.class, dbPath)
    .addMigrations(MIGRATION_1_2)
    .fallbackToDestructiveMigration()
    .build();

你可能感兴趣的:(android,数据库)