[鸿蒙2025领航者闯关]使用RelationalStore实现增删改查(CRUD)操作

问题描述

在 HarmonyOS 应用开发中,如何使用 RelationalStore 实现数据库的增删改查(CRUD)操作?很多开发者在使用关系型数据库时遇到以下问题:

  • 不清楚如何正确初始化数据库
  • 不知道如何设计 Dao 层
  • 查询结果集如何转换为对象
  • 如何处理异步操作

解决方案

1. 技术原理

RelationalStore 是 HarmonyOS 提供的关系型数据库解决方案,基于 SQLite 实现。核心流程:

复制代码
DatabaseHelper(单例) → 初始化RdbStore → 创建表结构 → Dao层操作

2. 完整实现代码

步骤 1: 创建数据库管理类
复制代码
import relationalStore from '@ohos.data.relationalStore';
​
export class DatabaseHelper {
  private static instance: DatabaseHelper;
  private rdbStore: relationalStore.RdbStore | null = null;
  private readonly DB_NAME: string = 'app_database.db';
  private readonly DB_VERSION: number = 1;
  
  private constructor() {}
  
  /**
   * 获取单例
   */
  static getInstance(): DatabaseHelper {
    if (!DatabaseHelper.instance) {
      DatabaseHelper.instance = new DatabaseHelper();
    }
    return DatabaseHelper.instance;
  }
  
  /**
   * 初始化数据库
   */
  async init(context: Context): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: this.DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S1
    };
    
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    await this.createTables();
  }
  
  /**
   * 创建表结构
   */
  private async createTables(): Promise<void> {
    const createTableSql = `
      CREATE TABLE IF NOT EXISTS user (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT NOT NULL,
        nickname TEXT,
        email TEXT,
        create_time INTEGER,
        update_time INTEGER
      )
    `;
    
    await this.rdbStore!.executeSql(createTableSql);
  }
  
  /**
   * 获取数据库实例
   */
  getStore(): relationalStore.RdbStore {
    if (!this.rdbStore) {
      throw new Error('Database not initialized');
    }
    return this.rdbStore;
  }
}
步骤 2: 创建数据模型
复制代码
export class User {
  id: number = 0;
  username: string = '';
  nickname: string = '';
  email: string = '';
  createTime: number = 0;
  updateTime: number = 0;
  
  /**
   * 从数据库记录创建对象
   */
  static fromDb(record: Record<string, Object>): User {
    const user = new User();
    user.id = record['id'] as number;
    user.username = record['username'] as string;
    user.nickname = record['nickname'] as string;
    user.email = record['email'] as string;
    user.createTime = record['create_time'] as number;
    user.updateTime = record['update_time'] as number;
    return user;
  }
}
步骤 3: 实现 Dao 层
复制代码
import relationalStore from '@ohos.data.relationalStore';
import { User } from '../models/User';
import { DatabaseHelper } from './DatabaseHelper';
​
const TABLE_NAME = 'user';
​
export class UserDao {
  private dbHelper: DatabaseHelper;
  
  constructor() {
    this.dbHelper = DatabaseHelper.getInstance();
  }
  
  /**
   * 插入用户
   */
  async insert(user: User): Promise<number> {
    const store = this.dbHelper.getStore();
    const valueBucket: relationalStore.ValuesBucket = {
      username: user.username,
      nickname: user.nickname,
      email: user.email,
      create_time: Date.now(),
      update_time: Date.now()
    };
    
    return await store.insert(TABLE_NAME, valueBucket);
  }
  
  /**
   * 更新用户
   */
  async update(user: User): Promise<number> {
    const store = this.dbHelper.getStore();
    const valueBucket: relationalStore.ValuesBucket = {
      nickname: user.nickname,
      email: user.email,
      update_time: Date.now()
    };
    
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    predicates.equalTo('id', user.id);
    
    return await store.update(valueBucket, predicates);
  }
  
  /**
   * 根据ID查询
   */
  async findById(id: number): Promise<User | null> {
    const store = this.dbHelper.getStore();
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    predicates.equalTo('id', id);
    
    const resultSet = await store.query(predicates);
    let user: User | null = null;
    
    if (resultSet.goToFirstRow()) {
      const record = this.resultSetToRecord(resultSet);
      user = User.fromDb(record);
    }
    
    resultSet.close();
    return user;
  }
  
  /**
   * 查询所有用户
   */
  async findAll(): Promise<User[]> {
    const store = this.dbHelper.getStore();
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    
    const resultSet = await store.query(predicates);
    const users: User[] = [];
    
    while (resultSet.goToNextRow()) {
      const record = this.resultSetToRecord(resultSet);
      users.push(User.fromDb(record));
    }
    
    resultSet.close();
    return users;
  }
  
  /**
   * 删除用户
   */
  async delete(id: number): Promise<number> {
    const store = this.dbHelper.getStore();
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    predicates.equalTo('id', id);
    
    return await store.delete(predicates);
  }
  
