【Jetpack】利用 Room 中的 Migration 升级数据库异常处置惩罚 ( 多个数据 ...

打印 上一主题 下一主题

主题 1026|帖子 1026|积分 3078

一、Room#Migration 迁徙工具升级数据库



Room Migration 数据库迁徙工具 Android Jetpack Architecture Components ( 架构组件 ) 的一部分 , 它是一个方便的 数据库迁徙工具 , 用于为 Android 中利用 Room 框架创建的数据库 提供 自动化迁徙方案 ;

Room Migration 数据库迁徙工具用途如下 :


  • 数据库修改 : 修改数据库表结构 ;
  • 迁徙代码 : 为每个数据库版本编写 迁徙代码 ;
  • 自动更新 : 执行应用时 自动 检测数据库版本号 并 自动举行数据迁徙 ;

迁徙前生存数据库数据 : 当在应用步调中更改 Room 数据库中的架构时 , 将须要执行数据库迁徙以保留旧数据并防止应用步调崩溃 ;
自动运行 : Room Migration 数据库迁徙工具 会 自动 创建迁徙文件 并将其应用于数据库 , 以使 SQLite 数据库 保持最新架构 ;




二、多个数据库版本的迁徙



在原始 版本 1 的数据库中 , 有如下 : id , name , age , 三个字段 ;
  1. @Entity(tableName = "student")
  2. class Student {
  3.     /**
  4.      * @PrimaryKey 设置主键 autoGenerate 为自增
  5.      * @ColumnInfo name 设置列名称 / typeAffinity 设置列类型
  6.      */
  7.     @PrimaryKey(autoGenerate = true)
  8.     @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
  9.     var id: Int = 0
  10.     /**
  11.      * 姓名字段
  12.      * 数据库表中的列名为 name
  13.      * 数据库表中的类型为 TEXT 文本类型
  14.      */
  15.     @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
  16.     lateinit var name: String
  17.     /**
  18.      * 年龄字段
  19.      * 数据库表中的列名为 age
  20.      * 数据库表中的类型为 INTEGER 文本类型
  21.      */
  22.     @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
  23.     var age: Int = 0
  24. }
复制代码
从数据库版本 1 升级到 数据库版本 2 , 添加了 sex 字段 ;
  1.         /**
  2.          * 数据库版本 1 升级到 版本 2 的迁移类实例对象
  3.          */
  4.         val MIGRATION_1_2: Migration = object : Migration(1, 2)
  5. {
  6.             override fun migrate(database: SupportSQLiteDatabase) {
  7.                 Log.i("Room_StudentDatabase", "数据库版本 1 升级到 版本 2")
  8.                 database.execSQL("alter table student add column sex integer not null default 1")
  9.             }
  10.         }
复制代码
从数据库版本 2 升级到数据库版本 3 , 增加了 degree 字段 ;
  1.         /**
  2.          * 数据库版本 2 升级到 版本 3 的迁移类实例对象
  3.          */
  4.         val MIGRATION_2_3: Migration = object : Migration(2, 3)
  5. {
  6.             override fun migrate(database: SupportSQLiteDatabase) {
  7.                 Log.i("Room_StudentDatabase", "数据库版本 2 升级到 版本 3")
  8.                 database.execSQL("alter table student add column degree integer not null default 1")
  9.             }
  10.         }
复制代码
用户之前运行该数据库 , 有大概安装的是 数据库 版本 1 / 版本 2 / 版本 3 恣意一个版本的数据库 ;

数据库 版本 1 -> 数据库 版本 3 升级过程 :
如果用户之前运行的是数据库版本 1 , 那么运行该最新应用时 , 先执行
  1. val MIGRATION_1_2: Migration = object : Migration(1, 2)
复制代码
迁徙对象对应的迁徙操作 , 先从数据库版本 1 升级到 数据库版本 2 ;
然后再 执行
  1. val MIGRATION_2_3: Migration = object : Migration(2, 3)
复制代码
迁徙对象对应的迁徙操作 , 从数据库版本 2 升级到 数据库版本 3 ;

数据库 版本 2 -> 数据库 版本 3 升级过程 :
如果之前用户手机中的数据库版本是 版本 2 , 那么 运行该最新应用时 , 直接执行
  1. val MIGRATION_2_3: Migration = object : Migration(2, 3)
复制代码
迁徙对象对应的迁徙操作 , 从数据库版本 2 升级到 数据库版本 3 ;




三、数据库异常处置惩罚 - RoomDatabase.Builder#fallbackToDestructiveMigration() 函数



在上一篇博客 【Jetpack】利用 Room 中的 Migration 升级数据库 ( 修改 Entity 实体类 - 更改数据模型 | 创建 Migration 迁徙类 | 修改数据库版本 | 代码示例 ) 中 , 讲授了怎样利用 Migration 升级数据库 ;
起首 , 创建 Migration 迁徙类 ,
  1.     companion object {        /**         * 数据库版本 1 升级到 版本 2 的迁徙类实例对象         */        val MIGRATION_1_2: Migration = object : Migration(1, 2)
  2. {            override fun migrate(database: SupportSQLiteDatabase) {                Log.i("StudentDatabase", "数据库版本 1 升级到 版本 2")                database.execSQL("alter table student add column sex integer not null default 1")            }        }        }
复制代码
然后 , 修改数据库版本 ;
  1. @Database(entities = [Student::class], version = 1, exportSchema = false)
  2. abstract class StudentDatabase: RoomDatabase() {
复制代码

如果 只在 RoomDatabase 的 @Database 注解上 修改了数据库版本 , 而没有创建对应的 Migration 迁徙类 , 那么就会出现 IllegalStateException 异常 ;
报错信息如下 :
  1. 2023-06-05 10:47:13.635 E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
  2.     Process: kim.hsl.rvl, PID: 31463
  3.     java.lang.RuntimeException: Exception while computing database live data.
  4.         at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
  5.         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
  6.         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
  7.         at java.lang.Thread.run(Thread.java:930)
  8.      Caused by: java.lang.IllegalStateException: A migration from 2 to 4 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration ...) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
  9.         at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:117)
  10.         at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:124)
  11.         at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:435)
  12.         at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:331)
  13.         at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
  14.         at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
  15.         at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
  16.         at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
  17.         at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
  18.         at androidx.room.util.DBUtil.query(DBUtil.java:83)
  19.         at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:125)
  20.         at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:122)
  21.         at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
  22.         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
  23.         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
  24.         at java.lang.Thread.run(Thread.java:930)
  25.    
  26.    
  27.     --------- beginning of system
