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 分层架构项目适用于课程设计、毕设中笔记、通讯录、本地缓存等结构化数据持久化场景。

相关新闻

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

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

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

2026/7/15 0:21:08 阅读更多 →
收藏了!1个让零基础小白也能看懂AI的GitHub开源课程,太全了

收藏了!1个让零基础小白也能看懂AI的GitHub开源课程,太全了

前几天有个做运营的朋友跟我说,她想搞明白大模型到底是怎么工作的,结果翻了一堆教程,全是公式和代码,看了三天就放弃了。我跟她说,其实不用那么硬核,有个课程专门给啥都不懂的人设计的,她花了两…

2026/7/15 0:17:07 阅读更多 →
基于STM32的智能家居系统-基于STM32单片机物联网设计【硬件+APP+云平台】

基于STM32的智能家居系统-基于STM32单片机物联网设计【硬件+APP+云平台】

(1)硬件端 1. 环境监测:温度、湿度、空气质量、光照强度等。 2. 家具模拟:门(舵机模拟)、窗(舵机模拟)、灯、风扇,有无线控制和手动控制两种方式; 3. …

2026/7/15 0:15:07 阅读更多 →

最新新闻

Unity网络通信基础:手把手实现稳定可靠的Socket TCP客户端

Unity网络通信基础:手把手实现稳定可靠的Socket TCP客户端

1. 项目概述:为什么Unity开发者绕不开Socket通信?如果你刚开始接触Unity网络功能,可能会被Unity自带的UNet(已弃用)、Mirror、Netcode for GameObjects这些框架搞得眼花缭乱。但无论上层框架如何封装,底层通…

2026/7/15 1:19:24 阅读更多 →
长三角非标产线自动化升级选型对比:机器人原厂、代理商、系统集成商深度技术分析

长三角非标产线自动化升级选型对比:机器人原厂、代理商、系统集成商深度技术分析

一、前言苏州作为长三角智能制造核心城市,全市在册非标自动化、机器人系统集成企业超过 300 家。 当前工厂自动化改造市场参与者分为机器人品牌原厂、区域贸易代理商、小型工程施工团队、本土专业系统集成商四大类。 四类厂商技术能力、方案架构、售后体系、项目风险…

2026/7/15 1:15:23 阅读更多 →
MSP430FR2633寄存器实战:从电源管理到CapTIvate的嵌入式开发指南

MSP430FR2633寄存器实战:从电源管理到CapTIvate的嵌入式开发指南

1. 项目概述与核心价值如果你正在使用德州仪器(TI)的MSP430FR2633、FR2632、FR2533或FR2532这几款微控制器(MCU)进行开发,那么你很可能已经翻遍了数据手册,面对那一大堆寄存器地址、缩写和功能描述感到头疼…

2026/7/15 1:15:23 阅读更多 →
ESP32+LVGL实战:从零构建精简中文字库与分区烧录指南

ESP32+LVGL实战:从零构建精简中文字库与分区烧录指南

1. 为什么需要自定义中文字库?在嵌入式设备上显示中文,最直接的方法是使用LVGL官方提供的lv_font_simsun_16_cjk.c字库文件。但这个字库存在两个明显问题:一是仅包含1000个常用汉字和部首,实际使用时容易出现缺字;二是…

2026/7/15 1:13:22 阅读更多 →
革命性macOS窗口预览神器:DockDoor重新定义你的多任务工作流

革命性macOS窗口预览神器:DockDoor重新定义你的多任务工作流

革命性macOS窗口预览神器:DockDoor重新定义你的多任务工作流 【免费下载链接】DockDoor Window peeking, alt-tab and other enhancements for macOS 项目地址: https://gitcode.com/gh_mirrors/do/DockDoor 还在为macOS上混乱的窗口管理而烦恼吗&#xff1f…

2026/7/15 1:09:21 阅读更多 →
从零构建J-Link OB调试器:基于STM32F103C8T6的固件烧录与实战

从零构建J-Link OB调试器:基于STM32F103C8T6的固件烧录与实战

1. 为什么选择STM32F103C8T6制作J-Link OB 说到嵌入式开发调试工具,J-Link绝对是工程师们的老朋友了。但正版J-Link动辄上千元的价格,让很多个人开发者望而却步。其实用一块不到20元的STM32F103C8T6核心板(俗称Blue Pill)&#x…

2026/7/15 1:07:20 阅读更多 →

日新闻

YOLO11 改进 - 特征融合 | STFFM空间时间特征融合模块,强化时空互补、抑制噪声,助力小目标检测高效涨点

YOLO11 改进 - 特征融合 | STFFM空间时间特征融合模块,强化时空互补、抑制噪声,助力小目标检测高效涨点

前言 本文介绍了面向红外小目标检测的时空特征融合模块——STFFM,用于增强复杂背景下目标与噪声、杂波的区分能力。该方法通过拼接空间特征与时间/运动特征,并结合通道注意力、空间注意力和残差增强机制,实现对关键语义通道与疑似目标区域的…

2026/7/15 0:01:00 阅读更多 →
YOLO26 改进 - 特征融合 | STFFM空间时间特征融合模块,强化时空互补、抑制噪声,助力小目标检测高效涨点

YOLO26 改进 - 特征融合 | STFFM空间时间特征融合模块,强化时空互补、抑制噪声,助力小目标检测高效涨点

前言 本文介绍了面向复杂背景小目标检测的时空特征融合模块——STFFM。该模块通过空间分支与时间/运动分支的特征拼接,引入通道注意力和空间注意力对融合特征进行自适应筛选,并结合残差增强与通道压缩,突出目标区域、抑制背景噪声。我们将 S…

2026/7/15 0:01:00 阅读更多 →
行星减速机为什么能提高扭矩?从功率守恒到输出扭矩校核

行星减速机为什么能提高扭矩?从功率守恒到输出扭矩校核

一、为什么减速以后扭矩会增大 旋转机械的功率、转速和扭矩之间存在以下关系: T 9550 P n 其中: T为扭矩,单位Nm; P为功率,单位kW; n为转速,单位r/min。 在功率基本不变的情况下:…

2026/7/15 0:03:00 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/14 16:53:23 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/14 14:00:13 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/14 7:15:24 阅读更多 →

月新闻