Android Room迁移中重构属性:attached_id转list_of_attached JSON列表
Room迁移实现步骤
核心前提:Room的类型转换器仅在ORM层生效,迁移阶段直接操作底层SQLite库,完全感知不到转换器逻辑,所有字段值的格式必须和转换器最终序列化的字符串格式完全对齐。
具体操作流程
- 第一步:调整表结构,新增目标字段
给原表新增list_of_attached TEXT列,这个列就是后续Room用来存序列化后列表JSON的底层字段,对应的SQL为:ALTER TABLE 你的实体对应的表名 ADD COLUMN list_of_attached TEXT; - 第二步:批量迁移历史数据
把所有行原有的attached_id字符串值,拼接成符合要求的列表JSON格式,写入新的list_of_attached字段。
因为是列表结构,所以最终存到这个TEXT字段的JSON是一个仅包含单个目标对象的数组,格式为[{"attached_id": "原attached_id值", "type": "new item"}]。对应的更新SQL需要注意转义原字段里的双引号,避免生成非法JSON导致后续解析崩溃:UPDATE 你的实体对应的表名 SET list_of_attached = '[{"attached_id": "' || replace(IFNULL(attached_id, ''), '"', '\\"') || '", "type": "new item"}]' - 第三步:删除废弃的旧字段
如果你的应用支持的SQLite版本≥3.25.0(对应Android API 30以上,绝大多数场景都满足),可以直接执行删除旧列的SQL:
如果需要兼容更低版本的SQLite环境,就不能直接删列,需要走临时表重建流程:ALTER TABLE 你的实体对应的表名 DROP COLUMN attached_id;- 创建和新表结构完全一致的临时表
- 从旧表导数据到临时表,导数据的同时按照上面的JSON规则拼接生成
list_of_attached字段的值 - 删除旧表
- 把临时表重命名为原来的表名
完整迁移代码示例
// 以从数据库版本2迁移到版本3为例 val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(db: SupportSQLiteDatabase) { // 新增新字段 db.execSQL("ALTER TABLE YourTargetTableName ADD COLUMN list_of_attached TEXT") // 批量迁移历史数据 db.execSQL( """ UPDATE YourTargetTableName SET list_of_attached = '[{"attached_id": "' || replace(IFNULL(attached_id, ''), '"', '\\"') || '", "type": "new item"}]' """.trimIndent() ) // 删除旧字段 db.execSQL("ALTER TABLE YourTargetTableName DROP COLUMN attached_id") } }
关键校验点
- 拼接出来的JSON格式必须和你写的TypeConverter用序列化框架(比如Gson、Moshi)输出的字符串格式完全一致,包括空格、字段顺序、转义规则,避免后续Room读取时出现解析失败。
- 迁移完成后要做全量校验:从旧版本数据库升级到新版本后,逐行检查
list_of_attached字段能不能被TypeConverter正常解析成目标列表对象,有没有空值、格式错误的问题。 - 不要在迁移逻辑里直接调用TypeConverter的转换方法,迁移阶段是裸SQLite操作,没有Room的ORM上下文,直接用SQL拼字符串是最稳定的实现方式。
内容的提问来源于stack exchange,提问作者AmSabba
相关产品推荐
相关产品推荐