复制代码

处置惩罚上述异常须要在 创建 RoomDatabase.Builder 时 , 执行一下 RoomDatabase.Builder#fallbackToDestructiveMigration() 函数 , 之后在利用 Migration 迁徙数据库时 , 如果出现异常 , 就会重建数据库表 , 但是之前的数据库数据也相应会被清空 ;
  1.                     // 创建数据库
  2.                     instance = Room.databaseBuilder(
  3.                         context.applicationContext,
  4.                         StudentDatabase::class.java,
  5.                         "student_database.db")
  6.                         .addMigrations(MIGRATION_1_2)
  7.                         .addMigrations(MIGRATION_2_3)
  8.                         .fallbackToDestructiveMigration()
  9.                         .allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
  10.                                                   // 如果要在主线程操作数据库需要调用该函数
  11.                         .build()
复制代码
再次运行时 , 打印的日记结果如下 :
  1. 2023-06-05 10:52:41.757 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: []
  2. 2023-06-05 10:52:42.154 I/Room_MainActivity: 插入数据 S1 : Student(id=0, name='Tom', age=18)
  3. 2023-06-05 10:52:42.158 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18)]
  4. 2023-06-05 10:52:42.664 I/Room_MainActivity: 插入数据 S2 : Student(id=0, name='Jerry', age=16)
  5. 2023-06-05 10:52:42.666 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18), Student(id=2, name='Jerry', age=16)]
  6. 2023-06-05 10:52:43.174 I/Room_MainActivity: 更新数据 S2 : Student(id=2, name='Jack', age=60)
  7. 2023-06-05 10:52:43.176 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18), Student(id=2, name='Jack', age=60)]
  8. 2023-06-05 10:52:43.681 I/Room_MainActivity: 删除数据 id = 1
  9. 2023-06-05 10:52:43.683 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=2, name='Jack', age=60)]
  10. 2023-06-05 10:52:44.182 I/Room_MainActivity: 主动查询 : LiveData : androidx.room.RoomTrackingLiveData@b957950 , 实际数据 : null
  11. 2023-06-05 10:52:44.183 I/Room_MainActivity: 主动查询2 : [Student(id=2, name='Jack', age=60)]
复制代码
第一行打印的日记是 Observer#onChanged 回调, List<Student>: [] , 当前数据库是空的 , 之前的数据都被清空 , 此时打印的日记都是本次应用运行时新插入的数据 ;




四、完整代码示例



代码地址 : https://github.com/han1202012/Room_ViewModel_LiveData

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

傲渊山岳

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表