前言

大家好,我是若城!👋 欢迎来到 HarmonyOS 5 开发实战系列。

在移动应用开发中,数据持久化是不可或缺的核心功能。无论是用户信息存储、应用配置管理,还是业务数据缓存,都需要可靠的数据库支持。本文将通过一个完整的"言记"应用中的"树洞"模块,深入讲解如何在 HarmonyOS 5 中使用 RdbStore 实现高效的数据库 CRUD 操作。

你将学到:

  • RdbStore 的核心概念和优势

  • 数据模型的设计与实现

  • 完整的 CRUD 操作封装

  • 实际项目中的最佳实践

RdbStore 简介

什么是 RdbStore?

RdbStore 是字节跳动为鸿蒙生态开发的高性能数据库组件,专门为 HarmonyOS 应用提供强大的数据持久化能力。

核心特性

开发效率

  • 基于 DTO 对象的操作方式,告别复杂的 SQL 语句

  • 自动化数据库创建和版本升级管理

  • 智能的查询结果反序列化

性能优化

  • 内置查询优化器和索引管理

  • 支持批量操作,提升数据处理效率

  • 完善的连接池管理

稳定可靠

  • 完备的单元测试覆盖

  • 全链路日志记录和性能监控

  • 异常处理和数据恢复机制

资源链接

项目演示

本文以"言记"应用中的"树洞"模块为例,展示 RdbStore 的完整使用流程。应用包含三个核心界面:

  1. 数据展示界面 - 显示所有树洞记录

  2. 内容编辑界面 - 创建和编辑树洞内容

  3. 数据管理界面 - 查询和删除操作

 

项目演示图

项目演示图

环境准备

项目目录结构

合理的项目结构是开发效率的基础,推荐按功能模块组织代码:

 

项目目录结构

项目目录结构

src/main/ets/
├── common/api/          # API 接口层
├── model/              # 数据模型
├── pages/              # 页面组件
├── types/              # 类型定义
└── utils/              # 工具类

安装依赖

在 DevEco Studio 中执行以下命令安装 RdbStore:

ohpm install rdbstore

 

安装插件

安装插件

数据模型设计

业务数据接口定义

首先定义业务层面的数据结构,这有助于保持代码的清晰性和可维护性:

// src/main/ets/types/types.ets
export interface TreeHoleData {
    id: number,              // 唯一标识符
    title: string,           // 树洞主题
    content: string,         // 树洞内容
    moods: string,           // 心情标签(逗号分隔)
    createTime: string,      // 创建时间
    isAnonymous: boolean     // 是否匿名发布
}

代码解析:

  • TreeHoleData 接口定义了业务层的数据结构

  • 每个字段都有明确的业务含义和数据类型

  • 这个接口将作为前端组件和数据库实体之间的桥梁

数据库实体模型

基于业务接口,创建对应的数据库实体模型:

// src/main/ets/model/TBTreeHole.ets

import { Columns, ColumnType, Entity } from "rdbstore"

/**
 * 树洞数据库实体模型
 * 使用 @Entity 装饰器标记为数据库表
 */
@Entity({
    tableName: 'tree_hole'  // 指定数据库表名
})
export class TreeHoleEntity {
    // 数据库主键,自增长
    @Columns({
        name: 'uid',                    // 数据库列名
        type: ColumnType.INTEGER,       // 数据类型:整型
        autoincrement: true,            // 自动递增
        isPrimaryKey: true              // 设为主键
    }) 
    uid: number | undefined = undefined

    // 业务ID字段
    @Columns({ name: 'id', type: ColumnType.INTEGER })
    id: number = 0

    // 树洞标题
    @Columns({ name: 'title', type: ColumnType.TEXT })
    title: string = ''

    // 树洞内容
    @Columns({ name: 'content', type: ColumnType.TEXT })
    content: string = ''

    // 心情标签(存储为逗号分隔的字符串)
    @Columns({ name: 'moods', type: ColumnType.TEXT })
    moods: string = ''

    // 创建时间(存储为字符串格式)
    @Columns({ name: 'createTime', type: ColumnType.TEXT })
    createTime: string = ''

