【Jetpack】使用 Room 中的 Migration 升级数据库异常处理 ( 多个数据库版本的迁移 | fallbackToDestructiveMigration() 函数处理升级异常 )

这篇具有很好参考价值的文章主要介绍了【Jetpack】使用 Room 中的 Migration 升级数据库异常处理 ( 多个数据库版本的迁移 | fallbackToDestructiveMigration() 函数处理升级异常 )。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。





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



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


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

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

迁移前保存数据库数据 : 当在应用程序中更改 Room 数据库中的架构时 , 将需要执行数据库迁移以保留旧数据并防止应用程序崩溃 ;

自动运行 : Room Migration 数据库迁移工具 会 自动 创建迁移文件 并将其应用于数据库 , 以使 SQLite 数据库 保持最新架构 ;





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



在原始 版本 1 的数据库中 , 有如下 : id , name , age , 三个字段 ;

@Entity(tableName = "student")
class Student {
    /**
     * @PrimaryKey 设置主键 autoGenerate 为自增
     * @ColumnInfo name 设置列名称 / typeAffinity 设置列类型
     */
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    var id: Int = 0

    /**
     * 姓名字段
     * 数据库表中的列名为 name
     * 数据库表中的类型为 TEXT 文本类型
     */
    @ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)
    lateinit var name: String

    /**
     * 年龄字段
     * 数据库表中的列名为 age
     * 数据库表中的类型为 INTEGER 文本类型
     */
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    var age: Int = 0
}

从数据库版本 1 升级到 数据库版本 2 , 添加了 sex 字段 ;

        /**
         * 数据库版本 1 升级到 版本 2 的迁移类实例对象
         */
        val MIGRATION_1_2: Migration = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                Log.i("Room_StudentDatabase", "数据库版本 1 升级到 版本 2")
                database.execSQL("alter table student add column sex integer not null default 1")
            }
        }

从数据库版本 2 升级到数据库版本 3 , 增加了 degree 字段 ;

        /**
         * 数据库版本 2 升级到 版本 3 的迁移类实例对象
         */
        val MIGRATION_2_3: Migration = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                Log.i("Room_StudentDatabase", "数据库版本 2 升级到 版本 3")
                database.execSQL("alter table student add column degree integer not null default 1")
            }
        }

用户之前运行该数据库 , 有可能安装的是 数据库 版本 1 / 版本 2 / 版本 3 任意一个版本的数据库 ;


数据库 版本 1 -> 数据库 版本 3 升级过程 :

如果用户之前运行的是数据库版本 1 , 那么运行该最新应用时 , 先执行

val MIGRATION_1_2: Migration = object : Migration(1, 2)

迁移对象对应的迁移操作 , 先从数据库版本 1 升级到 数据库版本 2 ;

然后再 执行

val MIGRATION_2_3: Migration = object : Migration(2, 3)

迁移对象对应的迁移操作 , 从数据库版本 2 升级到 数据库版本 3 ;


数据库 版本 2 -> 数据库 版本 3 升级过程 :

如果之前用户手机中的数据库版本是 版本 2 , 那么 运行该最新应用时 , 直接执行

val MIGRATION_2_3: Migration = object : Migration(2, 3)

迁移对象对应的迁移操作 , 从数据库版本 2 升级到 数据库版本 3 ;





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



在上一篇博客 【Jetpack】使用 Room 中的 Migration 升级数据库 ( 修改 Entity 实体类 - 更改数据模型 | 创建 Migration 迁移类 | 修改数据库版本 | 代码示例 ) 中 , 讲解了如何使用 Migration 升级数据库 ;

首先 , 创建 Migration 迁移类 ,

    companion object {
        /**
         * 数据库版本 1 升级到 版本 2 的迁移类实例对象
         */
        val MIGRATION_1_2: Migration = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                Log.i("StudentDatabase", "数据库版本 1 升级到 版本 2")
                database.execSQL("alter table student add column sex integer not null default 1")
            }
        }
	}

