在 Android 開發(fā)中,數(shù)據(jù)庫的升級往往需要進行手動遷移,這是一件比較繁瑣的事情。而在 Android Room 中,提供了自動遷移的功能,大大減輕了開發(fā)者的負擔。
當我們對數(shù)據(jù)庫的表結(jié)構(gòu)進行了修改,例如添加了新的列和索引,我們需要升級數(shù)據(jù)庫版本以便應(yīng)用程序能夠正確地處理新的數(shù)據(jù)。在 Android Room 中,我們可以使用 @Database 注解中的 version 屬性來指定版本號,然后使用 @Entity 注解中的 indices 和 columns 屬性來添加索引和列。
為了讓 Room 能夠自動遷移數(shù)據(jù)庫,我們需要在 @Database 注解中添加 autoMigrations 屬性,并提供一個數(shù)組,數(shù)組中的每個元素表示一個自動遷移操作。每個自動遷移操作都包含 fromVersion 和 toVersion 兩個屬性,分別表示起始版本和目標版本。在進行自動遷移時,Room 會自動根據(jù)起始版本和目標版本之間的差異來執(zhí)行相應(yīng)的操作。
一、自動升級
官方文檔地址:
https://developer.android.google.cn/training/data-storage/room/migrating-db-versions?hl=zh-cn
1.引入room的依賴
def room_version = "2.5.0-alpha01"http://2.2.5
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
2.設(shè)置room數(shù)據(jù)庫生成表的sql語句,這一步非常重要
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation":"$projectDir/schemas".toString(),
"room.incremental":"true",
"room.expandProjection":"true"]
}
}
}
3.基本的配置好了以后我們就可以正常的來增加注解來配置數(shù)據(jù)的庫的自動升級了
@Database(entities = {UserBean.class, SchoolsBean.class},
exportSchema = true,
version = 8,
autoMigrations = [
AutoMigration (from = 1, to = 2)
]
)
public abstract class MyDatabase extends RoomDatabase {}
4.如果需要在表里新增一個字段如test字段,只需要增加一個@ColumnInfo注解,并為其賦一個默認值
@ColumnInfo(defaultValue = "0")
配置完成后,我們的項目中schemas 目錄會存在多個json文件,這個是本地數(shù)據(jù)庫各個版本的sql語句,請注意,這些json文件需要保存不能刪除,否則下次升級會找不到上個數(shù)據(jù)庫的文件,導(dǎo)致自動升級失敗
手動升級數(shù)據(jù)庫
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
...
}
}
.addMigrations(MIGRATION_1_2)//可以添加多個參數(shù),以便從1->2,2->3,1->3
銷毀表創(chuàng)建臨時表重建
- 創(chuàng)建一張符合表結(jié)構(gòu)要求的臨時表,
- 將數(shù)據(jù)從舊表復(fù)制到臨時表,
- 刪除舊表,
- 臨時表重命名回原來的表名
異常處理
為了防止升級過程中失敗導(dǎo)致應(yīng)用程序崩潰的情況,可以在創(chuàng)建數(shù)據(jù)庫時加入fallbackToDestructiveMigration()方法。該方法能在數(shù)據(jù)庫升級異常時,重建數(shù)據(jù)庫而不崩潰,但是表中的數(shù)據(jù)會丟失
val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
//創(chuàng)建臨時表
database.execSQL(
"CREATE TABLE tem_user_info (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_name` TEXT, `last_name` TEXT, `sex` INTEGER DEFAULT 0)"
)
//將數(shù)據(jù)導(dǎo)入臨時表
database.execSQL("INSERT INTO tem_user_info (uid,first_name,last_name,sex) SELECT uid,first_name,last_name,sex FROM user_info")
//刪除原表
database.execSQL("DROP TABLE user_info")
//臨時表改成原表的名字
database.execSQL("ALTER TABLE tem_user_info RENAME TO user_info")
}
}
二、自動遷移
Room 的自動遷移無法檢測到數(shù)據(jù)庫上執(zhí)行的所有可能的變化,因此有時候它們需要一些幫助。舉一個常見的例子,Room 沒辦法檢測到一個數(shù)據(jù)庫表或列是否被重命名或者被刪除。在這種情況下,Room 會拋出一個編譯錯誤,并要求您實現(xiàn) AutoMigrationSpec。此類允許您指定做出更改的類型,實現(xiàn)一個 AutoMigrationSpec 并使用以下一項或多項來注解:
@DeleteTable(tableName)@RenameTable(fromTableName, toTableName)@DeleteColumn(tableName, columnName)@RenameColumn(tableName, fromColumnName, toColumnName)
假設(shè)我們將 User的數(shù)據(jù)庫表重命名為 Users。Room 無法檢測到我們是新建了這個表并刪除了 User 表,還是重命名了它以及要保留所有的值。
@Database(version = 2,
entities = [GoodDoggos.class],
autoMigrations = [AutoMigration(from = 1,
to = 2,
spec = MyDatabase.UsersAutoMigration::class)])
abstract class MyDatabase: RoomDatabase {
@RenameTable(fromTableName = "User", toTableName = "Users")
class UsersAutoMigration: AutoMigrationSpec{}
}