首次开发Room SQLite迁移遇问题:版本从2升3后数据完整性验证失败
刚上手Room数据库迁移就碰到数据完整性验证失败的问题,确实挺闹心的——我帮你梳理几个核心排查方向和解决办法,应该能帮你定位到问题:
先搞懂核心原因
Room的完整性验证本质是对比迁移后的实际数据库结构和当前实体类生成的预期schema,只要两者在表字段、约束、索引等任何细节上不匹配,就会抛出这个错误。你之前解决了Migration didn't properly handle错误,说明单表的字段变更逻辑是对的,但肯定还有没覆盖到的细节。
一步步排查修复
1. 导出新旧schema文件做对比
Room会在app/build/generated/source/kapt/debug/目录下生成对应版本的schema JSON文件(比如2.json和3.json),直接对比这两个文件:
- 检查新增/修改的字段:数据类型、是否可为空、默认值是否和实体类完全一致
- 检查约束:主键、外键、唯一约束有没有变更,但迁移代码没同步处理
- 检查索引:是否新增/删除了索引,但迁移时没执行对应的创建/删除语句
举个例子:如果实体类新增了@ColumnInfo(defaultValue = "0") val status: Int,但迁移代码只写了ALTER TABLE xxx ADD COLUMN status INTEGER,没加上默认值,就会导致schema不一致,触发验证失败。
2. 抠细迁移代码里的SQL语法细节
很多问题出在SQL的小细节上:
- SQLite没有布尔类型,如果实体类用了
Boolean,SQL里必须用INTEGER,不能写BOOLEAN - 低版本SQLite不支持直接
ALTER TABLE RENAME COLUMN,如果需要重命名字段,得先新增字段、复制旧字段数据、删除旧字段,三步走 - 如果涉及外键,要确认关联表的结构是否同步变更,且迁移顺序正确(比如先改父表再改子表,避免外键约束冲突)
3. 临时关闭验证调试(仅用于定位问题,上线前必须恢复)
如果一时找不到问题,可以先关闭Room的完整性验证,看看迁移后的数据是否正常,再反向排查:
Room.databaseBuilder(context, YourDb::class.java, "your_db") .addMigrations(MIGRATION_2_3) .fallbackToDestructiveMigration() // 紧急调试用,会丢失数据,别用于正式环境 .build()
或者更精准地关闭外键验证(调试后要改回来):
.addCallback(object : RoomDatabase.Callback() { override fun onOpen(db: SupportSQLiteDatabase) { super.onOpen(db) db.execSQL("PRAGMA foreign_keys=OFF;") } })
4. 检查隐式的schema变更
有些变更容易被忽略:
- 实体类的
@PrimaryKey是否从单主键改成了复合主键,但迁移代码没处理 @Entity注解里的indices是否新增/修改,但迁移时没创建对应索引- 是否新增/删除了实体类,但迁移代码没同步创建/删除表
5. 手动用工具验证迁移逻辑
用SQLiteStudio这类工具,打开旧版本(v2)的数据库,手动执行你的迁移SQL语句,然后对比生成的数据库结构和v3的schema是否一致——这样能直观看到哪里不匹配。
常见错误示例
比如你的实体类新增了唯一约束:
@Entity(indices = [Index(value = ["email"], unique = true)]) data class User(...)
但迁移代码只加了字段,没创建唯一索引,这就会触发验证失败。这时要在迁移代码里补上:
val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("ALTER TABLE User ADD COLUMN email TEXT") database.execSQL("CREATE UNIQUE INDEX index_User_email ON User(email)") } }
内容的提问来源于stack exchange,提问作者Jopz

