HarmonyOS RDB 数据迁移实战:建表升级、事务保护与回滚验证

RDB 真正难的不是 create table,而是版本升级。用户从旧版本直接升级到新版本时,表结构、索引、默认值和历史数据都要一起过关;任何一步失败,都可能导致启动卡死或数据丢失。本文用路线收藏表从 v1 升到 v2 的场景,写一套可回滚、可验证的迁移链路。

请添加图片描述

本文先把迁移失败场景讲清

迁移文章要解决的是升级路径,而不是孤立 SQL。读者需要知道每个版本做了什么、失败后如何回滚、升级后如何证明数据还在。

  • StoreConfig 固定,避免配置漂移。
  • 按当前版本顺序执行迁移。
  • 迁移过程放进事务保护。
  • 升级后跑结构和数据校验。

RDB 资料与声明入口

项目 内容
本地声明 D:/harmonyos/SDK/23/ets/api/@ohos.data.relationalStore.d.ts
核心 API getRdbStore、executeSql、beginTransaction、commit、rollBack。
异常线索 StoreConfig 变化可能触发 14800017。
关闭边界 ResultSet 与 RdbStore 需要按生命周期关闭。

关系存储的版本边界

项目 内容
SDK HarmonyOS SDK 23。
数据库 trail.db,示例版本从 1 升到 2。
场景 路线收藏、离线轨迹索引、搜索字段补充。
边界 大文件和图片不建议直接存入关系表。

请添加图片描述

请添加图片描述

数据库配置不能随意改

数据库名、加密、存储目录等配置一旦发布就要谨慎调整。配置变化可能让 getRdbStore 失败。

import relationalStore from '@ohos.data.relationalStore';

export const TrailDbConfig: relationalStore.StoreConfig = {
  name: 'trail.db',
  securityLevel: relationalStore.SecurityLevel.S1
};

export const TrailDbVersion = 2;

配置文件是数据库入口边界。版本号单独声明,便于启动后和 store.version 比较。

建表 SQL 要具备幂等性

初始化表结构使用 IF NOT EXISTS,避免重复启动或测试环境重建时失败。

export const CreateFavoriteRouteSql = `
CREATE TABLE IF NOT EXISTS favorite_route (
  id TEXT PRIMARY KEY,
  title TEXT NOT NULL,
  city TEXT NOT NULL,
  created_at INTEGER NOT NULL
)`;

export const CreateFavoriteIndexSql = `
CREATE INDEX IF NOT EXISTS idx_favorite_city ON favorite_route(city)`;

建表和索引分开声明。后续迁移可以精确知道哪些结构已经存在。

打开数据库后立即执行版本检查

不要等页面查询失败后再迁移。应用启动打开 RDB 后,先补齐结构再暴露仓储能力。

export async function openTrailStore(context: Context): Promise<relationalStore.RdbStore> {
  const store = await relationalStore.getRdbStore(context, TrailDbConfig);
  await store.executeSql(CreateFavoriteRouteSql);
  await store.executeSql(CreateFavoriteIndexSql);
  await migrateTrailDb(store, store.version, TrailDbVersion);
  store.version = TrailDbVersion;
  return store;
}

打开函数负责结构准备。业务仓储拿到 store 时,表结构已经处于目标版本。

迁移用事务包起来

v2 给收藏路线增加 difficulty 字段,并回填默认值。字段增加和数据回填要么一起成功,要么一起回滚。

async function migrateTrailDb(store: relationalStore.RdbStore, from: number, to: number): Promise<void> {
  if (from < 2 && to >= 2) {
    store.beginTransaction();
    try {
      await store.executeSql('ALTER TABLE favorite_route ADD COLUMN difficulty TEXT DEFAULT "normal"');
      await store.executeSql('UPDATE favorite_route SET difficulty = "normal" WHERE difficulty IS NULL');
      store.commit();
    } catch (err) {
      store.rollBack();
      throw err;
    }
  }
}

事务把结构变化和数据回填绑在一起。失败时抛出异常,启动层可以给出修复提示或降级策略。

插入数据前做实体校验

RDB 不应该替业务兜所有错误。仓储层先校验必填字段,再执行 SQL。

export interface FavoriteRouteEntity {
  id: string;
  title: string;
  city: string;
  createdAt: number;
  difficulty: 'easy' | 'normal' | 'hard';
}

function assertFavorite(route: FavoriteRouteEntity): void {
  if (!route.id || !route.title || !route.city) {
    throw new Error('favorite route fields are required');
  }
}

实体校验阻止坏数据进入数据库。SQL 约束和业务校验一起工作,问题更早暴露。

查询后关闭 ResultSet

结果集不关闭,长时间使用后可能积累资源问题。查询函数应在 finally 中 close。

async function countFavorites(store: relationalStore.RdbStore): Promise<number> {
  const result = await store.querySql('SELECT COUNT(1) AS total FROM favorite_route');
  try {
    result.goToFirstRow();
    return result.getLong(result.getColumnIndex('total'));
  } finally {
    result.close();
  }
}

查询函数只返回业务值,不泄漏 ResultSet。finally 保证异常路径也能释放。

升级后跑结构验收 SQL

迁移完成不代表正确。至少查询字段、索引和样本数据,确认新字段存在且旧数据还在。

async function verifyV2(store: relationalStore.RdbStore): Promise<void> {
  await store.executeSql('SELECT difficulty FROM favorite_route LIMIT 1');
  const total = await countFavorites(store);
  console.info(`[TrailDb] v2 verify total=${total}`);
}

验收 SQL 是迁移后的证据。它能在开发和灰度阶段尽早发现漏迁移。

迁移验证流程:从旧库样本升级到新结构

RDB 迁移建议准备三类样本:空库、只有 v1 表结构的旧库、包含多条真实收藏数据的旧库。升级后分别确认数据库能打开、difficulty 字段存在、旧数据条数不变、索引查询仍然可用。灰度前还要模拟迁移中途抛错,确认事务回滚后不会留下半截字段或半截数据。

async function verifyFavoriteMigration(store: relationalStore.RdbStore): Promise<void> {
  await verifyV2(store);
  const total = await countFavorites(store);
  if (total < 0) {
    throw new Error('favorite count is invalid');
  }
}

验证入口复用前面的结构检查和数量检查。它不修改业务数据,只证明迁移后的库可以被仓储层继续使用。

HarmonyOS RDB 数据迁移实战排查表

现象 优先查看 处理方式
升级后启动失败 StoreConfig 是否变化 保持数据库配置稳定,必要时设计专门迁移。
新增字段查询失败 ALTER TABLE 是否执行 检查 store.version 和迁移顺序。
旧收藏丢失 迁移是否直接重建表 优先 ALTER 和回填,避免 DROP。
内存占用异常 ResultSet 是否 close 查询函数使用 finally 关闭。

HarmonyOS RDB 数据迁移实战验收清单

上线或交付前建议逐项确认,尤其是生命周期、异常分支和数据一致性。

  • StoreConfig 发布后保持稳定。
  • 每个版本迁移都有明确 SQL。
  • 结构变化和数据回填在事务内完成。
  • 升级后执行结构和样本数据验收。
  • ResultSet 与 RdbStore 生命周期清楚。

小结

RDB 迁移要把用户已有数据放在第一位。建表、升级、回填、验证和关闭资源都写清楚,数据库版本升级才不会成为线上事故入口。

参考资料

以下资料用于核对 API 名称和能力边界,落地时请结合项目目标 API 版本复核。

  • HarmonyOS SDK 23 本地 API 声明:@ohos.data.relationalStore.d.ts
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