鸿蒙(HarmoneyOS),封装一个通用关系型数据库操作类

鸿蒙(HarmoneyOS),封装一个通用关系型数据库操作类

ts 复制代码
import { relationalStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { LogUtil } from '../util/LogUtil';
import { GlobalContext } from '../util/GlobalContext';

const TAG = 'RdbHelper';

/**
 * 数据库升级回调类型
 * @param oldVersion 当前旧版本号
 * @param store RdbStore实例,可直接执行升级SQL
 */
export type UpgradeCallback = (oldVersion: number, transaction: relationalStore.Transaction) => Promise<void>;

/**
 * 数据库首次创建回调类型
 * @param store RdbStore实例,用于执行建表SQL
 */
export type CreateCallback = (transaction: relationalStore.Transaction) => Promise<void>;

/**
 * 查询结果行类型,键为列名,值为对应列的值
 */
export type QueryRow = Record<string, relationalStore.ValueType>;

/**
 * 关系型数据库通用工具类
 *
 * 功能特性:
 * 1. 默认数据库名称为 XXXXXX.db
 * 2. 支持基于 user_version 的数据库版本升级
 * 3. 提供完整的增删改查接口
 * 4. 内置事务支持
 */
export class RdbHelper {
  private static readonly DEFAULT_DB_NAME = 'XXXXXX.db';
  private static instance: RdbHelper | null = null;
  private rdbStore: relationalStore.RdbStore | null = null;
  private dbName: string;
  private initialized: boolean = false;

  private constructor(dbName: string = RdbHelper.DEFAULT_DB_NAME) {
    this.dbName = dbName;
  }

  /**
   * 获取单例实例
   * @param dbName 可选,自定义数据库名称,仅在首次调用时生效
   */
  static getInstance(dbName?: string): RdbHelper {
    if (!RdbHelper.instance) {
      RdbHelper.instance = new RdbHelper(dbName ?? RdbHelper.DEFAULT_DB_NAME);
    }
    return RdbHelper.instance;
  }

  /**
   * 释放单例(通常用于测试场景或需要重新初始化时调用)
   */
  static releaseInstance(): void {
    if (RdbHelper.instance) {
      RdbHelper.instance.rdbStore = null;
      RdbHelper.instance.initialized = false;
    }
    RdbHelper.instance = null;
  }

  /**
   * 初始化数据库,自动处理创建及版本升级
   *
   * @param targetVersion 目标数据库版本号(大于0的整数)
   * @param onCreate 数据库首次创建时的回调,用于执行建表语句
   * @param onUpgrade 数据库升级回调,参数为当前旧版本号
   */
  async initDatabase(targetVersion: number, onCreate?: CreateCallback,
    onUpgrade?: UpgradeCallback): Promise<void> {
    if (this.initialized && this.rdbStore) {
      LogUtil.info(TAG, 'Database already initialized, skip.');
      return;
    }

    // 检查分词器支持
    let tokenType = relationalStore.Tokenizer.ICU_TOKENIZER;
    let tokenizerSupported = relationalStore.isTokenizerSupported(tokenType);
    if (!tokenizerSupported) {
      tokenType = relationalStore.Tokenizer.NONE_TOKENIZER;
      LogUtil.info(TAG, 'ICU_TOKENIZER is not supported on this platform, use NONE_TOKENIZER.');
    }

    const config: relationalStore.StoreConfig = {
      name: this.dbName,
      securityLevel: relationalStore.SecurityLevel.S3,
      encrypt: false,
      isReadOnly: false,
      tokenizer: tokenType
    };

    try {
      this.rdbStore = await relationalStore.getRdbStore(GlobalContext.getContext(), config);
      LogUtil.info(TAG, `Succeeded in getting RdbStore: ${this.dbName}`);
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Failed to get RdbStore. Code:${error.code}, message:${error.message}`);
      throw new Error(`Failed to get RdbStore: ${error.message}`);
    }

    // 处理数据库版本
    await this.handleVersionUpgrade(targetVersion, onCreate, onUpgrade);
    this.initialized = true;
    LogUtil.info(TAG, `Database initialized successfully, version: ${targetVersion}`);
  }

  /**
   * 处理数据库版本升级逻辑
   */
  private async handleVersionUpgrade(targetVersion: number, onCreate?: CreateCallback,
    onUpgrade?: UpgradeCallback): Promise<void> {
    if (!this.rdbStore) {
      throw new Error('RdbStore is not initialized');
    }

    let transaction: relationalStore.Transaction | null = null;
    try {
      transaction = await this.rdbStore.createTransaction({});
      let currentVersion = await this.fetchStoreVersion(transaction);

      LogUtil.info(TAG, `Current DB version: ${currentVersion}, Target version: ${targetVersion}`);

      if (currentVersion === 0) {
        // 数据库首次创建
        if (onCreate) {
          await onCreate(transaction);
        }
        await transaction.execute(`PRAGMA user_version = ${targetVersion}`);
        LogUtil.info(TAG, `Database created, set version to ${targetVersion}.`);
      } else if (currentVersion < targetVersion) {
        // 需要逐级升级
        if (onUpgrade) {
          for (let ver = currentVersion; ver < targetVersion; ver++) {
            LogUtil.info(TAG, `Upgrading database from version ${ver} to ${ver + 1}.`);
            await onUpgrade(ver, transaction);
          }
        }
        await transaction.execute(`PRAGMA user_version = ${targetVersion}`);
        LogUtil.info(TAG, `Database upgraded to version ${targetVersion}.`);
      } else if (currentVersion > targetVersion) {
        LogUtil.warn(TAG,
          `Current version ${currentVersion} is higher than target ${targetVersion}, skip upgrade.`);
      }

      await transaction.commit();
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Failed during version upgrade. Code:${error.code}, message:${error.message}`);
      if (transaction) {
        try {
          await transaction.rollback();
        } catch (rollbackErr) {
          LogUtil.error(TAG, `Rollback failed: ${(rollbackErr as BusinessError).message}`);
        }
      }
      throw new Error(`Database upgrade failed: ${error.message}`);
    }
  }

  /**
   * 通过事务查询当前数据库版本号
   */
  private async fetchStoreVersion(transaction: relationalStore.Transaction): Promise<number> {
    let storeVersion = await transaction.execute('PRAGMA user_version');
    return storeVersion as number;
  }

  // ==================== 内部校验 ====================

  /**
   * 确保数据库已初始化,返回 RdbStore 实例
   */
  private ensureInitialized(): relationalStore.RdbStore {
    if (!this.rdbStore) {
      throw new Error('Database not initialized. Please call initDatabase() first.');
    }
    return this.rdbStore;
  }

  // ==================== CRUD 操作 ====================

  /**
   * 插入单条数据
   *
   * @param table 表名
   * @param values 待插入的键值对数据
   * @returns 插入行的 rowId,失败时返回 -1
   */
  async insert(table: string, values: relationalStore.ValuesBucket): Promise<number> {
    let store = this.ensureInitialized();
    try {
      let rowId = await store.insert(table, values);
      LogUtil.info(TAG, `Insert into '${table}' success, rowId: ${rowId}`);
      return rowId;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Insert into '${table}' failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Insert failed: ${error.message}`);
    }
  }

  /**
   * 批量插入数据
   *
   * @param table 表名
   * @param valuesList 待插入的键值对数据列表
   * @returns 实际插入的行数
   */
  async batchInsert(table: string, valuesList: relationalStore.ValuesBucket[]): Promise<number> {
    let store = this.ensureInitialized();
    try {
      let count = await store.batchInsert(table, valuesList);
      LogUtil.info(TAG, `Batch insert into '${table}' success, count: ${count}`);
      return count;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Batch insert into '${table}' failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Batch insert failed: ${error.message}`);
    }
  }



  /**
   * 根据条件删除数据
   *
   * @param predicates 删除条件(需指定表名)
   * @returns 删除的行数
   */
  async delete(predicates: relationalStore.RdbPredicates): Promise<number> {
    let store = this.ensureInitialized();
    try {
      let count = await store.delete(predicates);
      LogUtil.info(TAG, `Delete success, count: ${count}`);
      return count;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Delete failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Delete failed: ${error.message}`);
    }
  }

  /**
   * 根据条件更新数据
   *
   * @param values 待更新的键值对数据
   * @param predicates 更新条件(需指定表名)
   * @returns 更新的行数
   */
  async update(values: relationalStore.ValuesBucket, predicates: relationalStore.RdbPredicates): Promise<number> {
    let store = this.ensureInitialized();
    try {
      let count = await store.update(values, predicates);
      LogUtil.info(TAG, `Update success, count: ${count}`);
      return count;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Update failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Update failed: ${error.message}`);
    }
  }

  /**
   * 查询多条数据
   *
   * @param predicates 查询条件(需指定表名)
   * @param columns 要查询的列名数组,不传则查询所有列
   * @returns 查询结果列表
   */
  async query(predicates: relationalStore.RdbPredicates, columns?: string[]): Promise<QueryRow[]> {
    let store = this.ensureInitialized();
    let resultSet: relationalStore.ResultSet | null = null;
    try {
      resultSet = await store.query(predicates, columns);
      let results: QueryRow[] = [];
      let columnNames = resultSet.columnNames;
      while (resultSet.goToNextRow()) {
        let row: QueryRow = {};
        for (let col of columnNames) {
          let colIdx = resultSet.getColumnIndex(col);
          row[col] = await this.getValueFromResultSet(resultSet, colIdx);
        }
        results.push(row);
      }
      return results;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Query failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Query failed: ${error.message}`);
    } finally {
      if (resultSet) {
        resultSet.close();
      }
    }
  }

  /**
   * 查询单条数据
   *
   * @param predicates 查询条件(需指定表名)
   * @param columns 要查询的列名数组,不传则查询所有列
   * @returns 查询结果行,未找到返回 null
   */
  async queryOne(predicates: relationalStore.RdbPredicates, columns?: string[]): Promise<QueryRow | null> {
    let store = this.ensureInitialized();
    let resultSet: relationalStore.ResultSet | null = null;
    try {
      resultSet = await store.query(predicates, columns);
      if (resultSet.goToFirstRow()) {
        let row: QueryRow = {};
        let columnNames = resultSet.columnNames;
        for (let col of columnNames) {
          let colIdx = resultSet.getColumnIndex(col);
          row[col] = await this.getValueFromResultSet(resultSet, colIdx);
        }
        return row;
      }
      return null;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `QueryOne failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`QueryOne failed: ${error.message}`);
    } finally {
      if (resultSet) {
        resultSet.close();
      }
    }
  }

  /**
   * 查询记录总数
   *
   * @param predicates 查询条件(需指定表名)
   * @returns 匹配的记录数
   */
  async count(predicates: relationalStore.RdbPredicates): Promise<number> {
    let store = this.ensureInitialized();
    let resultSet: relationalStore.ResultSet | null = null;
    try {
      resultSet = await store.query(predicates, ['COUNT(*) AS cnt']);
      if (resultSet.goToFirstRow()) {
        let colIdx = resultSet.getColumnIndex('cnt');
        return resultSet.getLong(colIdx);
      }
      return 0;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Count failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Count failed: ${error.message}`);
    } finally {
      if (resultSet) {
        resultSet.close();
      }
    }
  }

  /**
   * 执行非查询 SQL 语句(INSERT / UPDATE / DELETE / DDL 等)
   *
   * @param sql SQL 语句
   * @param bindArgs 绑定参数(与 ? 占位符一一对应)
   */
  async executeSql(sql: string, bindArgs?: relationalStore.ValueType[]): Promise<void> {
    let store = this.ensureInitialized();
    try {
      await store.executeSql(sql, bindArgs);
      LogUtil.info(TAG, 'Execute SQL success.');
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Execute SQL failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Execute SQL failed: ${error.message}`);
    }
  }

  /**
   * 执行查询 SQL 语句
   *
   * @param sql 查询 SQL 语句
   * @param bindArgs 绑定参数(与 ? 占位符一一对应)
   * @returns 查询结果列表
   */
  async querySql(sql: string, bindArgs?: relationalStore.ValueType[]): Promise<QueryRow[]> {
    let store = this.ensureInitialized();
    let resultSet: relationalStore.ResultSet | null = null;
    try {
      resultSet = await store.querySql(sql, bindArgs);
      let results: QueryRow[] = [];
      let columnNames = resultSet.columnNames;
      while (resultSet.goToNextRow()) {
        let row: QueryRow = {};
        for (let col of columnNames) {
          let colIdx = resultSet.getColumnIndex(col);
          row[col] = await this.getValueFromResultSet(resultSet, colIdx);
        }
        results.push(row);
      }
      return results;
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Query SQL failed. Code:${error.code}, message:${error.message}`);
      throw new Error(`Query SQL failed: ${error.message}`);
    } finally {
      if (resultSet) {
        resultSet.close();
      }
    }
  }

  /**
   * 从 ResultSet 中根据列类型安全地提取值
   */
  private async getValueFromResultSet(resultSet: relationalStore.ResultSet, columnIndex: number): Promise<relationalStore.ValueType> {
    if (resultSet.isColumnNull(columnIndex)) {
      return '';
    }
    let columnType = await resultSet.getColumnType(columnIndex);
    switch (columnType) {
      case relationalStore.ColumnType.INTEGER:
        return resultSet.getLong(columnIndex);
      case relationalStore.ColumnType.FLOAT_VECTOR:
        return resultSet.getDouble(columnIndex);
      case relationalStore.ColumnType.TEXT:
        return resultSet.getString(columnIndex);
      case relationalStore.ColumnType.BLOB:
        return resultSet.getBlob(columnIndex);
      default:
        return resultSet.getString(columnIndex);
    }
  }

  // ==================== 事务操作 ====================

  /**
   * 在事务中执行操作,自动 commit / rollback
   *
   * @param action 事务内要执行的操作,回调参数为当前 RdbStore 实例
   *
   * 使用示例:
   * ```
   * await helper.runInTransaction(async (store) => {
   *   await store.executeSql('INSERT INTO ...');
   *   await store.executeSql('UPDATE ...');
   * });
   * ```
   */
  async runInTransaction(action: (store: relationalStore.RdbStore) => Promise<void>): Promise<void> {
    let store = this.ensureInitialized();
    let transaction: relationalStore.Transaction | null = null;
    try {
      transaction = await store.createTransaction({});
      await action(store);
      await transaction.commit();
      LogUtil.info(TAG, 'Transaction committed successfully.');
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Transaction failed. Code:${error.code}, message:${error.message}`);
      if (transaction) {
        try {
          await transaction.rollback();
          LogUtil.info(TAG, 'Transaction rolled back.');
        } catch (rollbackErr) {
          LogUtil.error(TAG, `Rollback failed: ${(rollbackErr as BusinessError).message}`);
        }
      }
      throw new Error(`Transaction failed: ${error.message}`);
    }
  }

  // ==================== 工具方法 ====================

  /**
   * 获取原始 RdbStore 实例,用于直接调用原生 API
   */
  getRawStore(): relationalStore.RdbStore | null {
    return this.rdbStore;
  }

  /**
   * 获取当前数据库名称
   */
  getDatabaseName(): string {
    return this.dbName;
  }

  /**
   * 检查数据库是否已初始化
   */
  isInitialized(): boolean {
    return this.initialized && this.rdbStore !== null;
  }

  /**
   * 关闭数据库(清除实例引用)
   */
  async close(): Promise<void> {
    this.rdbStore = null;
    this.initialized = false;
    LogUtil.info(TAG, 'Database store reference cleared.');
  }

  /**
   * 删除数据库文件
   * 注意:调用前请确保没有正在进行的数据库操作
   */
  async deleteDatabase(): Promise<void> {
    await this.close();
    try {
      await relationalStore.deleteRdbStore(GlobalContext.getContext(), this.dbName);
      LogUtil.info(TAG, `Database '${this.dbName}' deleted.`);
    } catch (err) {
      let error = err as BusinessError;
      LogUtil.error(TAG, `Failed to delete database. Code:${error.code}, message:${error.message}`);
      throw new Error(`Delete database failed: ${error.message}`);
    }
  }
}