    // 匿名状态(布尔值存储为 BLOB 类型)
    @Columns({ name: 'isAnonymous', type: ColumnType.BLOB })
    isAnonymous: boolean = false
}

代码解析:

  1. @Entity 装饰器:将类标记为数据库实体,tableName 指定对应的数据库表名

  2. @Columns 装饰器:定义数据库列的属性

    • name:数据库中的列名

    • type:数据类型,必须指定

    • isPrimaryKey:是否为主键

    • autoincrement:是否自动递增(仅适用于整型主键)

  3. 字段设计考虑

    • uid:数据库主键,自增长,确保唯一性

    • id:业务层面的ID,便于业务逻辑处理

    • 其他字段对应业务需求,类型选择合理

数据类型说明

RdbStore 支持四种基本数据类型,选择合适的类型对性能和存储效率至关重要:

/**
 * RdbStore 支持的数据库列类型
 * 选择合适的类型对性能和存储效率至关重要
 */
export enum ColumnType {
  /**
   * 文本类型 - 存储字符串数据
   * 适用场景:标题、内容、时间字符串等
   */
  TEXT = 'TEXT',
  
  /**
   * 整型 - 存储整数数据
   * 数值范围:±2,147,483,647
   * 适用场景:ID、计数器、状态码等
   */
  INTEGER = 'INTEGER',
  
  /**
   * 浮点型 - 存储小数数据
   * 适用场景:价格、评分、坐标等
   */
  REAL = 'REAL',
  
  /**
   * 二进制对象 - 存储二进制数据
   * 适用场景:布尔值、图片、文件等
   */
  BLOB = 'BLOB'
}

类型选择建议:

  • TEXT:字符串数据,如标题、内容

  • INTEGER:整数数据,如ID、数量

  • REAL:浮点数据,如价格、评分

  • BLOB:二进制数据,如布尔值、文件

列属性配置

@Columns 装饰器支持丰富的配置选项,满足各种业务需求:

/**
 * 数据库列配置接口
 * 提供了丰富的约束和转换选项
 */
export interface ColumnInfo {
  /**
   * 数据库列名
   * 不指定时使用属性名
   */
  name?: string
  
  /**
   * 列数据类型(必填)
   * 决定了数据在数据库中的存储格式
   */
  type: ColumnType
  
  /**
   * 主键约束
   * 确保数据唯一性,一个表只能有一个主键
   */
  isPrimaryKey?: boolean
  
  /**
   * 自增约束
   * 仅适用于整型主键,自动生成唯一值
   */
  autoincrement?: boolean
  
  /**
   * 唯一性约束
   * 确保列值在表中唯一,插入时会进行校验
   */
  unique?: boolean
  
  /**
   * 非空约束
   * 确保列值不能为空
   */
  notNull?: boolean
  
  /**
   * 类型转换器
   * 用于业务类型与数据库类型之间的转换
   * 例如:Date 对象 ↔ 时间戳字符串
   */
  convertor?: ClassConstructor<PropertyConverter<Object, Object>>
}

配置选项说明:

  • 约束配置:主键、唯一性、非空等约束确保数据完整性

  • 自动化功能:自增主键减少手动ID管理的复杂性

  • 类型转换:支持复杂对象与数据库类型的自动转换

工具类封装

数据库相关功能封装

// src/main/ets/utils/dbHelper.ets
 import { EventParamsBaseType, IEventSender, ILog, Rdb, RdbDao, RdbDatabase } from "rdbstore";
import { relationalStore } from "@kit.ArkData";
import { hilog } from "@kit.PerformanceAnalysisKit";
import { TreeHoleEntity } from "../model/TBTreeHole";


// 日志级别枚举定义
enum LogLevel {
    DEBUG = 0,
    INFO = 1,
    WARN = 2,
    ERROR = 3
}

class DBLog implements ILog {
    private static domain: number = 0x0000;
    private static isLogEnabled: boolean = true; // 总开关
    private static logLevel: LogLevel = LogLevel.DEBUG; // 默认级别

    // 日志级别枚举
    static setLogLevel(level: LogLevel): void {
        DBLog.logLevel = level;
    }

