
一、前言前面讲解的Preferences仅适合存储轻量键值对面对通讯录、备忘录、商品清单、大量结构化数据时能力不足。OpenHarmony 提供RDB关系型数据库基于 SQLite 内核支持标准 SQL 语句、数据表创建、增删改查、事务操作是应用存储大批量结构化数据的首选方案。本文从环境依赖、数据库初始化、数据表设计、基础 CRUD、分页查询、事务操作一步步讲解搭配完整可运行案例同时结合前文组件、弹窗完成综合业务实战。适用场景备忘录、联系人、本地账单、离线缓存列表等复杂数据存储。 依赖模块ohos.data.rdb二、核心概念与前置说明核心对象RdbStore数据库实例执行增删改查、事务的核心对象RdbHelper数据库工具类负责打开 / 创建数据库ResultSet查询结果集遍历读取查询数据数据类型支持整型、浮点型、字符串、布尔值兼容 SQLite 字段规则权限本地数据库无需额外申请权限应用沙盒内私有存储三、基础使用数据库与数据表初始化3.1 导入模块 工具类封装统一封装数据库工具避免重复编写初始化代码便于项目维护。etsimport rdb from ohos.data.rdb import common from ohos.app.ability.common // 数据库全局工具类 class RdbUtil { private static rdbStore: rdb.RdbStore | null null // 数据库名称 private static readonly DB_NAME app_database.db // 数据库版本 private static readonly DB_VERSION 1 /** * 初始化数据库 创建数据表 * param context 应用上下文 */ static async initDB(context: common.UIAbilityContext) { if (this.rdbStore) { return this.rdbStore } // 数据库配置 let config: rdb.RdbStoreConfig { name: this.DB_NAME, securityLevel: rdb.SecurityLevel.S1 } // 打开/创建数据库 this.rdbStore await rdb.getRdbStore(context, config, this.DB_VERSION, (store) { // 数据库首次创建时执行建表语句 const createTableSql CREATE TABLE IF NOT EXISTS user_note ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, create_time TEXT ) store.executeSql(createTableSql) }) return this.rdbStore } // 获取数据库实例 static getStore(): rdb.RdbStore | null { return this.rdbStore } }数据表user_note简易备忘录表id自增主键title标题content内容create_time创建时间。版本升级如需修改表结构递增DB_VERSION并在版本回调中编写更新 SQL。3.2 应用入口初始化数据库在页面生命周期中完成数据库初始化全局只需执行一次。etsEntry Component struct RdbIndex { context getContext(this) as common.UIAbilityContext aboutToAppear() { // 初始化数据库 RdbUtil.initDB(this.context) } build() { Column() { Text(RDB 关系型数据库演示) .fontSize(22) .fontWeight(FontWeight.Bold) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }四、数据表 CRUD 基础操作4.1 新增数据插入记录使用insert方法插入单条数据接收表名与数据对象。etsEntry Component struct RdbInsertDemo { context getContext(this) as common.UIAbilityContext // 插入备忘录数据 async insertNote() { let store await RdbUtil.initDB(this.context) if (!store) return // 构造插入数据 let valueBucket: rdb.ValuesBucket { title: 第一条备忘录, content: 学习 OpenHarmony RDB 数据库, create_time: new Date().toLocaleString() } // 执行插入 let rowId await store.insert(user_note, valueBucket) console.info(插入成功行ID, rowId) } build() { Column({ space: 20 }) { Button(新增一条备忘录) .width(220) .onClick(() this.insertNote()) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) } }4.2 查询数据全量查询 条件查询使用query方法查询数据通过ResultSet遍历结果集。etsEntry Component struct RdbQueryDemo { State noteList: string[] [] context getContext(this) as common.UIAbilityContext // 查询全部数据 async queryAll() { let store await RdbUtil.initDB(this.context) if (!store) return // 查询条件查所有字段、无筛选条件 let predicates new rdb.RdbPredicates(user_note) let resultSet await store.query(predicates) this.noteList [] // 遍历结果集 while (resultSet.goToNextRow()) { let id resultSet.getLong(resultSet.getColumnIndex(id)) let title resultSet.getString(resultSet.getColumnIndex(title)) let time resultSet.getString(resultSet.getColumnIndex(create_time)) this.noteList.push(ID:${id} 标题:${title} 时间:${time}) } // 关闭结果集释放资源 resultSet.close() } build() { Column({ space: 15 }) { Button(查询全部备忘录).onClick(() this.queryAll()) List({ space: 5 }) { ForEach(this.noteList, (item: string) { ListItem() { Text(item).fontSize(16).padding(10) } }) } .layoutWeight(1) .width(100%) } .width(100%) .height(100%) .padding(20) } }条件查询示例根据标题模糊查询etsasync queryByTitle(keyword: string) { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) // 模糊匹配 predicates.like(title, %${keyword}%) let resultSet await store.query(predicates) // 遍历逻辑同上 resultSet.close() }4.3 修改数据通过RdbPredicates设置条件使用update方法更新字段。etsasync updateNote() { let store RdbUtil.getStore() if (!store) return // 要更新的内容 let valueBucket: rdb.ValuesBucket { title: 修改后的标题, content: 内容已更新 } // 更新条件id 1 let predicates new rdb.RdbPredicates(user_note) predicates.equalTo(id, 1) let count await store.update(valueBucket, predicates) console.info(受影响行数, count) }4.4 删除数据支持按条件删除、清空整张表。ets// 按ID删除 async deleteNoteById(delId: number) { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) predicates.equalTo(id, delId) let count await store.delete(predicates) console.info(删除行数, count) } // 清空整张表 async clearTable() { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) await store.delete(predicates) }五、进阶用法分页查询数据量较大时使用分页加载提升性能核心依靠predicates.offset()和predicates.limit()。ets/** * 分页查询 * param pageIndex 页码从0开始 * param pageSize 每页条数 */ async queryByPage(pageIndex: number, pageSize: number) { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) predicates.offset(pageIndex * pageSize) // 偏移量 predicates.limit(pageSize) // 每页数量 predicates.orderByDesc(id) // 按ID倒序 let resultSet await store.query(predicates) // 遍历读取数据 resultSet.close() }六、进阶用法事务操作多条数据批量增删改时使用事务保证数据一致性要么全部执行成功要么全部回滚。etsasync batchInsertWithTransaction() { let store RdbUtil.getStore() if (!store) return try { // 开启事务 await store.beginTransaction() // 批量插入多条数据 let data1: rdb.ValuesBucket { title: 事务笔记1, content: 测试事务, create_time: new Date().toLocaleString() } let data2: rdb.ValuesBucket { title: 事务笔记2, content: 测试事务, create_time: new Date().toLocaleString() } await store.insert(user_note, data1) await store.insert(user_note, data2) // 提交事务 await store.commit() console.info(事务执行成功) } catch (err) { // 异常回滚 await store.rollBack() console.error(事务执行失败已回滚, err) } }七、综合实战备忘录完整页面RDB 自定义弹窗 List整合前面所学的CustomDialog自定义弹窗、List 列表、RDB 数据库实现新增、查看、删除备忘录完整功能。7.1 新增笔记弹窗etsCustomDialog struct AddNoteDialog { controller: CustomDialogController DialogParam onSubmit: (title: string, content: string) void State noteTitle: string State noteContent: string build() { Column({ space: 15 }) { Text(新增备忘录).fontSize(20).fontWeight(FontWeight.Bold) TextInput({ placeholder: 请输入标题 }) .width(100%) .height(45) .onChange(v this.noteTitle v) TextInput({ placeholder: 请输入内容 }) .width(100%) .height(80) .onChange(v this.noteContent v) Row({ space: 20 }) { Button(取消) .layoutWeight(1) .backgroundColor(#999) .onClick(() this.controller.close()) Button(保存) .layoutWeight(1) .backgroundColor(#007DFF) .onClick(() { this.onSubmit(this.noteTitle, this.noteContent) this.controller.close() }) } } .width(320) .padding(20) .backgroundColor(Color.White) .borderRadius(12) } }7.2 主页面列表展示 增删操作etsEntry Component struct NoteMainPage { State noteArr: Arrayrdb.ValuesBucket [] context getContext(this) as common.UIAbilityContext // 弹窗控制器 addDialog: CustomDialogController new CustomDialogController({ builder: AddNoteDialog({ onSubmit: (title: string, content: string) { this.saveNote(title, content) } }), autoCancel: false }) aboutToAppear() { // 初始化数据库 加载列表 RdbUtil.initDB(this.context).then(() { this.loadNoteList() }) } // 保存笔记到数据库 async saveNote(title: string, content: string) { let store RdbUtil.getStore() if (!store || !title) return let bucket: rdb.ValuesBucket { title: title, content: content, create_time: new Date().toLocaleString() } await store.insert(user_note, bucket) // 重新刷新列表 this.loadNoteList() } // 加载列表数据 async loadNoteList() { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) predicates.orderByDesc(id) let resultSet await store.query(predicates) let tempList: Arrayrdb.ValuesBucket [] while (resultSet.goToNextRow()) { tempList.push({ id: resultSet.getLong(resultSet.getColumnIndex(id)), title: resultSet.getString(resultSet.getColumnIndex(title)), content: resultSet.getString(resultSet.getColumnIndex(content)), create_time: resultSet.getString(resultSet.getColumnIndex(create_time)) }) } resultSet.close() this.noteArr tempList } // 删除单条笔记 async deleteItem(id: number) { let store RdbUtil.getStore() if (!store) return let predicates new rdb.RdbPredicates(user_note) predicates.equalTo(id, id) await store.delete(predicates) this.loadNoteList() } build() { Column() { // 顶部标题新增按钮 Row() { Text(我的备忘录) .fontSize(22) .fontWeight(FontWeight.Bold) .layoutWeight(1) Button(新增) .width(80) .onClick(() this.addDialog.open()) } .width(100%) .padding(15) // 笔记列表 List({ space: 8 }) { ForEach(this.noteArr, (item: rdb.ValuesBucket) { ListItem() { Row() { Column({ space: 5 }) { Text(item.title as string) .fontSize(18) .fontWeight(FontWeight.Bold) Text(item.content as string) .fontSize(14) .fontColor(#666) Text(item.create_time as string) .fontSize(12) .fontColor(#999) } .layoutWeight(1) Button(删除) .width(70) .backgroundColor(#f56c6c) .onClick(() this.deleteItem(item.id as number)) } .width(100%) .padding(12) .backgroundColor(Color.White) .borderRadius(8) } }) } .layoutWeight(1) .width(100%) } .width(100%) .height(100%) .backgroundColor(#F5F5F5) } }八、开发规范与踩坑总结8.1 使用规范资源释放ResultSet使用完毕必须调用close()避免内存泄漏。数据库初始化全局仅初始化一次建议放在应用入口页面。批量操作多条增删改优先使用事务提升执行效率、保证数据安全。分表原则不同业务数据拆分不同数据表不要全部存在一张表。版本管理表结构变更时递增数据库版本号编写版本升级逻辑。8.2 常见问题表不存在检查建表 SQL 语句是否正确数据库初始化是否执行。查询无数据核对字段名、查询条件大小写SQLite 默认区分字段名。卡顿问题大数据列表必须使用分页查询禁止一次性查询全表。线程阻塞所有数据库操作均为异步统一使用async/await。8.3 选型对比表格存储方案适用场景优缺点Preferences轻量配置、开关、登录状态简单易用不支持复杂结构RDB大量结构化数据、列表数据支持 SQL、事务、分页功能强大九、系列总结 后续拓展至此整套OpenHarmony 应用开发核心技术栈全部完结 UI 组件 → 布局 / 懒加载 → 路由传参 → Preferences → 网络请求 → AppStorage 全局状态 → 自定义弹窗 → RDB 数据库