Android Room预填充数据库迁移 无需大量SQL且保留用户数据的方案咨询
解决方案:附加预填充库整表迁移
该方案不需要手动编写任何插入语句,也不会破坏旧表用户数据,全程可自动化实现。
核心原理是在Migration执行过程中,将assets中的新版预填充数据库临时附加到当前用户数据库,直接批量迁移新增表的结构和全部数据。
实现步骤
1. 前置校验
确保你assets中新版预填充库的3张新增表结构,和你新增的Room实体类定义完全一致,避免后续Room抛出schema不匹配异常。你可以直接从Room自动生成的schemas目录下的json文件中导出建表语句,和预填充库的表结构做比对。
2. 编写1->2的Migration逻辑
val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { // 1. 将assets中的新版预填充库拷贝到应用私有缓存目录 val cachePrepopDb = File(context.cacheDir, "new_prepop.db") context.assets.open("new_prepop.db").use { input -> cachePrepopDb.outputStream().use { output -> input.copyTo(output) } } // 2. 附加预填充库到当前数据库 database.execSQL("ATTACH DATABASE '${cachePrepopDb.absolutePath}' AS prepop_db") // 3. 批量迁移所有新增表 val newTables = listOf("table_a", "table_b", "table_c") newTables.forEach { tableName -> // 无特殊约束的表可以直接从预填充库整表导入,建表+插数据一步完成 database.execSQL("CREATE TABLE $tableName AS SELECT * FROM prepop_db.$tableName") // 如果表有主键、索引、外键约束,替换为下面两句: // 建表语句直接从Room生成的schema文件复制即可,不用手动编写 // database.execSQL("/* Room生成的对应表的CREATE TABLE语句 */") // database.execSQL("INSERT INTO $tableName SELECT * FROM prepop_db.$tableName") } // 4. 卸载附加库,删除临时文件 database.execSQL("DETACH DATABASE prepop_db") cachePrepopDb.delete() } }
3. 注册Migration
把上面的Migration添加到你的Room数据库Builder中即可:
Room.databaseBuilder(context, AppDatabase::class.java, "app.db") .addMigrations(MIGRATION_1_2) .createFromAsset("old_prepop.db") // 原来的预填充配置保留即可 .build()
注意事项
- 如果新增表使用自增主键,导入完成后需要同步主键序列,避免后续插入冲突:
UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM 表名) WHERE name = '表名' - 如果后续需要再次升级新增预填充表,只需要更新assets中的预填充库,在对应版本的Migration中复用上述逻辑即可,无需维护大量插入语句。
内容的提问来源于stack exchange,提问作者x-devel
相关产品推荐
相关产品推荐

