发布时间:2026/9/6 20:25:49
OpenHarmony 关系型数据库 RDB 完整封装(API Version23 本地持久化数据库) 摘要RDB 是鸿蒙内置轻量关系型数据库基于 SQLite 封装适合存储结构化大量数据笔记、用户列表、商品数据相比 Preferences 仅适合简单键值对RDB 支持表、字段、主键、条件分页查询、多条件筛选、批量增删改查。API23 重构数据库实例生命周期、事务、字段类型校验、并发锁机制修复旧版数据库未关闭导致占用、大量数据查询卡顿、事务回滚失效、多页面同时操作崩溃等问题。本文封装通用 RDB 工具基类附带笔记业务数据表完整 CRUD、分页查询、事务批量插入实战代码给出数据库初始化、资源释放、事务使用规范适配所有需要本地大量结构化存储的项目。关键词OpenHarmonyArkTSAPI23relationalStoreRDB本地数据库SQLite事务分页查询一、引言1.1 RDB 开发背景本地存储分为两类Preferences小型键值存储仅存配置、开关、token不支持复杂查询RDB 关系数据库结构化数据表支持主键、自增、WHERE 条件、分页、批量操作适合笔记、通讯录、本地缓存列表等多条业务数据。API23 RDB 核心升级点强制规范 RdbStore 实例生命周期不用必须 close防止文件锁占用完善事务 transaction 原子操作批量插入失败自动回滚优化 RdbPredicates 条件构造器替代手写拼接 SQL 防注入统一字段类型强校验区分数字、文本、布尔多页面并发读写加内置读写锁避免同时操作数据库崩溃升级大数量查询内存优化分页读取不一次性加载全部数据。1.2 开发环境与场景模块ohos.data.relationalStore 无需额外动态权限应用沙盒自有数据库读写权限 测试场景笔记新增 / 删除 / 修改、分页加载列表、批量插入事务、按标题模糊搜索、清空数据表二、API23 RDB 核心基础 API2.1 数据库初始化配置etsimport relationalStore from ohos.data.relationalStore const storeConfig: relationalStore.StoreConfig { name: app_db, // 数据库文件名 securityLevel: relationalStore.SecurityLevel.S1 // 安全等级 } // 获取数据库实例 const rdbStore await relationalStore.getRdbStore(context, storeConfig)2.2 建表 SQL 示例sqlCREATE TABLE IF NOT EXISTS note ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, create_time INTEGER )2.3 核心操作方法executeSql (sql)执行建表、删除表、自定义 SQLinsert (table, values)新增单条数据update (table, values, predicates)按条件修改delete (table, predicates)按条件删除query (table, columns, predicates)条件查询返回 ResultSetbeginTransaction /commit/rollback事务批量操作close ()关闭数据库释放文件锁2.4 RdbPredicates 条件构造器防 SQL 注入etslet predicates new relationalStore.RdbPredicates(note) predicates.equalTo(id, 1) // id1 predicates.contains(title, 鸿蒙) // 模糊搜索标题 predicates.orderByDesc(create_time) // 按创建时间倒序 predicates.offset(0).limit(10) // 分页2.5 API23 强制约束页面销毁、工具释放必须调用 close ()否则数据库文件持续锁定无法删除复杂查询禁止手写拼接字符串 SQL统一使用 RdbPredicates批量新增、批量删除必须包裹事务避免中途插入一半数据错乱ResultSet 使用完毕必须 close否则内存持续占用禁止全局单例长期持有 RdbStore切换页面重新获取实例。三、通用 RDB 底层工具封装 utils/rdb_util.etsetsimport relationalStore from ohos.data.relationalStore import common from ohos.app.ability.common import promptAction from ohos.promptAction // 笔记实体类型导出 export interface Note { id: number title: string content: string create_time: number } // 数据库配置常量 const DB_NAME note_database const TABLE_NOTE note class RdbUtil { private static instance: RdbUtil private ctx: common.UIAbilityContext | null null private store: relationalStore.RdbStore | null null static getInstance(): RdbUtil { if (!RdbUtil.instance) { RdbUtil.instance new RdbUtil() } return RdbUtil.instance } // 注入页面上下文 setContext(context: common.UIAbilityContext) { this.ctx context } // 初始化数据库自动建表 async init(): Promiseboolean { if (!this.ctx) { promptAction.showToast({ message: 上下文未初始化 }) return false } try { const config: relationalStore.StoreConfig { name: DB_NAME, securityLevel: relationalStore.SecurityLevel.S1 } this.store await relationalStore.getRdbStore(this.ctx, config) // 创建笔记表 const createTableSql CREATE TABLE IF NOT EXISTS ${TABLE_NOTE} ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, create_time INTEGER ) await this.store.executeSql(createTableSql) return true } catch (err) { promptAction.showToast({ message: 数据库初始化失败 }) console.error(rdb init error, err) return false } } // 新增笔记返回自增id async addNote(title: string, content: string): Promisenumber { if (!this.store) return -1 const values: Recordstring, any { title, content, create_time: new Date().getTime() } const rowId await this.store.insert(TABLE_NOTE, values) return rowId } // 根据id删除笔记 async deleteNote(id: number): Promiseboolean { if (!this.store) return false const predicates new relationalStore.RdbPredicates(TABLE_NOTE) predicates.equalTo(id, id) const delCount await this.store.delete(TABLE_NOTE, predicates) return delCount 0 } // 修改笔记 async updateNote(id: number, title: string, content: string): Promiseboolean { if (!this.store) return false const values: Recordstring, any { title, content } const predicates new relationalStore.RdbPredicates(TABLE_NOTE) predicates.equalTo(id, id) const updateCount await this.store.update(TABLE_NOTE, values, predicates) return updateCount 0 } // 分页查询笔记列表时间倒序 async queryNoteList(page: number, pageSize: number, keyword: string ): PromiseNote[] { if (!this.store) return [] const predicates new relationalStore.RdbPredicates(TABLE_NOTE) // 模糊搜索 if (keyword) { predicates.contains(title, keyword) } predicates.orderByDesc(create_time) predicates.offset(page * pageSize).limit(pageSize) const resultSet await this.store.query(TABLE_NOTE, [id, title, content, create_time], predicates) const list: Note[] [] while (resultSet.goToNextRow()) { list.push({ id: resultSet.getLong(0), title: resultSet.getString(1), content: resultSet.getString(2), create_time: resultSet.getLong(3) }) } // 释放结果集 resultSet.close() return list } // 批量插入事务示例 async batchAddNote(noteArr: Array{ title: string, content: string }): Promiseboolean { if (!this.store) return false try { await this.store.beginTransaction() for (const item of noteArr) { await this.store.insert(TABLE_NOTE, { title: item.title, content: item.content, create_time: new Date().getTime() }) } await this.store.commit() return true } catch (err) { await this.store.rollback() console.error(批量插入失败事务回滚, err) return false } } // 清空整张笔记表 async clearAllNote(): Promisevoid { if (!this.store) return const predicates new relationalStore.RdbPredicates(TABLE_NOTE) await this.store.delete(TABLE_NOTE, predicates) } // 关闭数据库释放锁页面销毁必须调用 async closeDB() { if (this.store) { await this.store.close() this.store null } } } export default RdbUtil.getInstance()四、实战页面笔记增删改查分页页面etsimport RdbUtil, { Note } from ../utils/rdb_util import promptAction from ohos.promptAction Entry Component struct RdbNotePage { State noteList: Note[] [] State searchKey: string pageIndex: number 0 readonly PAGE_SIZE 10 async aboutToAppear() { const ctx getContext(this) RdbUtil.setContext(ctx) const initOk await RdbUtil.init() if (initOk) { await this.refreshList() } } // 刷新第一页数据 async refreshList() { this.pageIndex 0 this.noteList await RdbUtil.queryNoteList(this.pageIndex, this.PAGE_SIZE, this.searchKey) } // 加载下一页 async loadMore() { this.pageIndex 1 const more await RdbUtil.queryNoteList(this.pageIndex, this.PAGE_SIZE, this.searchKey) if (more.length 0) { this.noteList [...this.noteList, ...more] } else { promptAction.showToast({ message: 没有更多笔记 }) } } // 新增笔记弹窗简易逻辑 async addNewNote() { const id await RdbUtil.addNote(新建笔记, 这里是笔记内容文本) if (id 0) { promptAction.showToast({ message: 新增成功 }) await this.refreshList() } } // 删除单条笔记 async delItem(id: number) { const res await RdbUtil.deleteNote(id) if (res) { promptAction.showToast({ message: 删除完成 }) await this.refreshList() } } build() { Column({ space: 12 }) { Row({ space: 10 }) { TextInput({ text: this.searchKey, placeholder: 搜索笔记标题 }) .layoutWeight(1) .onChange(val this.searchKey val) Button(搜索).onClick(() this.refreshList()) } .width(95%) Row({ space: 10 }) { Button(新增笔记).backgroundColor(#007DFF).onClick(() this.addNewNote()) Button(加载更多).backgroundColor(#009966).onClick(() this.loadMore()) Button(清空全部).backgroundColor(#f56c6c).onClick(async () { await RdbUtil.clearAllNote() await this.refreshList() }) } List({ space: 10 }).layoutWeight(1).width(95%) { ForEach(this.noteList, (item: Note) { ListItem() { Row() { Column({ space: 4 }).layoutWeight(1) { Text(item.title).fontSize(17).fontWeight(500) Text(item.content).fontColor(#666) } Button(删除).backgroundColor(#f56c6c) .onClick(() this.delItem(item.id)) } .width(100%) .padding(16) .backgroundColor(Color.White) .borderRadius(10) } }) } } .width(100%) .height(100%) .padding(15) .backgroundColor(#F5F5F5) } async aboutToDisappear() { // 页面退出关闭数据库释放文件锁 await RdbUtil.closeDB() } }五、事务批量插入演示页面片段ets// 批量测试数据插入 async function batchInsertDemo() { const data [ { title: 批量笔记1, content: 批量事务测试内容1 }, { title: 批量笔记2, content: 批量事务测试内容2 }, { title: 批量笔记3, content: 批量事务测试内容3 } ] const ok await RdbUtil.batchAddNote(data) if (ok) { promptAction.showToast({ message: 批量插入完成 }) await refreshList() } else { promptAction.showToast({ message: 批量插入失败已回滚 }) } }六、API23 RDB 开发规范6.1 生命周期规范页面aboutToAppear传入上下文并执行 init 初始化页面aboutToDisappear必须调用 closeDB 关闭 RdbStore 释放文件锁ResultSet 查询结果集使用完立即 close避免内存泄漏。6.2 事务使用规范批量新增、批量删除、多表联动修改必须开启事务出现异常执行 rollback 回滚保证原子性不会出现半截数据单条增删改无需事务减少性能开销。6.3 查询性能规范列表统一分页 offsetlimit禁止一次性查询上千条模糊搜索使用 RdbPredicates.contains禁止拼接 SQL 字符串常用查询字段建立索引建表 SQL 增加 INDEX提升检索速度。6.4 安全规范所有条件过滤使用 RdbPredicates杜绝 SQL 注入风险用户输入内容不直接拼接进 executeSql 原始语句。七、高频报错与解决方案问题 1数据库文件被占用无法删除 / 初始化失败 解决页面退出调用 closeDB及时释放 RdbStore 实例。问题 2查询结果为空新增数据不显示 解决确认 init 建表语句执行成功主键自增配置正确。问题 3批量插入一半数据中断数据残留 解决批量操作包裹 beginTransaction commit异常执行 rollback。问题 4页面多次进入数据库卡顿 解决不要全局持有 store每次页面重新初始化退出关闭。问题 5升级 API23 旧版 sql 拼接查询报错 解决全部替换为 RdbPredicates 条件构造器废弃字符串拼接 SQL。问题 6ResultSet 内存持续上涨 解决循环读取完数据后调用 resultSet.close () 释放。八、总结RDB 是鸿蒙本地结构化海量数据存储标准方案API23 完善数据库实例生命周期、事务原子操作、条件安全查询、并发读写锁能力弥补 Preferences 无法复杂查询、仅支持简单键值对的短板。 封装通用 Rdb 工具类统一管理初始化、增删改查、分页搜索、批量事务、资源释放搭配完整笔记业务页面可直接集成进之前 HAR/HSP 分层架构项目适用于课程设计、毕设中笔记、通讯录、本地缓存等结构化数据持久化场景。

相关新闻

2026/9/5 18:25:37

为什么同一个问题,AI 每次给出的答案都不一样?

第一次问 AI,它说方案 A 最合适;刷新后再问,它又推荐方案 B。写文案时,这种变化带来灵感;查事实时,却让人心里没底。同一个问题为什么没有唯一答案?是 AI 随机乱说,还是我们其实没有…

2026/9/5 14:50:02

你以为 AI 在思考,其实它只是在猜下一个词?真相没那么简单

“大模型不就是自动补全吗?”这句话既抓住了核心,又很容易把人带偏。它确实通过预测下一个词元来训练和生成,但“训练目标简单”不等于“最终能力简单”。就像钢琴只有有限的琴键,却能演奏完全不同的作品;理解 AI&…

2026/9/6 1:28:43

2026年批发订货小程序哪个好?不同行业批发商该怎么挑

今天给大家带来2026年批发订货小程序哪个好?不同行业批发商该怎么挑。据艾瑞咨询2025年《中国小程序电商行业发展报告》,2025年中国小程序电商交易规模突破4.8万亿元,B2B批发订货类小程序占比约18%,年增速32%;国内SaaS…

2026/9/6 20:23:14

两级全差分高增益放大器设计:从指标分解到流片实战

简介:面向模拟集成电路学习者的设计参考文档,以两级全差分高增益放大器为设计对象,完整覆盖从性能指标设定、电路结构选型、参数计算到前仿真、版图绘制与物理验证的模拟IC全流程。文档基于华大九天Aether平台与0.18um PDK,针对DC…

2026/9/6 20:23:14

IOPaint完整指南:几步搞定图片去水印与路人消除

IOPaint完整指南:几步搞定图片去水印与路人消除 【免费下载链接】IOPaint Image inpainting tool powered by SOTA AI Model. Remove any unwanted object, defect, people from your pictures or erase and replace(powered by stable diffusion) any thing on you…

2026/9/6 20:23:14

开关电源并联供电系统实战:UC3843与LM358均流设计

简介:2011年全国大学生电子设计竞赛A题“开关电源并联供电系统”的完整设计报告,适合电子设计竞赛备赛学生、开关电源入门开发者参考。压缩包为1个doc文档,大小255KB,目前已获317人学习浏览。报告系统梳理了从方案论证到指标测试的…

2026/9/6 20:23:14

Fluent燃烧算例调参指南:从模型选型到收敛控制的完整路径

简介:《Fluent燃烧算例详细教程》是一份面向Fluent燃烧仿真场景的中文PDF技术资料,适合需要处理燃烧、湍流与反应流计算问题的工程师和科研人员。文档系统梳理了燃烧模型、湍流模型、燃烧反应模型以及计算流体力学仿真四个核心模块,重点介绍了…

2026/9/6 20:23:14

微信聊天记录导出成HTML、Word、CSV:WeChatMsg 三步完整指南

微信聊天记录导出成HTML、Word、CSV:WeChatMsg 三步完整指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/6 0:06:59

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/6 0:06:59

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/6 0:06:59

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/6 11:40:10

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…