    // 全局开关控制
    static enableLog(): void {
        DBLog.isLogEnabled = true;
    }

    static disableLog(): void {
        DBLog.isLogEnabled = false;
    }

    d(tag: string, message: string): void {
        if (!DBLog.isLogEnabled || DBLog.logLevel > LogLevel.DEBUG) {
            return;
        }
        hilog.debug(DBLog.domain, tag, '%{public}s', message);
    }

    i(tag: string, message: string): void {
        if (!DBLog.isLogEnabled || DBLog.logLevel > LogLevel.INFO) {
            return;
        }
        hilog.info(DBLog.domain, tag, '%{public}s', message);
    }

    w(tag: string, message: string): void {
        if (!DBLog.isLogEnabled || DBLog.logLevel > LogLevel.WARN) {
            return;
        }
        hilog.warn(DBLog.domain, tag, '%{public}s', message);
    }

    e(tag: string, message: string): void {
        if (!DBLog.isLogEnabled || DBLog.logLevel > LogLevel.ERROR) {
            return;
        }
        hilog.error(DBLog.domain, tag, '%{public}s', message);
    }
}

class DBEventSender implements IEventSender{
    send(key: string, params: Record<string, EventParamsBaseType>): void {
        console.log(key)
    }
}


export default class DbHelper {
    static readonly DB_VERSION: number = 6
    static readonly DB_NAME: string = "treeHole.db"
    private static INSTANCE: DbHelper;
    private context?: Context;
    private db?: RdbDatabase;

    // 私有化构造函数
    private constructor() {

    }

    // 单例获取方法
    static getInstance(): DbHelper {
        if (!DbHelper.INSTANCE) {
            DbHelper.INSTANCE = new DbHelper();
        }
        return DbHelper.INSTANCE;
    }

    async init(context: Context) {
        DBLog.enableLog();
        DBLog.setLogLevel(LogLevel.DEBUG);
        this.db = Rdb.databaseBuilder(context, {
            version: DbHelper.DB_VERSION,
            dbName: DbHelper.DB_NAME,
            entities: [TreeHoleEntity],
            migrations: [],
            encrypt: false,
            securityLevel: relationalStore.SecurityLevel.S1,
            autoMigrate: true
        })
            .setLogger(new DBLog())
            .setEvent(new DBEventSender())
            .build()

        await this.initDb()
    }

    getDb(): RdbDatabase {
        if (!this.db) {
            throw new Error('db not initialized');
        }
        return this.db;
    }

    initDb() {
        this.db?.initTask
    }

    async getTBTreeHoleDAO(): Promise<RdbDao<TreeHoleEntity>> {
        if (!this.db) {
            throw new Error('db not initialized');
        }
        return await this.db.getDao(TreeHoleEntity)
    }

}


代码解析:

  • 初始化数据库

  • 获取数据库帮助类单例实例

API 接口设计

数据访问层是连接业务逻辑和数据存储的桥梁,良好的API设计能大大提升开发效率:

// src/main/ets/common/api/treeHoleApi.ets 

import { TreeHoleEntity } from "../../model/TBTreeHole"
import DbHelper from "../../utils/dbHelper"

/**
 * 树洞数据访问接口
 * 封装所有与树洞相关的数据库操作
 * 
 * 设计原则:
 * - 单一职责:只处理树洞相关的数据操作
 * - 统一接口:提供一致的异步操作接口
 * - 错误处理:统一的异常处理机制
 */
export default class DBTreeHoleApi {

    /**
     * 创建新的树洞记录
     * 
     * @param entity 树洞实体对象
     * @returns 影响的行数,>0 表示成功
     * 
     * 使用示例:
     * const result = await DBTreeHoleApi.insertCard(newTreeHole);
     * if (result > 0) console.log('创建成功');
     */
    static async insertCard(entity: TreeHoleEntity): Promise<number> {
        // 获取数据访问对象(DAO)
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        // 执行插入操作,返回影响行数
        return await dao.insert(entity)
    }