然后 , 修改数据库版本 ;

@Database(entities = [Student::class], version = 1, exportSchema = false)
abstract class StudentDatabase: RoomDatabase() {

如果 只在 RoomDatabase 的 @Database 注解上 修改了数据库版本 , 而没有创建对应的 Migration 迁移类 , 那么就会出现 IllegalStateException 异常 ;

报错信息如下 :

2023-06-05 10:47:13.635 E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
    Process: kim.hsl.rvl, PID: 31463
    java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:930)
     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.
        at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:117)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:124)
        at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:435)
        at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:331)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:92)
        at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:53)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
        at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
        at androidx.room.util.DBUtil.query(DBUtil.java:83)
        at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:125)
        at kim.hsl.rvl.StudentDao_Impl$4.call(StudentDao_Impl.java:122)
        at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:930) 
    
    
    --------- beginning of system

【Jetpack】使用 Room 中的 Migration 升级数据库异常处理 ( 多个数据库版本的迁移 | fallbackToDestructiveMigration() 函数处理升级异常 )

处理上述异常需要在 创建 RoomDatabase.Builder 时 , 执行一下 RoomDatabase.Builder#fallbackToDestructiveMigration() 函数 , 之后在使用 Migration 迁移数据库时 , 如果出现异常 , 就会重建数据库表 , 但是之前的数据库数据也相应会被清空 ;

                    // 创建数据库
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        StudentDatabase::class.java,
                        "student_database.db")
                        .addMigrations(MIGRATION_1_2)
                        .addMigrations(MIGRATION_2_3)
                        .fallbackToDestructiveMigration()
                        .allowMainThreadQueries() // Room 原则上不允许在主线程操作数据库
                                                  // 如果要在主线程操作数据库需要调用该函数
                        .build()

再次运行时 , 打印的日志结果如下 :

2023-06-05 10:52:41.757 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: []
2023-06-05 10:52:42.154 I/Room_MainActivity: 插入数据 S1 : Student(id=0, name='Tom', age=18)
2023-06-05 10:52:42.158 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=1, name='Tom', age=18)]
2023-06-05 10:52:42.664 I/Room_MainActivity: 插入数据 S2 : Student(id=0, name='Jerry', age=16)
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)]
2023-06-05 10:52:43.174 I/Room_MainActivity: 更新数据 S2 : Student(id=2, name='Jack', age=60)
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)]
2023-06-05 10:52:43.681 I/Room_MainActivity: 删除数据 id = 1
2023-06-05 10:52:43.683 I/Room_MainActivity: Observer#onChanged 回调, List<Student>: [Student(id=2, name='Jack', age=60)]
2023-06-05 10:52:44.182 I/Room_MainActivity: 主动查询 : LiveData : androidx.room.RoomTrackingLiveData@b957950 , 实际数据 : null
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文章来源地址https://www.toymoban.com/news/detail-474869.html

