HarmonyOS鸿蒙Next中在开发原生应用时,如何优雅地实现RelationalStore数据库的初始化、表结构创建、索引优化以及数据库版本升级?

HarmonyOS鸿蒙Next中在开发原生应用时,如何优雅地实现RelationalStore数据库的初始化、表结构创建、索引优化以及数据库版本升级? 在实际项目中会遇到以下问题:

  1. 数据库初始化流程不清晰
  2. 数据库版本升级时如何保证数据安全
  3. 如何优化数据库查询性能
  4. 如何处理数据库升级失败的回滚
3 回复

基于真实的人情管理系统项目,分享一套完整的数据库管理解决方案。

技术要点

  • RelationalStore数据库管理
  • 数据库版本升级机制
  • 事务处理与回滚
  • 索引优化策略
  • 单例模式应用

完整实现代码

/**
 * 数据库管理器
 * 负责数据库的创建、升级和数据操作
 */

import { relationalStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';

export class DatabaseManager {
  private static instance: DatabaseManager;
  private store: relationalStore.RdbStore | null = null;
  private readonly DB_NAME = 'lelv_human_relations.db';
  private readonly DB_VERSION = 2; // 当前数据库版本
  private readonly CURRENT_VERSION_KEY = 'database_version';

  private constructor() {}

  /**
   * 获取单例实例
   */
  public static getInstance(): DatabaseManager {
    if (!DatabaseManager.instance) {
      DatabaseManager.instance = new DatabaseManager();
    }
    return DatabaseManager.instance;
  }

  /**
   * 初始化数据库
   */
  public async initDatabase(context: common.UIAbilityContext): Promise<void> {
    try {
      console.info('开始初始化数据库...');
      
      const config: relationalStore.StoreConfig = {
        name: this.DB_NAME,
        securityLevel: relationalStore.SecurityLevel.S1
      };

      // 创建数据库连接
      this.store = await relationalStore.getRdbStore(context, config);
      console.info('数据库连接创建成功');
      
      // 创建数据表
      await this.createTables();
      
      // 检查数据库版本并执行升级
      await this.checkAndUpgradeDatabase();
      
      console.info('数据库初始化成功');
    } catch (error) {
      console.error('数据库初始化失败:', JSON.stringify(error));
      throw new Error(`数据库初始化失败: ${error.message}`);
    }
  }

  /**
   * 创建数据表
   */
  private async createTables(): Promise<void> {
    if (!this.store) {
      throw new Error('数据库未初始化');
    }

    // 创建人物表
    const createPersonTable = `
      CREATE TABLE IF NOT EXISTS lelv_persons (
        id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        relationship_type TEXT NOT NULL,
        relationship_tags TEXT,
        phone TEXT,
        avatar TEXT,
        contact_id TEXT,
        create_time INTEGER NOT NULL,
        update_time INTEGER NOT NULL
      )
    `;

    // 创建人情记录表
    const createRecordTable = `
      CREATE TABLE IF NOT EXISTS lelv_human_records (
        id TEXT PRIMARY KEY,
        type TEXT NOT NULL,
        event_type TEXT NOT NULL,
        custom_event_type TEXT,
        amount REAL NOT NULL,
        event_time INTEGER NOT NULL,
        person_id TEXT NOT NULL,
        location TEXT,
        remark TEXT,
        photos TEXT,
        custom_fields TEXT,
        create_time INTEGER NOT NULL,
        update_time INTEGER NOT NULL,
        FOREIGN KEY (person_id) REFERENCES lelv_persons (id)
      )
    `;

    // 创建应用设置表
    const createSettingsTable = `
      CREATE TABLE IF NOT EXISTS lelv_app_settings (
        key TEXT PRIMARY KEY,
        value TEXT NOT NULL,
        update_time INTEGER NOT NULL
      )
    `;

    await this.store.executeSql(createPersonTable);
    await this.store.executeSql(createRecordTable);
    await this.store.executeSql(createSettingsTable);

    // 创建索引以提升查询性能
    await this.createIndexes();
  }

  /**
   * 创建索引
   */
  private async createIndexes(): Promise<void> {
    if (!this.store) return;

    const indexes = [
      'CREATE INDEX IF NOT EXISTS idx_records_person_id ON lelv_human_records (person_id)',
      'CREATE INDEX IF NOT EXISTS idx_records_event_time ON lelv_human_records (event_time)',
      'CREATE INDEX IF NOT EXISTS idx_records_type ON lelv_human_records (type)',
      'CREATE INDEX IF NOT EXISTS idx_persons_name ON lelv_persons (name)',
      'CREATE INDEX IF NOT EXISTS idx_persons_phone ON lelv_persons (phone)'
    ];

    for (const index of indexes) {
      await this.store.executeSql(index);
    }
  }

  /**
   * 检查并升级数据库
   */
  private async checkAndUpgradeDatabase(): Promise<void> {
    if (!this.store) return;

    try {
      // 获取当前数据库版本
      const currentVersion = await this.getCurrentVersion();
      console.info(`当前数据库版本: ${currentVersion}, 目标版本: ${this.DB_VERSION}`);

      if (currentVersion < this.DB_VERSION) {
        console.info('开始数据库升级...');
        await this.upgradeDatabase(currentVersion, this.DB_VERSION);
        await this.setCurrentVersion(this.DB_VERSION);
        console.info('数据库升级完成');
      }
    } catch (error) {
      console.error('数据库升级失败:', JSON.stringify(error));
      throw new Error('数据库升级失败');
    }
  }

  /**
   * 获取当前数据库版本
   */
  private async getCurrentVersion(): Promise<number> {
    if (!this.store) return 0;

    try {
      const resultSet = await this.store.querySql(
        'SELECT value FROM lelv_app_settings WHERE key = ?',
        [this.CURRENT_VERSION_KEY]
      );

      if (resultSet.goToFirstRow()) {
        const versionIndex = resultSet.getColumnIndex('value');
        if (versionIndex >= 0) {
          const version = resultSet.getLong(versionIndex);
          resultSet.close();
          return version;
        }
      }
      resultSet.close();
      return 0; // 默认版本0
    } catch (error) {
      console.warn('获取数据库版本失败,使用默认版本0');
      return 0;
    }
  }

  /**
   * 设置当前数据库版本
   */
  private async setCurrentVersion(version: number): Promise<void> {
    if (!this.store) return;

    const now = Date.now();
    await this.store.executeSql(
      'INSERT OR REPLACE INTO lelv_app_settings (key, value, update_time) VALUES (?, ?, ?)',
      [this.CURRENT_VERSION_KEY, version.toString(), now]
    );
  }

  /**
   * 升级数据库
   */
  private async upgradeDatabase(fromVersion: number, toVersion: number): Promise<void> {
    if (!this.store) return;

    try {
      // 开启事务
      await this.store.beginTransaction();

      // 从版本1升级到版本2
      if (fromVersion < 2 && toVersion >= 2) {
        await this.upgradeToVersion2();
      }

      // 可以继续添加更多版本的升级逻辑
      // if (fromVersion < 3 && toVersion >= 3) {
      //   await this.upgradeToVersion3();
      // }

      // 提交事务
      await this.store.commit();
    } catch (error) {
      // 回滚事务
      await this.store.rollback(void 0);
      throw new Error(`数据库升级失败: ${error.message}`);
    }
  }

  /**
   * 升级到版本2
   */
  private async upgradeToVersion2(): Promise<void> {
    if (!this.store) return;

    console.info('执行数据库升级到版本2...');

    // 添加新的索引以提升查询性能
    const newIndexes = [
      'CREATE INDEX IF NOT EXISTS idx_records_amount ON lelv_human_records (amount)',
      'CREATE INDEX IF NOT EXISTS idx_persons_relationship_type ON lelv_persons (relationship_type)'
    ];

    for (const indexSql of newIndexes) {
      await this.store.executeSql(indexSql);
    }

    // 添加新的字段
    try {
      await this.store.executeSql(`
        ALTER TABLE lelv_human_records ADD COLUMN is_deleted INTEGER DEFAULT 0
      `);
    } catch (error) {
      // 字段可能已存在,忽略错误
      console.warn('添加is_deleted字段失败,可能已存在');
    }

    console.info('数据库升级到版本2完成');
  }

  /**
   * 优化数据库性能
   */
  public async optimizeDatabase(): Promise<void> {
    if (!this.store) return;

    try {
      console.info('开始数据库性能优化...');
      
      // 执行VACUUM命令回收空间
      await this.store.executeSql('VACUUM');
      
      // 执行ANALYZE命令更新统计信息
      await this.store.executeSql('ANALYZE');
      
      console.info('数据库性能优化完成');
    } catch (error) {
      console.error('数据库性能优化失败:', JSON.stringify(error));
      throw new Error('数据库性能优化失败');
    }
  }

  /**
   * 获取数据库实例
   */
  public getStore(): relationalStore.RdbStore | null {
    return this.store;
  }
}

核心原理解析

1. 单例模式设计

使用单例模式确保整个应用只有一个数据库管理器实例,避免多次初始化造成的资源浪费和数据冲突。

2. 数据库版本管理

  • 通过DB_VERSION常量管理当前数据库版本
  • lelv_app_settings表中记录实际数据库版本
  • 初始化时自动检查版本并执行升级

3. 事务处理

数据库升级使用事务保证原子性:

await this.store.beginTransaction();  // 开启事务
// ... 执行升级操作
await this.store.commit();            // 提交事务
// 或
await this.store.rollback(void 0);    // 回滚事务

4. 索引优化策略

根据查询场景创建合适的索引:

  • 主键索引: id字段
  • 外键索引: person_id字段
  • 查询索引: event_time, type, amount等常用查询字段

最佳实践建议

  1. 数据库初始化时机: 在应用启动时的UIAbility中初始化
  2. 版本号管理: 每次数据库结构变更都需要增加版本号
  3. 升级脚本测试: 充分测试各个版本之间的升级路径
  4. 错误处理: 捕获异常并提供友好的错误提示
  5. 性能监控: 定期执行VACUUM和ANALYZE优化数据库

避坑指南

  1. 不要在主线程执行耗时的数据库操作

    • 使用async/await确保异步执行
  2. 不要忘记关闭ResultSet

    • 每次查询后都要调用resultSet.close()
  3. 不要在事务中执行长时间操作

    • 事务要尽快提交或回滚
  4. ALTER TABLE时注意兼容性

    • 使用IF NOT EXISTSDEFAULT

效果展示

数据库初始化日志输出:

开始初始化数据库...
数据库连接创建成功
数据表创建完成
当前数据库版本: 1, 目标版本: 2
开始数据库升级...
执行数据库升级到版本2...
数据库升级到版本2完成
数据库升级完成
数据库初始化成功

总结

本方案提供了一套完整的鸿蒙RelationalStore数据库管理解决方案,包括:

  • ✅ 规范的数据库初始化流程
  • ✅ 安全的版本升级机制
  • ✅ 完善的事务回滚保护
  • ✅ 合理的索引优化策略

更多关于HarmonyOS鸿蒙Next中在开发原生应用时,如何优雅地实现RelationalStore数据库的初始化、表结构创建、索引优化以及数据库版本升级?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,使用RelationalStore数据库时,通过RdbStoreConfig配置数据库参数实现初始化。表结构创建需在RdbOpenCallback的onCreate方法中执行SQL建表语句。索引优化通过在常用查询字段建立索引提升性能,使用CREATE INDEX语句实现。数据库版本升级在onUpgrade方法中处理,通过比较新旧版本号执行ALTER TABLE等SQL操作完成表结构变更。整个过程需保证数据完整性和事务一致性。

在HarmonyOS Next中,使用RelationalStore进行数据库管理时,建议采用以下方法实现优雅的初始化、表结构创建、索引优化和版本升级:

1. 数据库初始化与表结构创建

通过RdbStoreConfig配置数据库,并在RdbOpenCallbackonCreate方法中执行建表语句。使用executeSql批量执行CREATE TABLE语句,确保原子性。示例:

const config: relationalStore.RdbStoreConfig = {
  name: 'test.db',
  securityLevel: relationalStore.SecurityLevel.S1
};
relationalStore.getRdbStore(context, config, (err, store) => {
  store.executeSql('CREATE TABLE IF NOT EXISTS user (id INTEGER PRIMARY KEY, name TEXT)');
});

2. 索引优化

对频繁查询的字段(如name)添加索引:

store.executeSql('CREATE INDEX idx_name ON user(name)');

避免过度索引,仅针对WHERE、JOIN或ORDER BY常用列创建。

3. 数据库版本升级与数据安全

RdbOpenCallbackonUpgrade中处理版本迁移:

  • 使用ALTER TABLE添加列或新表,避免直接修改原有结构。
  • 通过事务包装所有升级操作,失败时自动回滚:
store.beginTransaction();
try {
  if (oldVersion < 2) {
    store.executeSql('ALTER TABLE user ADD COLUMN age INTEGER');
  }
  store.commit();
} catch (e) {
  store.rollback();
}
  • 重要数据提前备份(如导出到文件)。

4. 升级失败回滚

依赖事务机制确保操作原子性。若升级过程中发生异常,事务回滚会自动还原至初始状态,结合try-catch处理错误日志。

5. 查询性能优化

  • 使用索引加速过滤。
  • 避免SELECT *,仅查询所需字段。
  • 对大数据集采用分页查询(LIMIT/OFFSET)。

此方案通过事务、结构化回调和索引策略,保障数据库操作的可靠性与效率。

回到顶部