修改Room实体类可空性与添加ColumnInfo后如何迁移数据库?
解决Room数据库迁移崩溃及迁移SQL缺失问题
一、先明确你的实体变更对应的SQL操作
你做的两处修改对应的迁移逻辑要分情况处理:
- pucd字段加@ColumnInfo:如果只是标注列名且和原有列名一致,这一步不需要SQL变更;如果是修改了列名(比如原来叫
old_pucd),需要执行ALTER TABLE Product RENAME COLUMN old_pucd TO pucd。 - updatedAt从可空String改非空:SQLite不支持直接修改列的NOT NULL约束,必须用临时表方案,而且要先处理现有数据里的NULL值:
- 先创建和新表结构一致的临时表
- 迁移数据时把NULL的
updatedAt替换成默认值(比如空字符串或当前时间) - 替换原表
二、关于identity_hash的说明
这个哈希是Room用来校验schema一致性的标记,Room会根据实体、DAO等代码生成唯一哈希,存在room_master_table里。当你修改实体后,新哈希和旧的不匹配,Room就会判定schema不一致,直接触发崩溃(默认没配置迁移时会销毁重建数据库)。setupQueries里的相关语句是Room内部用来维护这个哈希的,你不用手动干预。
三、怎么拿到正确的迁移SQL
- 开启schema导出(关键)
在app模块的build.gradle里加配置,让Room自动生成每个版本的schema文件:
android { defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] } } } }
每次升级数据库版本号、修改实体后,schemas目录下会生成对应版本的.json文件,里面有完整的schema结构,对比新旧版本的json就能明确需要的迁移SQL。
- 手动写迁移SQL(针对你的场景)
针对updatedAt的非空修改,完整的SQL逻辑如下:
-- 1. 创建临时表,复制新的Product表结构 CREATE TABLE Product_new ( -- 这里要把Product表的所有字段按新结构写全,注意updatedAt加NOT NULL id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, pucd TEXT, -- 按你的@ColumnInfo配置调整类型/约束 createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL, -- 其他字段原样照搬 ); -- 2. 迁移旧表数据,处理NULL的updatedAt INSERT INTO Product_new (id, pucd, createdAt, updatedAt, ...) SELECT id, pucd, createdAt, COALESCE(updatedAt, '') -- 把NULL替换成空字符串,或你需要的默认值 -- 其他字段对应写上 FROM Product; -- 3. 删除旧表,替换为新表 DROP TABLE Product; ALTER TABLE Product_new RENAME TO Product;
- 编写Room迁移类
把上面的SQL放进Migration类:
// 替换N和N+1为你的旧版本号和新版本号 val MIGRATION_N_TO_N1 = object : Migration(N, N+1) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE Product_new (...)") // 补全完整表结构 database.execSQL("INSERT INTO Product_new (...) SELECT ... FROM Product") // 补全字段和逻辑 database.execSQL("DROP TABLE Product") database.execSQL("ALTER TABLE Product_new RENAME TO Product") // 如果pucd改了列名,再加这句: // database.execSQL("ALTER TABLE Product RENAME COLUMN old_pucd TO pucd") } }
然后在初始化RoomDatabase时添加迁移:
Room.databaseBuilder(context, AppDatabase::class.java, "你的数据库名") .addMigrations(MIGRATION_N_TO_N1) .build()
四、崩溃原因排查
升级后崩溃大概率是这几个原因:
- 没加对应的Migration,Room默认销毁重建数据库,可能因数据量或其他逻辑导致崩溃;
- 迁移SQL没处理
updatedAt的NULL值,插入临时表时触发NOT NULL约束错误; - 数据库版本号没正确升级,或者迁移逻辑和实体变更不匹配导致哈希校验失败。
内容的提问来源于stack exchange,提问作者c-an
相关产品推荐
相关产品推荐