  /**
   * 将ResultSet转换为记录对象
   */
  private resultSetToRecord(resultSet: relationalStore.ResultSet): Record<string, Object> {
    const record: Record<string, Object> = {};
    const columnNames = resultSet.columnNames;
    
    for (const name of columnNames) {
      const index = resultSet.getColumnIndex(name);
      const type = resultSet.getColumnType(index);
      
      switch (type) {
        case relationalStore.ColumnType.TYPE_INTEGER:
          record[name] = resultSet.getLong(index);
          break;
        case relationalStore.ColumnType.TYPE_STRING:
          record[name] = resultSet.getString(index);
          break;
        case relationalStore.ColumnType.TYPE_FLOAT:
          record[name] = resultSet.getDouble(index);
          break;
        default:
          record[name] = resultSet.getString(index);
      }
    }
    
    return record;
  }
}
步骤 4: 在 EntryAbility 中初始化
复制代码
import { DatabaseHelper } from '../database/DatabaseHelper';
​
export default class EntryAbility extends UIAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 初始化数据库
    await DatabaseHelper.getInstance().init(this.context);
  }
}
步骤 5: 使用示例
复制代码
import { UserDao } from '../database/UserDao';
import { User } from '../models/User';
​
// 创建用户
const userDao = new UserDao();
const user = new User();
user.username = 'zhangsan';
user.nickname = '张三';
user.email = 'zhangsan@example.com';
​
const userId = await userDao.insert(user);
console.log('插入用户ID:', userId);
​
// 查询用户
const foundUser = await userDao.findById(userId);
console.log('查询到用户:', foundUser?.nickname);
​
// 更新用户
if (foundUser) {
  foundUser.nickname = '张三三';
  await userDao.update(foundUser);
}
​
// 查询所有用户
const allUsers = await userDao.findAll();
console.log('用户总数:', allUsers.length);
​
// 删除用户
await userDao.delete(userId);

3. 运行效果

复制代码
[日志] 插入用户ID: 1
[日志] 查询到用户: 张三
[日志] 用户总数: 1
[日志] 删除成功

关键要点

1. 单例模式

DatabaseHelper 使用单例模式,确保全局只有一个数据库实例,避免资源浪费。

2. 异步操作

所有数据库操作都是异步的,使用 async/await 确保数据一致性。

3. ResultSet 处理

  • 必须调用 goToFirstRow()goToNextRow() 移动游标
  • 使用完毕后必须调用 close() 释放资源
  • 根据列类型使用对应的 getter 方法

4. ValuesBucket

插入和更新操作使用 ValuesBucket 传递数据,字段名必须与表结构一致。

5. RdbPredicates

查询和删除操作使用 RdbPredicates 构建条件,支持链式调用:

复制代码
predicates
  .equalTo('status', 1)
  .and()
  .greaterThan('age', 18)
  .orderByAsc('create_time');

常见问题

Q1: 如何处理数据库初始化失败?

复制代码
async init(context: Context): Promise<void> {
  try {
    const config: relationalStore.StoreConfig = {
      name: this.DB_NAME,
      securityLevel: relationalStore.SecurityLevel.S1
    };
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    await this.createTables();
  } catch (err) {
    console.error('数据库初始化失败:', JSON.stringify(err));
    throw err;
  }
}

Q2: 如何实现分页查询?

复制代码
async findByPage(page: number, pageSize: number): Promise<User[]> {
  const store = this.dbHelper.getStore();
  const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
  predicates.limitAs(pageSize).offsetAs((page - 1) * pageSize);

  const resultSet = await store.query(predicates);
  const users: User[] = [];

  while (resultSet.goToNextRow()) {
    const record = this.resultSetToRecord(resultSet);
    users.push(User.fromDb(record));
  }

  resultSet.close();
  return users;
}

Q3: 如何执行复杂 SQL 查询?

复制代码
async customQuery(sql: string, args: Array<string | number>): Promise<any[]> {
  const store = this.dbHelper.getStore();
  const resultSet = await store.querySql(sql, args);
  const results: any[] = [];

  while (resultSet.goToNextRow()) {
    results.push(this.resultSetToRecord(resultSet));
  }

  resultSet.close();
  return results;
}

总结

本文展示了 HarmonyOS 中 RelationalStore 的完整 CRUD 实现方案:

  • ✅ 单例模式管理数据库实例
  • ✅ 规范的 Dao 层设计
  • ✅ 类型安全的数据转换
  • ✅ 完善的错误处理
  • ✅ 支持复杂查询

参考资料

相关推荐
后端小张4 小时前
【鸿蒙2025领航者闯关】从技术突破到生态共建,开发者的成长与远航
华为·wpf·生态·harmonyos·鸿蒙·鸿蒙系统·鸿蒙2025领航者试炼
花启莫你是不是傻5 小时前
鸿蒙下FFmpeg编译流程梳理
华为·ffmpeg·harmonyos
malajisi015 小时前
鸿蒙PC开发笔记三:HarmonyOS PC 命令行开发和Helloworld
笔记·华为·harmonyos·命令行开发·鸿蒙pc
L、2185 小时前
状态共享新范式:在 Flutter + OpenHarmony 应用中实现跨框架状态同步(Riverpod + ArkState)
javascript·华为·智能手机·electron·harmonyos
赵财猫._.5 小时前
React Native鸿蒙开发实战(八):打包发布与AppGallery上架
react native·react.js·harmonyos
kirk_wang5 小时前
Flutter 三方库鸿蒙适配手记:让 `flutter_isolate` 在 OpenHarmony 上跑起来
flutter·移动开发·跨平台·arkts·鸿蒙
晚霞的不甘5 小时前
[鸿蒙2025领航者闯关]鸿蒙实战进阶:多端协同与性能优化实践心得
华为·性能优化·harmonyos
ezeroyoung5 小时前
Flutter HarmonyOS 键盘高度监听插件开发指南
flutter·计算机外设·harmonyos
爱吃大芒果14 小时前
GitCode口袋工具的部署运行教程
flutter·华为·harmonyos·gitcode