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

修改Room实体类可空性与添加ColumnInfo后如何迁移数据库?

解决Room数据库迁移崩溃及迁移SQL缺失问题

一、先明确你的实体变更对应的SQL操作

你做的两处修改对应的迁移逻辑要分情况处理:

  1. pucd字段加@ColumnInfo:如果只是标注列名且和原有列名一致,这一步不需要SQL变更;如果是修改了列名(比如原来叫old_pucd),需要执行ALTER TABLE Product RENAME COLUMN old_pucd TO pucd。
  2. updatedAt从可空String改非空:SQLite不支持直接修改列的NOT NULL约束,必须用临时表方案,而且要先处理现有数据里的NULL值:
    • 先创建和新表结构一致的临时表
    • 迁移数据时把NULL的updatedAt替换成默认值(比如空字符串或当前时间)
    • 替换原表

二、关于identity_hash的说明

这个哈希是Room用来校验schema一致性的标记,Room会根据实体、DAO等代码生成唯一哈希,存在room_master_table里。当你修改实体后,新哈希和旧的不匹配,Room就会判定schema不一致,直接触发崩溃(默认没配置迁移时会销毁重建数据库)。setupQueries里的相关语句是Room内部用来维护这个哈希的,你不用手动干预。

三、怎么拿到正确的迁移SQL

  1. 开启schema导出(关键)
    在app模块的build.gradle里加配置,让Room自动生成每个版本的schema文件:
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }
}

每次升级数据库版本号、修改实体后,schemas目录下会生成对应版本的.json文件,里面有完整的schema结构,对比新旧版本的json就能明确需要的迁移SQL。

  1. 手动写迁移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;
  1. 编写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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 23:30:32