到了这里,关于【Jetpack】使用 Room 中的 Migration 升级数据库异常处理 ( 多个数据库版本的迁移 | fallbackToDestructiveMigration() 函数处理升级异常 )的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Jetpack 从使用到源码深耕【数据库注解Room 从实践到原理 】(一)

    android 开发中,常见的数据存储的方式,有SharePreference、网络、Sqlite、MMKV、文件、 ContentProvider,其中,SharePreference、MMKV从简单的使用入手,到使用上的经验总结,最终我们借助于源码的深入阅读学习,对其原理也进行了深入的总结。但是,大家也发现了,数据库Sqlite有很多

    2023年04月10日
    浏览(45)
  • Android--Jetpack--数据库Room详解二

    本是青灯不归客,却因浊酒恋红尘 关于Room数据库的基本使用,请参考文章Android--Jetpack--数据库Room详解一-CSDN博客 LiveData与ViewModle的使用,请参考文章Android--Jetpack--LiveData-CSDN博客 我们通过结合Room与LiveData和ViewModle的使用,可以使当我们的数据库发生变化的时候,自动的去更新

    2024年02月04日
    浏览(55)
  • 【Jetpack】使用 Room 框架访问 Android 平台 SQLite 数据库 ( 导入依赖 | 定义 Entity 实体类 | 定义 Dao 数据库访问对象接口 | 定义数据库实例类 )

    对于 Room 框架 来说 , 使用 Java 语言 开发和使用 Kotlin 语言 开发 , 需要在 build.gradle 构建脚本 中进行不同的配置 , 主要有以下两个配置不同 : 应用的插件不同 ; 导入依赖库方式不同 ; 应用插件 应用的插件区别 : 如果使用 Java 语言开发 , 只需要导入 android 插件 ; 如果使用 Kotli

    2024年02月05日
    浏览(62)
  • android jetpack Room的基本使用(java)

    添加依赖 创建表 @Entity表示根据实体类创建数据表,如果有多个主键要使用primaryKeys = {} @ColumnInfo 表示在数据表中的名字 @Ignore 表示不在数据表创建此字段 @PrimaryKey 主键 创建DAO 每一个表都对应一个dao。 创建数据库 创建一个抽象类,设置要创建的数据表,数据版本,数据库名

    2024年02月07日
    浏览(41)
  • 如何使用 .Net Core 实现数据库迁移 (Database Migration)

    当我们在编写基于数据库的应用程序时,随着需求的增加和改变,我们需要升级我们的数据库,变更数据库表的字段,当我们的系统的不同版本被部署到了不同的客户那里,在需要给客户升级时,我们如何实现数据库模式 (schema) 的自动升级呢? 传统的管理办法是针对每个数

    2024年02月05日
    浏览(55)
  • 【Android】Room数据库的使用

    Room 是在 SQLite 的基础上推出的 Android 库,它是 Google 官方对数据库操作的推荐方式。使用 Room 可以更方便、高效地操作 SQLite 数据库。 添加依赖 在使用 Room 之前,需要在项目中添加 Room 相关的依赖。在 build.gradle 文件中添加以下依赖: 在上面的依赖中,我们添加了 room-runti

    2024年02月09日
    浏览(49)
  • 【Jetpack】Room + ViewModel + LiveData 综合使用 ( 核心要点说明 | 组合方式 | 代码示例 )

    在上一篇博客 【Jetpack】使用 Room 框架访问 Android 平台 SQLite 数据库 ( 导入依赖 | 定义 Entity 实体类 | 定义 Dao 数据库访问对象接口 | 定义数据库实例类 ) 中 , 实现了 使用 Room 框架访问 Android 中的 SQLite 数据库的操作 , 每当数据库中的数据发生变化时 , 就需要开启线程 , 重新获

    2024年02月06日
    浏览(48)
  • Android使用kotlin+协程+room数据库的简单应用

    前言:一般主线程(UI线程)中是不能执行创建数据这些操作的,因为等待时间长。所以协程就是为了解决这个问题出现。 第一步:在模块级的build.gradle中引入   好了前期工作ok,正式编写room吧! 第二步:创建表实体  第三部:编写对应的Dao接口  第四步:创建数据库信息

    2024年02月13日
    浏览(50)
  • kotlin中使用Room数据库(包含升降级崩溃处理)

    目录 1.导入依赖库 2.数据实体类 3.数据访问对象 (DAO) 4.数据库类 5.调用DAO里面的“增、删、改、查”方法 6.数据库升降级处理 升级(保存数据库历史数据): 升级(不保存数据库历史数据): 降级(不保存数据库历史数据): 1.导入依赖库 2.数据实体类 3.数据访问对象 (D

    2024年02月16日
    浏览(47)
  • Compose中使用paging3进行列表分页加载Room中的数据

    本文简要记录下流程,代码需要修改后才可以运行。 另外使用paging进行数据加载可以做到数据变动感知,只要数据库数据变动,页面就会自动刷新,无需额外操作 project/build.gradle app/build.gradle ViewModel UI androidx.paging.compose Paging 库概览

    2024年01月23日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包