You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Room数据库新增列报错:计算数据库LiveData时发生异常的原因及解决方案

问题:Room数据库升级后Migration不匹配导致崩溃

我定义了包含requirePolice布尔字段的Crime数据类:

@Entity
data class Crime(
    @PrimaryKey val id : UUID = UUID.randomUUID(),
    var title: String = "",
    var date: Date = Date(),
    var isSolved: Boolean = false,
    var requirePolice : Boolean = false, // 新增布尔字段
    var suspect: String = ""
){
    val photoFileName get() = "IMG_$id.jpg"
}

将数据库版本从2升级到3并添加迁移:

@Database(entities = [Crime::class], version = 3)
@TypeConverters(CrimeTypeConverters::class)
abstract class CrimeDatabase: RoomDatabase(){
    abstract fun crimeDao() : CrimeDao
}

val migration_1_2 = object : Migration(1,2){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Crime ADD COLUMN suspect TEXT NOT NULL DEFAULT ''")
    }
}

val migration_2_3 = object : Migration(2,3){
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE Crime ADD COLUMN requirePolice TEXT NOT NULL DEFAULT ''")
    }
}

在Repository中配置迁移后,运行时出现崩溃,错误日志如下:

E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
Process: com.bignerdranch.android.criminalintent, PID: 22566
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:923)
Caused by: java.lang.IllegalStateException: Migration didn't properly handle: Crime(com.bignerdranch.android.criminalintent.Crime).
Expected:
TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
Found:
TableInfo{name='Crime', columns={date=Column{name='date', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, requirePolice=Column{name='requirePolice', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''}, isSolved=Column{name='isSolved', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=1, defaultValue='null'}, suspect=Column{name='suspect', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, title=Column{name='title', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
    at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:103)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:177)
    at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:416)
    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:316)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
    at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
    at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:731)
    at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:508)
    at androidx.room.RoomDatabase.query(RoomDatabase.java:551)
    at androidx.room.util.DBUtil.query(DBUtil.java:83)
    at com.bignerdranch.android.criminalintent.database.CrimeDao_Impl$3.call(CrimeDao_Impl.java:158)
    at com.bignerdranch.android.criminalintent.database.CrimeDao_Impl$3.call(CrimeDao_Impl.java:155)
    at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
    ... 3 more

错误原因分析

看日志里的Expected和Found对比就能发现核心问题:

  • 你的Crime数据类中requirePolice是Boolean类型,Room会自动将Boolean映射为SQLite的INTEGER类型(用0代表false,1代表true)
  • 但你写的迁移SQL把这个字段创建成了TEXT类型,还设置了默认值为空字符串'',这和Room预期的表结构完全不匹配,所以触发了Migration验证失败的异常。

解决方案

1. 修正迁移SQL语句

把migration_2_3中的SQL改成匹配Room预期的类型和默认值:

val migration_2_3 = object : Migration(2,3){
    override fun migrate(database: SupportSQLiteDatabase) {
        // Boolean对应SQLite的INTEGER,默认值0对应false
        database.execSQL("ALTER TABLE Crime ADD COLUMN requirePolice INTEGER NOT NULL DEFAULT 0")
    }
}

2. 清理测试设备上的旧数据库

如果你已经在测试设备上运行过错误的迁移版本,数据库中已经创建了错误类型的字段,那么需要卸载当前应用后重新运行——因为SQLite不支持修改已存在字段的类型,开发阶段这是最快捷的清理方式。

3. 优化数据库构建器配置

可以把多个迁移合并到一个addMigrations调用中,代码更简洁:

private val database: CrimeDatabase = Room.databaseBuilder(
    context.applicationContext,
    CrimeDatabase::class.java,
    DATABASE_NAME
)
.addMigrations(migration_1_2, migration_2_3)
.build()

完成以上修改后,Room就能正确匹配数据类和数据库表的结构,迁移崩溃问题就会解决。


内容的提问来源于stack exchange,提问作者Saher Al-Sous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 17:42:48