    /**
     * 批量创建树洞记录
     * 适用于数据导入、批量操作等场景
     * 
     * @param entries 树洞实体数组
     * @returns 影响的行数
     * 
     * 性能优势:
     * - 减少数据库连接次数
     * - 事务性操作,要么全部成功要么全部失败
     */
    static async batchInsertCard(entries: Array<TreeHoleEntity>): Promise<number> {
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        return await dao.batchInsert(entries)
    }

    /**
     * 删除指定的树洞记录
     * 
     * @param entity 要删除的树洞实体(需要包含主键信息)
     * @returns 影响的行数,>0 表示删除成功
     * 
     * 注意事项:
     * - 删除操作不可逆,建议在UI层添加确认提示
     * - 可以考虑软删除(标记删除状态)而非物理删除
     */
    static async deleteCard(entity: TreeHoleEntity): Promise<number> {
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        return await dao.delete(entity)
    }

    /**
     * 查询所有树洞记录
     * 
     * @returns 树洞实体数组,可能为空数组
     * 
     * 查询逻辑:
     * - 使用谓词构建器构建查询条件
     * - 不添加 where 条件表示查询全部
     * - 结果按创建时间倒序排列(最新的在前)
     */
    static async queryAllCard(): Promise<TreeHoleEntity[] | undefined> {
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        // 构建查询:无条件查询所有记录
        const entities = await dao.predicatesBuilder().build().run()
        return entities
    }

    /**
     * 根据ID查询特定树洞记录
     * 
     * @param id 树洞的唯一标识符(uid字段)
     * @returns 匹配的树洞实体数组
     * 
     * 查询逻辑:
     * - 获取 uid 属性的谓词对象
     * - 使用 equalTo 方法构建等值查询条件
     * - 返回数组格式(虽然ID唯一,但保持接口一致性)
     */
    static async queryItemCard(id: number): Promise<TreeHoleEntity[] | undefined> {
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        // 获取 uid 字段的属性对象
        const uIdProp = dao.getProperty('uid')
        // 构建查询条件:uid = id
        const entities = await dao.predicatesBuilder()
            .where(uIdProp.equalTo(id))
            .build()
            .run()
        return entities
    }

    /**
     * 更新树洞记录
     * 
     * @param entity 包含更新数据的树洞实体(必须包含主键)
     * @returns 影响的行数,>0 表示更新成功
     * 
     * 更新策略:
     * - 根据主键(uid)定位记录
     * - 更新所有非主键字段
     * - 保持数据完整性和一致性
     */
    static async updateCard(entity: TreeHoleEntity): Promise<number> {
        const dao = await DbHelper.getInstance().getTBTreeHoleDAO()
        return await dao.update(entity)
    }
}

API设计亮点:

  1. 静态方法:无需实例化,直接调用,简化使用

  2. 统一返回:增删改操作返回影响行数,查询操作返回实体数组

  3. 异步操作:所有方法都是异步的,避免阻塞UI线程

应用初始化

依赖导入

在应用入口文件中导入必要的依赖:

// src/main/ets/entryability/EntryAbility.ets

// HarmonyOS 核心能力包
import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';

// 应用工具类
import PreferencesUtils from '../utils/PreferencesUtils';
import DbHelper from '../utils/dbHelper';
import { CustomStyle } from "../types/types"

导入说明:

  • 系统能力包:HarmonyOS 提供的核心功能模块

  • 工具类:应用自定义的工具和辅助类

  • 类型定义:TypeScript 类型定义,提供类型安全

生命周期初始化

在应用的关键生命周期中初始化存储组件:

// 在 onWindowStageCreate 生命周期中初始化存储组件
onWindowStageCreate(windowStage: window.WindowStage): void {
    // 初始化首选项存储
    PreferencesUtils.createPreferences(this.context);
    
    // 初始化数据库
    // 获取数据库帮助类单例实例
    DbHelper.getInstance();
    // 使用应用上下文初始化数据库连接
    DbHelper.getInstance().init(this.context);
    
    // 其他窗口初始化逻辑...
}

初始化流程解析:

  1. 首选项初始化:创建首选项存储实例,用于轻量级配置存储

  2. 数据库初始化

    • 获取数据库帮助类的单例实例

    • 使用应用上下文初始化数据库连接

    • 自动创建表结构和处理版本升级

  3. 时机选择:在 onWindowStageCreate 中初始化确保:

    • 应用上下文已经准备就绪

    • 在UI创建之前完成数据层初始化

    • 避免后续操作中的初始化延迟

