天天看點

Android Architecture Component Room持久化資料庫(五)遷移Room資料庫

版權聲明:本文為部落客原創文章,歡迎大家轉載!

但是轉載請标明出處: 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()

繼續閱讀