使用示例

ts 复制代码
const TAG = 'AppDBController';

export class AppDBController {
  private static readonly DB_VERSION = 1;
  private static instance: AppDBController;
  private helper: RdbHelper;

  private constructor() {
    this.helper = RdbHelper.getInstance();
  }

  public static getInstance(): AppDBController {
    if (!AppDBController.instance) {
      AppDBController.instance = new AppDBController();
    }
    return AppDBController.instance;
  }

  /**
   * 初始化数据库(首次创建建全部表,后续按版本升级)
   */
  async init(): Promise<void> {
    await this.helper.initDatabase(
      AppDBController.DB_VERSION,
      // onCreate:首次创建时建表
        LogUtil.info(TAG, `Table ${AppRecord.TABLE_NAME} created.`);
      },
      // onUpgrade:逐级升级
      async (oldVersion: number, transaction: relationalStore.Transaction) => {
        // v2 -> v3:新增 XXX
        /*if (oldVersion === 2) {
          await transaction.execute(``);
        }*/
      }
    );
  }
}
相关推荐
Nontee1 小时前
新手数据库避坑指南:通俗理解“页分裂”与“数据碎片”
数据库·oracle
Vd7H20A71 小时前
TencentOS Server 3.3 安装 PostgreSQL 18 完整指南
数据库·postgresql
Nontee1 小时前
新手建表指南:数据库主键选自增ID还是UUID?
数据库·oracle
AI智图坊1 小时前
亚马逊多站点Listing视觉制作的效率瓶颈与AI解决方案:GPT-Image-2与Nano Banana Pro双模型分析
大数据·前端·数据库·人工智能·自动化·aigc
G_dou_2 小时前
Flutter三方库适配OpenHarmony【palindrome_checker】回文检测器项目完整实战
flutter·harmonyos
wanghao6664552 小时前
精益方法论:用更少的资源创造更大的价值
大数据·前端·数据库·敏捷开发
fQ9F9I58m2 小时前
Redis 分布式锁进阶第三百一十一篇
数据库·redis·分布式
风满城332 小时前
鸿蒙原生应用实战(五):个人中心与数据可视化 —— 统计图表与成就徽章
harmonyos