CRUD 操作实战

完成基础配置后,我们来实现完整的数据库 CRUD 操作。以下代码展示了在实际业务场景中如何使用 RdbStore 进行数据操作。

Create - 数据创建

依赖导入

// src/main/ets/pages/CreateTreeHolePage.ets

// 数据访问接口
import DBTreeHoleApi from "../common/api/treeHoleApi"
// 数据模型
import { TreeHoleEntity } from "../model/TBTreeHole"
// UI交互组件
import { promptAction } from '@kit.ArkUI'
// 路由导航
import router from '@ohos.router'

核心创建逻辑

以下是完整的数据创建和更新逻辑,支持新建和编辑两种模式:

/**
 * 提交树洞数据的核心方法
 * 支持新建和编辑两种模式,统一处理业务逻辑
 */
async submitTreeHole() {
    console.log('=== 开始提交树洞数据 ===')
    console.log('当前内容:', this.content)
    console.log('当前标题:', this.title)
    console.log('是否编辑模式:', this.isEditMode)

    // 数据验证:标题和内容不能都为空
    if (this.content.trim().length === 0 && this.title.trim().length === 0) {
        console.log('提交失败:标题和内容不能都为空')
        promptAction.showToast({
            message: '请输入标题或内容',
            duration: 2000
        })
        return
    }

    console.log('开始提交树洞数据...')
    this.isSubmitting = true; // 设置提交状态,防止重复提交

    try {
        if (this.isEditMode && this.editingTreeHole) {
            // 编辑模式:更新现有树洞
            await this.updateExistingTreeHole()
        } else {
            // 创建模式:插入新树洞
            await this.createNewTreeHole()
        }
    } catch (error) {
        console.error('提交树洞数据时发生错误:', JSON.stringify(error))
        promptAction.showToast({
            message: '操作失败,请重试',
            duration: 2000
        })
    } finally {
        this.isSubmitting = false // 重置提交状态
    }
}

/**
 * 更新现有树洞记录
 * 保持原有的创建时间和ID,只更新可编辑字段
 */
private async updateExistingTreeHole() {
    console.log('执行更新操作...')

    // 更新树洞数据
    this.editingTreeHole.title = this.title
    this.editingTreeHole.content = this.content
    this.editingTreeHole.moods = this.selectedMoods.join(',')
    this.editingTreeHole.isAnonymous = this.isAnonymous

    console.log('准备更新的数据:', JSON.stringify({
        id: this.editingTreeHole.id,
        title: this.editingTreeHole.title,
        content: this.editingTreeHole.content,
        moods: this.editingTreeHole.moods,
        createTime: this.editingTreeHole.createTime,
        isAnonymous: this.editingTreeHole.isAnonymous
    }))

    // 调用数据库更新操作
    const updateResult = await DBTreeHoleApi.updateCard(this.editingTreeHole)
    console.log('数据库更新结果:', updateResult)

    if (updateResult > 0) {
        console.log('树洞数据更新成功,更新行数:', updateResult)
        promptAction.showToast({
            message: '树洞更新成功',
            duration: 2000
        })
        router.back() // 返回上一页
    } else {
        promptAction.showToast({
            message: '树洞更新失败',
            duration: 2000
        })
        console.error('数据库更新失败,返回值:', updateResult)
    }
}

/**
 * 创建新的树洞记录
 * 生成新的ID和时间戳,插入完整的数据记录
 */
