HarmonyOS 7 / API 26 RDB 表结构升级怎么防翻车:版本号、事务迁移和回滚检查怎么拆
HarmonyOS 7 / API 26 RDB 表结构升级怎么防翻车版本号、事务迁移和回滚检查怎么拆RDB 表结构升级最怕两件事开发环境没问题老用户一升级就打不开迁移跑到一半失败库里留下半成品。HarmonyOS 7.0.0 / API 26 项目里我会把 RDB 升级当成发布前必须验证的稳定性问题而不是临时改几条 SQL。这篇只拆一个问题表结构升级时版本号、事务迁移和回滚检查怎么一起做才能避免老数据被改坏。运行环境和检查范围项目取值系统版本HarmonyOS 7.0.0满足 HarmonyOS 5.0.0 及以上范围API 版本API 26工程模型Stage 模型开发语言ArkTS数据能力RDB 本地关系型数据库验证目标老版本数据升级后可用迁移失败不留下半成品问题一般怎么发生坏例子通常是发现字段不够用了就直接加一列awaitrdbStore.executeSql(ALTER TABLE recipe ADD COLUMN cover TEXT)awaitrdbStore.executeSql(UPDATE recipe SET cover defaultCover)如果第二句失败表已经被改了数据却没补齐。更麻烦的是代码里没有记录当前库版本下一次启动还可能重复执行。先用脚本把坏迁移挡住我用三个场景做检查没有版本号的坏迁移、正常升级、升级中失败回滚。constcases[{name:bad-rdb-migration,hasSchemaVersion:false,usesTransaction:false,hasRollbackCheck:false},{name:good-v1-to-v2,hasSchemaVersion:true,usesTransaction:true,hasRollbackCheck:true},{name:good-rollback,hasSchemaVersion:true,usesTransaction:true,hasRollbackCheck:true},];functioninspect(item){consterrors[];if(!item.hasSchemaVersion)errors.push(schema version is missing);if(!item.usesTransaction)errors.push(migration is not transactional);if(!item.hasRollbackCheck)errors.push(rollback check is missing);return{...item,passed:errors.length0,errors};}本地验证结果是 2 个通过、1 个失败。失败项就是没有版本号、没有事务、没有回滚检查的迁移写法。{total:3,passed:2,failed:1}第一层先维护 schema 版本号数据库版本不能靠猜。项目里要有明确的 schemaVersion启动时先读当前版本再按版本差异执行迁移。constTARGET_SCHEMA_VERSION2asyncfunctionensureSchema(rdbStore:relationalStore.RdbStore){constcurrentawaitreadSchemaVersion(rdbStore)if(current2){awaitmigrateV1ToV2(rdbStore)}awaitsaveSchemaVersion(rdbStore,TARGET_SCHEMA_VERSION)}这里不要把所有升级都塞到一个大函数里。每个版本到下一个版本单独写一个迁移函数出问题也好定位。第二层迁移必须放进事务表结构和数据补齐要一起成功。只要中间失败就不要留下半成品。asyncfunctionmigrateV1ToV2(rdbStore:relationalStore.RdbStore){awaitrdbStore.beginTransaction()try{awaitrdbStore.executeSql(ALTER TABLE recipe ADD COLUMN cover TEXT)awaitrdbStore.executeSql(UPDATE recipe SET cover ? WHERE cover IS NULL,[default.png])awaitrdbStore.commit()}catch(error){awaitrdbStore.rollBack()throwerror}}事务的意义很直接升级成功就是完整成功失败就回到升级前。第三层迁移后要跑检查迁移执行完不代表数据一定对。至少要查字段是否存在、空值是否补齐、关键索引是否还能用。asyncfunctionverifyRecipeSchema(rdbStore:relationalStore.RdbStore){constcursorawaitrdbStore.querySql(SELECT COUNT(*) AS count FROM recipe WHERE cover IS NULL)cursor.goToFirstRow()constcountcursor.getLong(cursor.getColumnIndex(count))cursor.close()if(count0){thrownewError(recipe.cover still has empty rows)}}这个检查比“启动没报错”更可靠因为它直接验证了迁移目标有没有达成。两个用例怎么验第一个用例是从 v1 升到 v2。准备一份没有 cover 字段的老库升级后检查字段存在、旧数据能打开、默认封面已补齐。第二个用例是迁移中断。故意让 UPDATE 抛错升级后检查事务是否回滚schemaVersion 不能被错误写成目标版本。方案对比方案好处风险启动时直接 ALTER TABLE写得快半成品风险高重复执行风险高只维护版本号能知道迁移进度失败时仍可能留下脏数据版本号 事务 迁移后检查最稳需要写迁移脚本和验证脚本我会选第三种。RDB 升级不是把 SQL 跑完就行关键是老用户数据升级后还能稳定使用。最后怎么避免再出问题以后每改一次表结构我都会同时补三样东西schema 版本号、迁移函数、迁移后检查。没有检查的数据库升级发布前都不能算真正完成。