
26 数据库版本升级策略从 v1 到 v2 的增量迁移实践前言图26 数据库版本升级策略从 v1 到 v2 的增量迁移实践 运行效果截图HarmonyOS NEXT在应用迭代过程中数据库表结构往往会发生变化——新增字段、创建新表、修改索引。如何在不丢失用户已有数据的前提下优雅地完成数据库迁移是每个应用开发者必须掌握的技能。本文以鹿鹿·笔迹心理分析项目的 [DatabaseService.ets](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/DatabaseService.ets) 中从 v1 到 v2 的实战迁移为例深入解析鸿蒙 RDB 的版本管理机制和增量升级策略。鸿蒙官方·数据库版本管理developer.huawei.com项目源码仓库harmony-app GitHub图鸿蒙 RDB 版本升级策略——增量迁移从 v1 到 vN否版本已是最新是需要升级是否currentVersion 2是否应用启动 initstore.version DB_VERSION?直接使用 RdbStore调用 upgrade 方法currentVersion 1?执行 v1→v2 迁移脚本ALTER TABLE / CREATE TABLEstore.version 2执行 v2→v3 脚本还需继续升级?一、版本管理的核心机制1.1 RdbStore 的版本属性在鸿蒙 RDB 中每打开一个数据库RdbStore实例都会暴露一个version属性constcurrentVersionstore.version各阶段的 version 值场景store.version 值说明新创建的数据库0磁盘上还没有版本号v1 版本的数据库1upgrade(from0,to1) 执行后 v1 1v2 版本的数据库2upgrade(from1,to2) 执行后 v2 2已更新过代码但未赋值的仍为 1需要显式 store.version DB_VERSION关键点store.version读取的是数据库中记录的版本号而非代码中声明的DB_VERSION。正确做法是逐段升级后再赋值。1.2 版本检测与升级入口asyncinit(context:common.UIAbilityContext):Promisevoid{this.storeawaitrelationalStore.getRdbStore(context,DB_CONFIG)// 版本检测store.version 从磁盘读取DB_VERSION 是代码中的最新版awaitthis.upgrade(this.store,this.store.version,DB_VERSION)// 写入最新版本号到数据库this.store.versionDB_VERSION}二、upgrade 方法的设计模式2.1 逐段升级的结构privateasyncupgrade(store:relationalStore.RdbStore,fromVersion:number,toVersion:number):Promisevoid{// 从 v0 到 v1完整建表if(fromVersion1){// 执行 v1 的 DDL// ...}// 从 v1 到 v2新增字段if(fromVersion2){// 执行 v2 的 DDL// ...}// 后续版本追加 if (fromVersion 3) { ... }// if (fromVersion 4) { ... }}增量升级的核心思想每一个版本升级都是添加操作——在现有结构上增加新表或新列确保所有老版本的用户都能平滑升级到最新版。2.2 版本升级路径示例// 场景 Av0 → v2新用户// 当前版本0// 执行fromVersion 1 → 建全部 5 张表// 执行fromVersion 2 → 添加 3 个新列// 最终版本实际版本 2// 场景 Bv1 → v2老用户// 当前版本1// 跳过fromVersion 1 → 表已存在不执行// 执行fromVersion 2 → 添加 3 个新列// 最终版本2// 场景 Cv0 → v1 → v2 → v3跨越多个版本// 每个 if (fromVersion N) 都会依次执行确保不遗漏任何中间版本三、v1 阶段完整建表3.1 DDL 语句fromVersion 1分支负责初始版本的所有建表语句if(fromVersion1){constddls:string[][// 5 张表的 CREATE TABLECREATE TABLE IF NOT EXISTS user (...),CREATE TABLE IF NOT EXISTS archive (...),CREATE TABLE IF NOT EXISTS handwriting (...),CREATE TABLE IF NOT EXISTS report (...),CREATE TABLE IF NOT EXISTS relation (...),// 索引CREATE INDEX IF NOT EXISTS idx_handwriting_archive ON handwriting(archive_id, created_at DESC),CREATE INDEX IF NOT EXISTS idx_report_handwriting ON report(handwriting_id)]for(constsqlofddls){try{awaitstore.executeSql(sql)}catch(e){hilog.error(0x0000,DatabaseService,建表 SQL 执行失败: %s,JSON.stringify(e))thrownewError(建表 SQL 执行失败: JSON.stringify(e))}}}3.2 异常处理关键做法建表失败时立即抛出异常阻止应用继续运行。因为数据库是应用的基础设施表结构不完整时后续所有操作都会失败。尽早报错比在用户使用中途崩溃更好。提示CREATE TABLE IF NOT EXISTS保证了幂等性——多次执行不会报错也不会重复建表。四、v2 阶段字段新增4.1 迁移需求在 v2 版本中需要为 handwriting 表增加 3 个字段device_type设备类型如 ‘Camera’, ‘Tablet’word_count字数统计write_duration书写时长if(fromVersion2){constmigrates:string[][ALTER TABLE handwriting ADD COLUMN device_type TEXT DEFAULT \\,ALTER TABLE handwriting ADD COLUMN word_count INTEGER DEFAULT 0,ALTER TABLE handwriting ADD COLUMN write_duration INTEGER DEFAULT 0]for(constsqlofmigrates){try{awaitstore.executeSql(sql)}catch(e){// 列已存在时静默忽略hilog.error(0x0000,DatabaseService,迁移 SQL 执行失败: %s,JSON.stringify(e))// 不抛出异常列已存在时可安全继续}}}4.2 异常处理的差异阶段异常处理理由v1 建表throw new Error(...)终止应用核心结构缺失无法正常运行v2 迁移日志记录不抛出异常列可能已存在调试期反复创建静默忽略五、字段新增的限制与策略5.1 SQLite ALTER TABLE 限制SQLite 对ALTER TABLE的支持非常有限-- ✅ 支持ALTERTABLEhandwritingADDCOLUMNdevice_typeTEXTDEFAULT-- ❌ 不支持ALTERTABLEhandwritingDROPCOLUMNdevice_type-- SQLite 3.35.0-- ❌ 不支持ALTERTABLEhandwritingRENAMECOLUMNdevice_typeTOdevice-- SQLite 3.25.0-- ❌ 不支持ALTERTABLEhandwritingALTERCOLUMNdevice_typeSETNOTNULL5.2 ALTER TABLE ADD COLUMN 的注意事项// ✅ 正确指定 DEFAULT 值ALTER TABLE handwriting ADD COLUMN device_type TEXT DEFAULT \\ALTER TABLE handwriting ADD COLUMN word_count INTEGER DEFAULT 0ALTER TABLE handwriting ADD COLUMN write_duration INTEGER DEFAULT 0// ❌ 错误NOT NULL 但无 DEFAULT// (SQLite 要求对已有表新增 NOT NULL 列必须指定 DEFAULT)ALTER TABLE handwriting ADD COLUMN device_type TEXT NOT NULL// 会失败5.3 复杂的表结构变更如果需要进行 SQLite 不直接支持的变更如删除列、修改列类型需要采用重建表的策略// 复杂变更策略示例非本项目if(fromVersion3){awaitstore.executeSql(BEGIN TRANSACTION)// 1. 创建新表awaitstore.executeSql(CREATE TABLE handwriting_new (...))// 2. 迁移数据awaitstore.executeSql(INSERT INTO handwriting_new SELECT ... FROM handwriting)// 3. 删除旧表awaitstore.executeSql(DROP TABLE handwriting)// 4. 重命名新表awaitstore.executeSql(ALTER TABLE handwriting_new RENAME TO handwriting)// 5. 重建索引awaitstore.executeSql(CREATE INDEX ...)awaitstore.executeSql(COMMIT)}六、版本号管理规范6.1 版本号的意义// DatabaseService.ets 头部constDB_VERSION2// 每次表结构变化时 1// 版本号历史// v1 初始版本5 张表 2 个索引// v2 handwriting 增加 device_type / word_count / write_duration6.2 版本号更新流程需求变更 → 需要修改表结构 ↓ ① 在 DatabaseService 中修改 DB_VERSION const DB_VERSION 3 ↓ ② 在 upgrade() 方法末尾追加新分支 if (fromVersion 3) { ... } ↓ ③ 更新注释说明版本变化 // v3 handwriting 新增 xxx 字段 ↓ ④ 更新 Models.ets 接口定义 interface HandwritingEntity { xxx?: type } ↓ ⑤ 更新对应 DAO 的 COLS 数组和 parseRow 方法七、调试技巧7.1 日志追踪// DatabaseService.etshilog.info(0x0000,DatabaseService,RDB 初始化完成 v%d,DB_VERSION)// upgrade 阶段hilog.info(0x0000,DatabaseService,建表完成 → v1)// v1→v2hilog.info(0x0000,DatabaseService,迁移完成 → v2)典型日志输出// 首次安装v0 → v2 [DatabaseService] 建表完成 → v1 [DatabaseService] 迁移完成 → v2 [DatabaseService] RDB 初始化完成 v2 // 老版本升级v1 → v2 [DatabaseService] 迁移完成 → v2 [DatabaseService] RDB 初始化完成 v2 // 已升级到 v2再次启动无变更 [DatabaseService] RDB 初始化完成 v27.2 版本号重置开发阶段开发调试时可能需要重置数据库// 方案 A删除数据库文件需要在真机上清除应用数据// 设置 → 应用管理 → 鹿鹿 → 删除数据// 方案 B卸载重装// adb uninstall com.example.lulu// 方案 C递增 DB_VERSION不推荐在正式版使用constDB_VERSION999// 所有已有数据会留在旧数据库中八、向前兼容8.1 v2 字段的兼容处理由于 v2 新增的字段都有DEFAULT值旧版本 DAO 的 COLS 数组也能正常查询// HandwritingDao.ets — COLS 数组包含新字段constCOLS:string[][id,archive_id,source,image_path,ocr_text,feature_json,energy_score,device_type,// ← v2 新增word_count,// ← v2 新增write_duration,// ← v2 新增created_at]8.2 增量升级的优势特性增量升级全量重建用户数据✅ 保留❌ 丢失执行速度✅ 快只需执行迁移 SQL❌ 慢重建所有数据代码复杂度✅ 低追加 if 分支❌ 高需要备份恢复风险✅ 低❌ 高总结本文深入解析了鸿蒙 RDB 数据库版本升级的完整方案版本检测store.version读取磁盘版本与代码中的DB_VERSION对比增量升级if (fromVersion N)逐段执行迁移代码v0→v1 建表使用CREATE TABLE IF NOT EXISTS 索引v1→v2 迁移ALTER TABLE ADD COLUMN DEFAULT 值异常策略建表失败终止应用迁移失败静默忽略向前兼容新增字段默认值、DAO 包含所有列、Entity 接口可选中核心原则每个版本升级都是添加操作不修改已有数据。用户从任何旧版本都能平滑升级到最新版。下一篇文章将深入RdbPredicates 条件查询——鸿蒙 ORM 风格的查询条件构建。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力参考资源relationalStore 版本管理SQLite ALTER TABLE[DatabaseService 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/DatabaseService.ets)CREATE TABLE IF NOT EXISTS鸿蒙数据持久化指南RdbStore 执行 SQL鸿蒙开发文档[HandwritingDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/HandwritingDao.ets)[Models.ets 实体定义](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/Models.ets)你遇到过数据库升级问题吗A. 升级后字段缺失数据读取崩溃B. 版本号遗漏递增升级逻辑未触发C. 旧版本数据格式与新版本不兼容D. 目前没有遇到过问题欢迎在评论区分享你的数据库迁移经历下一篇文章将介绍RdbPredicates 条件查询——如何用链式 API 构建精确的查询条件。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力