private async createNewTreeHole() {
    console.log('执行创建操作...')

    // 生成业务数据
    const uniqueId = this.generateUniqueId();      // 生成唯一业务ID
    const currentTime = this.getCurrentDateTime(); // 获取当前时间

    console.log('生成的唯一ID:', uniqueId)
    console.log('当前时间:', currentTime)
    console.log('选中的心情标签:', this.selectedMoods)

    // 构建新的树洞实体
    const newTreeHole = new TreeHoleEntity();
    newTreeHole.id = uniqueId;                                    // 业务ID
    newTreeHole.title = this.title;                               // 标题
    newTreeHole.content = this.content;                           // 内容
    newTreeHole.moods = this.selectedMoods.join(',');            // 心情标签(逗号分隔)
    newTreeHole.createTime = currentTime;                         // 创建时间
    newTreeHole.isAnonymous = this.isAnonymous;                   // 匿名状态

    console.log('准备插入的数据:', JSON.stringify({
        id: newTreeHole.id,
        title: newTreeHole.title,
        content: newTreeHole.content,
        moods: newTreeHole.moods,
        createTime: newTreeHole.createTime,
        isAnonymous: newTreeHole.isAnonymous
    }))

    // 调用数据库插入操作
    const insertResult = await DBTreeHoleApi.insertCard(newTreeHole)
    console.log('数据库插入结果:', insertResult)

    if (insertResult > 0) {
        console.log('树洞数据插入成功,插入行数:', insertResult)
        promptAction.showToast({
            message: '树洞发布成功',
            duration: 2000
        })
        router.back() // 返回上一页
    } else {
        promptAction.showToast({
            message: '树洞发布失败',
            duration: 2000
        })
        console.error('数据库插入失败,返回值:', insertResult)
    }
}

代码解析:

  1. 数据验证:在提交前验证必填字段,提升用户体验

  2. 状态管理:使用 isSubmitting 防止重复提交

  3. 模式区分:根据 isEditMode 区分新建和编辑操作

  4. 错误处理:完善的异常捕获和用户提示

  5. 日志记录:详细的操作日志,便于调试和问题定位

Read - 数据读取

依赖导入

// src/main/ets/pages/TreeHoleListPage.ets

// 数据访问接口
import DBTreeHoleApi from "../common/api/treeHoleApi"
// 数据模型
import { TreeHoleEntity } from "../model/TBTreeHole"
// UI交互组件
import { promptAction } from '@kit.ArkUI'

数据查询实现

实现完整的数据查询和状态管理逻辑:

/**
 * 页面状态定义
 */
@State treeHoles: TreeHoleEntity[] = []        // 树洞数据列表
@State showDialog: boolean = false             // 对话框显示状态
@State isLoading: boolean = false              // 加载状态
@State isEmpty: boolean = false                // 空数据状态

/**
 * 页面显示时的生命周期回调
 * 每次页面显示时都会重新加载数据,确保数据最新
 */
onPageShow() {
    console.log('页面显示,开始加载树洞数据')
    this.loadMyTreeHoles()
}

/**
 * 切换对话框显示状态
 */
handleShow = () => {
    this.showDialog = !this.showDialog
}

/**
 * 加载树洞数据的核心方法
 * 从数据库查询所有树洞记录并更新UI状态
 */
async loadMyTreeHoles() {
    console.log('开始加载树洞数据...')
    this.isLoading = true // 显示加载状态

    try {
        // 调用API查询所有树洞数据
        const allTreeHoles = await DBTreeHoleApi.queryAllCard()
        
        if (allTreeHoles && allTreeHoles.length > 0) {
            console.log('查询到的树洞数量:', allTreeHoles.length)
            
            // 数据处理:按创建时间倒序排列(最新的在前)
            this.treeHoles = allTreeHoles.sort((a, b) => {
                return new Date(b.createTime).getTime() - new Date(a.createTime).getTime()
            })
            
            this.isEmpty = false
            console.log('树洞数据加载成功')
        } else {
            // 无数据情况
            this.treeHoles = []
            this.isEmpty = true
            console.log('暂无树洞数据')
        }
    } catch (error) {
        console.error('加载树洞数据失败:', error)
        promptAction.showToast({
            message: '数据加载失败,请重试',
            duration: 2000
        })
        this.treeHoles = []
        this.isEmpty = true
    } finally {
        this.isLoading = false // 隐藏加载状态
    }
}

/**
 * 刷新数据
 * 提供下拉刷新功能
 */
async refreshData() {
    console.log('用户触发数据刷新')
    await this.loadMyTreeHoles()
    promptAction.showToast({
        message: '刷新完成',
        duration: 1000
    })
}

