异常处理

=======================================================================

如果我们将数据库升级到了3,但是缺没有写对应的migration,那么使用的时候room直接回抛出IllagelStateException。因为Room在升级过程中没有匹配到相应的Migration。为了防止出现升级失败导致应用程序崩溃的情况,可以在创建数据库时加入fallbackToDestructiveMigration()方法。该方法能够在出现升级异常时,重新创建数据表。**需要注意的是,虽然应用程序不会崩溃,但由于数据表被重新创建,所有的数据也将会丢失。**如下所示:

Room.databaseBuilder(

AppUtil.application,

StudentDataBase::class.java,

STUDENT_DB_NAME

)

.addMigrations(MIGRATION_1_2,MIGRATION_2_3)

.fallbackToDestructiveMigration()

.build()

实战

=====================================================================

本例子基于前面预先创建好的学生数据库表,进行两次升级,第一次升级增加Fruit表,第二次在Fruit表中新增字段。可参考Jetpack:Room超详细使用踩坑指南!Jetpack:Room配合LiveData/Flow使用优化,Room+Flow使用原理解析。

1.创建Fruit表实体类

@Entity(tableName = FRUIT_TABLE_NAME)

data class FruitEntity(

@PrimaryKey(autoGenerate = true)

@ColumnInfo(name = FRUIT_TABLE_ID)

val id: Int = 0,

@ColumnInfo(name = FRUIT_TABLE_TEXT)

val text: String?

)

/**

  • 表名字相关,统一定义.

*/

const val FRUIT_TABLE_NAME = “fruit”

const val FRUIT_TABLE_ID = “fruit_id”

const val FRUIT_TABLE_TEXT = “fruit_name”

2.在数据升级的声明的Database注解中,新加入Fruit实体类,升级版本为2。

//之前

@Database(entities = arrayOf(StudentEntity::class), version = 1)

abstract class StudentDataBase : RoomDatabase()

//新增FruitEntity实体类 升级版本号

@Database(entities = arrayOf(StudentEntity::class,FruitEntity::class), version = 2)

abstract class StudentDataBase : RoomDatabase()

3.新增migration,同事设置进addMigration方法。

/**

  • 数据库升级 1 到 2

*/

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

override fun migrate(database: SupportSQLiteDatabase) {

//新增 FRUIT 表

database.execSQL(“CREATE TABLE IF NOT EXISTS $FRUIT_TABLE_NAME ($FRUIT_TABLE_ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, $FRUIT_TABLE_TEXT TEXT)”)

}

}

//修改数据库声明,将MIGRATION_1_2 设置进addMigrations

fun getDataBase(): StudentDataBase {

return INSTANT ?: synchronized(this) {

INSTANT ?: Room.databaseBuilder(

AppUtil.application,

StudentDataBase::class.java,

STUDENT_DB_NAME

).addMigrations(MIGRATION_1_2)

.fallbackToDestructiveMigration()

.build()

.also {

INSTANT = it

}

}

}

写Dao接口,测试:

@Dao

interface ConflateDao {

@Query(“select * from $FRUIT_TABLE_NAME”)

suspend fun obtainFruit() : List

@Insert

suspend fun insertFruit(fruitEntity: FruitEntity)

}

//StudentDataBase中获取Dao

abstract fun getConflateEntityDao():ConflateDao

//activity 测试代码

btnTransactionInsertGet.text = “MIGRATION TEST”

btnTransactionInsertGet.setOnClickListener {

val conflateEntityDao = StudentDataBase.getDataBase().getConflateEntityDao()

lifecycleScope.launch {

StudentDataBase.getDataBase().withTransaction {

conflateEntityDao.insertFruit(FruitEntity(text = “apple”)

val obtainFruit = conflateEntityDao.obtainFruit()

withContext(Dispatchers.Main.immediate){

binding.text.text = obtainFruit.toString()

}

}

}

}

如此,第一次升级新增表就可以了。

下面将数据库由2升级到3,在FruitEntity中新增一个字段

1.新增text2字段在FruitEntity中

@ColumnInfo(name = FRUIT_TABLE_OTHER_NAME)

val text2: String?

const val FRUIT_TABLE_OTHER_NAME = “fruit_other_name”

2.StudentDataBase升级为3,新增MIGRATION_2_3,并且加入addMigrations中

//升级version 为3

@Database(entities = arrayOf(StudentEntity::class,FruitEntity::class), version = 3)

abstract class StudentDataBase : RoomDatabase()

//新增MIGRATION_2_3

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

override fun migrate(database: SupportSQLiteDatabase) {

//FRUIT 表 新增一列

database.execSQL("ALTER TABLE $FRUIT_TABLE_NAME ADD COLUMN $FRUIT_TABLE_OTHER_NAME TEXT ")

}

}

//加入addMigrations中

Room.databaseBuilder(

AppUtil.application,

StudentDataBase::class.java,

STUDENT_DB_NAME

)

.addMigrations(MIGRATION_1_2,MIGRATION_2_3)

.fallbackToDestructiveMigration()

.build()

3.测试代码修改,多传入text2字段的值。

conflateEntityDao.insertFruit(FruitEntity(text = “apple”,text2 = “other apple2”))

val obtainFruit = conflateEntityDao.obtainFruit()

withContext(Dispatchers.Main.immediate){

binding.text.text = obtainFruit.toString()

}

这个数据库由2到3也升级完成了。注意字段声明为可空类型,这样的数据没有text2字段,对应的返回结果为null

扩展知识

=======================================================================

在Sqlite中修改表结构比较麻烦。例如,我们想将Student表中的age字段类型从INTEGER改为TEXT。

最好的方式是采用销毁与重建策略,该策略大致分为以下几个步骤。

  1. 创建符合要求的临时表,比如:temp_student.
最后笔者收集整理了一份Flutter高级入门进阶资料PDF

以下是资料目录和内容部分截图



里面包括详细的知识点讲解分析,带你一个星期入门Flutter。还有130个进阶学习项目实战视频教程,让你秒变大前端。


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
_student.

最后笔者收集整理了一份Flutter高级入门进阶资料PDF

以下是资料目录和内容部分截图

[外链图片转存中…(img-pYMVloSx-1714761893331)]
[外链图片转存中…(img-WVdh4RoU-1714761893332)]
里面包括详细的知识点讲解分析,带你一个星期入门Flutter。还有130个进阶学习项目实战视频教程,让你秒变大前端。

[外链图片转存中…(img-n6MBkAwJ-1714761893333)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