Room數據庫遷移

? ? ? ?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的簡單使用上總體感覺還算方便快速,就是這個數據遷移會踩點坑,比較麻煩

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。