? ? ? ?App迭代過程中,難免需要對數據庫進行更新,有可能是字段的增加,修改或刪除,也有可能需要新建一張表,這就涉及到數據庫的遷移Migrate,最簡單粗暴的方法是在Room的配置項中添加fallbackToDestructiveMigration()
方法,如下
Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java, "exercise.db")
.fallbackToDestructiveMigration() //添加此方法
.build()
? ? ? ?為什么說是簡單粗暴呢,因為它是直接刪除之前的數據庫,重新再創建新的數據庫,這在大部分情況下絕對是災難性的做法,所以更好的做法是addMigrations(Migration... migrations)
,具體以下步驟:
一、version升級+1 (我這邊是3 -> 4)
@Database(entities = [HistoryEntity::class,CollectEntity::class], version = 4)
abstract class AppDatabase: RoomDatabase() {
//code
}
二、自定義Migration,如下三種情況:
1.增加一個字段update_time
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE history ADD COLUMN update_time INTEGER NOT NULL DEFAULT 0")
}
}
2.創建一個新表collect
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE collect (collect_id INTEGER NOT NULL DEFAULT 0,line_name_collect TEXT NOT NULL DEFAULT '', upper_or_down_collect TEXT NOT NULL DEFAULT '', PRIMARY KEY(collect_id))")
}
}
3.更改一個字段類型
private val MIGRATION_4_3: Migration = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
//創建一個備份的新表
database.execSQL(
"CREATE TABLE collect_new_one (collect_id INTEGER NOT NULL DEFAULT 0,line_name_collect TEXT NOT NULL DEFAULT '', upper_or_down_collect TEXT NOT NULL DEFAULT '', PRIMARY KEY(collect_id))")
// 把舊表的數據拷貝到新表
database.execSQL(
"INSERT INTO collect_new_one (collect_id , line_name_collect, upper_or_down_collect) SELECT collect_id, line_name_collect, upper_or_down_collect FROM collect")
// 刪除掉舊表
database.execSQL("DROP TABLE collect")
// 重命名新表(完成謀權篡位)
database.execSQL("ALTER TABLE collect_new_one RENAME TO collect")
}
}
三、將自定義好的Migration加入配置
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "exercise.db"
) .addMigrations(MIGRATION_1_2,MIGRATION_2_3,MIGRATION_3_4)
.build()
? ? ? ?這就是完整的Room數據庫遷移,但是這當中可能會碰到幾個異常報錯,比如
Room Database Migration doesnt properly handle ALTER TABLE migration
解決方法可以定位到我第一個例子(增加一個字段)的sql語句,在后面加入INTEGER NOT NULL DEFAULT 0
保證當它為null時賦予0,否則Room會直接拋異常;其他的報錯基本能從報錯信息理解出來。
? ? ? ?順便曬出AppDatabase
代碼
@Database(entities = [HistoryEntity::class, CollectEntity::class], version = 4)
abstract class AppDatabase : RoomDatabase() {
abstract fun historyDao(): HistoryDao
abstract fun collectDao(): CollectionDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(): AppDatabase =
INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(App.instance).also { INSTANCE = it }
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java, "exercise.db"
).addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
.build()
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE history ADD COLUMN update_time INTEGER NOT NULL DEFAULT 0")
}
}
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE collect (collect_id INTEGER NOT NULL DEFAULT 0,line_name_collect TEXT NOT NULL DEFAULT '', upper_or_down_collect TEXT NOT NULL DEFAULT '', PRIMARY KEY(collect_id))"
)
}
}
private val MIGRATION_3_4: Migration = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE collect_new_one (collect_id INTEGER NOT NULL DEFAULT 0,line_name_collect TEXT NOT NULL DEFAULT '', upper_or_down_collect TEXT NOT NULL DEFAULT '', PRIMARY KEY(collect_id))"
)
database.execSQL(
"INSERT INTO collect_new_one (collect_id , line_name_collect, upper_or_down_collect) SELECT collect_id, line_name_collect, upper_or_down_collect FROM collect"
)
database.execSQL("DROP TABLE collect")
database.execSQL("ALTER TABLE collect_new_one RENAME TO collect")
}
}
}
}
總結
? ? ? ?這是我自己練手項目中截取的一個記錄,項目里使用到jetpack,后面有時間還會一一記錄與分享,對于Room的簡單使用上總體感覺還算方便快速,就是這個數據遷移會踩點坑,比較麻煩