基于Room Migration处理应用更新中的数据库与文件迁移问题问询
用Migration对象同步处理数据库迁移与JPEG文件重定位
如果你在应用更新时需要同时搞定数据库字段调整和JPEG文件的迁移,用Room的Migration对象来统一处理绝对是个靠谱的方案——既能借助数据库事务保证操作的原子性,又能同步完成文件系统的修改,完全避免数据不一致的坑。
下面是整理后的简化版Migration类实现,补充了完整的核心逻辑:
class VersionOneToTwo(private val appDataDir: File) : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.beginTransaction() try { // 第一步:调整数据库中跟踪文件的表结构(这里以重命名URI列为例) renameUriColumnInMediaTable(database) // 第二步:遍历数据库中的JPEG记录,完成文件重定位并更新数据库 relocateJpegFiles(database) database.setTransactionSuccessful() } finally { database.endTransaction() } } private fun renameUriColumnInMediaTable(database: SupportSQLiteDatabase) { // 执行SQL修改表字段:把旧的URI列重命名为新列 database.execSQL("ALTER TABLE media RENAME COLUMN old_file_uri TO new_file_uri") } private fun relocateJpegFiles(database: SupportSQLiteDatabase) { // 查询所有标记为JPEG的文件记录 val cursor = database.query("SELECT id, old_file_uri FROM media WHERE file_type = 'JPEG'") cursor.use { while (it.moveToNext()) { val fileId = it.getLong(it.getColumnIndexOrThrow("id")) val oldUriPath = it.getString(it.getColumnIndexOrThrow("old_file_uri")) val oldFile = File(appDataDir, oldUriPath) // 构建新的文件存储目录(比如按日期分类存放) val newDir = File(appDataDir, "migrated_jpegs/${SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(Date())}") if (!newDir.exists()) newDir.mkdirs() val newFile = File(newDir, oldFile.name) // 移动文件到新位置 if (oldFile.renameTo(newFile)) { // 同步更新数据库中的文件路径 database.execSQL( "UPDATE media SET new_file_uri = ? WHERE id = ?", arrayOf(newFile.relativeTo(appDataDir).path, fileId) ) } else { // 文件移动失败时抛出异常,触发事务回滚,避免数据不一致 throw IllegalStateException("Failed to relocate JPEG file: ${oldFile.path}") } } } } }
这里有几个关键细节需要注意:
- 事务原子性:整个操作被包裹在数据库事务中,只要数据库修改或文件迁移任一环节出问题,事务就会回滚,不会出现半完成的状态。
- 文件与数据库同步:每完成一个文件的重定位,就立刻更新对应的数据库记录,确保两者状态完全匹配。
- 灵活的错误处理:示例中文件移动失败会直接抛出异常回滚事务,你也可以根据业务需求改成记录错误日志并跳过该文件,具体看你的容错要求。
- 依赖注入:通过构造函数传入
appDataDir,避免在Migration类里硬编码路径,不仅更灵活,也方便写单元测试。
内容的提问来源于stack exchange,提问作者Hadi Satrio
相关产品推荐
相关产品推荐