代码解析:

  1. 状态管理:使用多个状态变量管理不同的UI状态

  2. 生命周期:在 onPageShow 中加载数据,确保数据实时性

  3. 数据排序:按创建时间倒序显示,提升用户体验

  4. 空状态处理:区分加载中、空数据、错误等不同状态

  5. 用户反馈:提供加载提示和操作结果反馈

Delete - 数据删除

实现安全的数据删除操作,包含确认机制和状态同步:

/**
 * 删除树洞记录
 * 包含确认提示和状态同步机制
 * 
 * @param item 要删除的树洞实体
 */
async deleteTreeHole(item: TreeHoleEntity) {
    console.log('准备删除树洞:', item.id, item.title)
    
    try {
        // 显示确认对话框(可选,根据UI设计决定)
        // const confirmed = await this.showConfirmDialog('确定要删除这条树洞吗?')
        // if (!confirmed) return
        
        // 调用删除API
        const deleteResult = await DBTreeHoleApi.deleteCard(item)
        
        if (deleteResult > 0) {
            console.log('数据库删除成功,影响行数:', deleteResult)
            
            // 从本地列表中移除(优化用户体验,避免重新查询)
            this.treeHoles = this.treeHoles.filter(treeHole => treeHole.id !== item.id)
            
            // 更新空状态
            this.isEmpty = this.treeHoles.length === 0
            
            promptAction.showToast({
                message: '删除成功',
                duration: 2000
            })
            
            console.log('树洞删除成功:', item.id)
        } else {
            console.error('数据库删除失败,返回值:', deleteResult)
            promptAction.showToast({
                message: '删除失败,请重试',
                duration: 2000
            })
        }
    } catch (error) {
        console.error('删除树洞时发生错误:', error)
        promptAction.showToast({
            message: '删除失败,请重试',
            duration: 2000
        })
    }
}

/**
 * 批量删除树洞(扩展功能)
 * 适用于管理界面的批量操作
 * 
 * @param items 要删除的树洞实体数组
 */
async batchDeleteTreeHoles(items: TreeHoleEntity[]) {
    console.log('准备批量删除树洞,数量:', items.length)
    
    if (items.length === 0) {
        promptAction.showToast({
            message: '请选择要删除的项目',
            duration: 2000
        })
        return
    }
    
    try {
        let successCount = 0
        let failCount = 0
        
        // 逐个删除(也可以考虑实现批量删除API)
        for (const item of items) {
            try {
                const result = await DBTreeHoleApi.deleteCard(item)
                if (result > 0) {
                    successCount++
                } else {
                    failCount++
                }
            } catch (error) {
                console.error('删除单个项目失败:', item.id, error)
                failCount++
            }
        }
        
        // 刷新数据列表
        await this.loadMyTreeHoles()
        
        // 显示操作结果
        if (failCount === 0) {
            promptAction.showToast({
                message: `成功删除 ${successCount} 条记录`,
                duration: 2000
            })
        } else {
            promptAction.showToast({
                message: `删除完成:成功 ${successCount} 条,失败 ${failCount} 条`,
                duration: 3000
            })
        }
        
        console.log('批量删除完成,成功:', successCount, '失败:', failCount)
    } catch (error) {
        console.error('批量删除过程中发生错误:', error)
        promptAction.showToast({
            message: '批量删除失败',
            duration: 2000
        })
    }
}

代码解析:

  1. 安全删除:可以添加确认对话框,防止误删

  2. 状态同步:删除成功后立即更新本地状态,提升响应速度

  3. 错误处理:完善的异常处理和用户提示

  4. 批量操作:提供批量删除功能,适用于管理场景

  5. 用户反馈:详细的操作结果提示

结语

RdbStore 为 HarmonyOS 5 应用开发提供了强大而易用的数据持久化解决方案。通过合理的架构设计和最佳实践,我们可以构建出高性能、可维护的数据层。 希望本文能够帮助你快速掌握 RdbStore 的使用方法,在实际项目中发挥其强大的能力。如果你有任何问题或建议,欢迎在评论区交流讨论!

Logo

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

更多推荐