尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

HarmonyOS 关系型数据库 RDB 数据持久化(ArkTS)实战:建库建表、CRUD、事务、FTS、性能优化,一篇搞懂

HarmonyOS 关系型数据库 RDB 数据持久化(ArkTS)实战:建库建表、CRUD、事务、FTS、性能优化,一篇搞懂
📅 发布时间:2026/6/20 18:14:14

HarmonyOS 关系型数据库 RDB 数据持久化(ArkTS)实战:建库建表、CRUD、事务、FTS、性能优化,一篇搞懂


1. 场景:为什么要用关系型数据库(RDB)?鸿蒙开发者第四期活动

如果你的数据像“表格”一样,字段之间有明确对应关系,比如:

  • 班级学生信息:姓名、学号、各科成绩
  • 公司员工信息:姓名、工号、职位、部门
  • App 里的“错题本/笔记/订单/清单”:都有筛选、排序、分页需求

这类数据的特点是:结构清晰、字段多、查询条件复杂(按学号查、按成绩排序、按日期筛选等)。
这时候 KV-Store(键值)就不舒服了,RDB 才是正解。

RDB 底层基于 SQLite,支持事务、索引、视图、触发器、外键、预编译 SQL、参数化查询等能力,适合做“正经业务数据”的持久化。


2. 必懂概念:谓词 & 结果集

2.1 谓词(RdbPredicates)

你可以理解成:“我想改/删/查哪些行”的条件表达器。
比如:NAME = 'Lisa'、score > 90、id in (...)。

2.2 结果集(ResultSet)

查询返回的数据不是一次性给你数组,而是一个“游标”:

  • 默认指向-1
  • goToNextRow()才会移动到下一行
  • 用完一定要close(),不然容易内存占用飙升

3. 约束和坑点

这些是官方重点强调的,实际开发也很常见:

  • 单次查询建议不超过 5000 条,否则可能卡死
  • 大查询建议放到TaskPool里做
  • SQL 拼接尽量简洁、合理分页/分批
  • 同一时间只能有一个写操作(写连接不会动态扩充)
  • 单条数据建议不要超过2MB:超过可能“插入成功但读取失败”
  • ArkTS 支持的基本类型:number / string / boolean / 二进制(Uint8Array) / BigInt(用 UNLIMITED INT)
  • 默认日志模式WAL,落盘方式FULL
  • 应用卸载后:数据库文件和临时文件(-wal/-shm)会被清理

4. 实战案例:做一个“学生表”并完成全套 CRUD(Stage 模型,ArkTS)

我用一个最经典的案例:学生信息管理。

4.1 表结构设计

表:STUDENT

字段建议:

  • ID:主键自增
  • NO:学号(唯一)
  • NAME:姓名
  • MATH:数学成绩
  • ENGLISH:英语成绩
  • UPDATED_AT:更新时间(时间戳,便于排序)

建表 SQL:

constSQL_CREATE_STUDENT_TABLE=`CREATE TABLE IF NOT EXISTS STUDENT ( ID INTEGER PRIMARY KEY AUTOINCREMENT, NO TEXT NOT NULL UNIQUE, NAME TEXT NOT NULL, MATH INTEGER, ENGLISH INTEGER, UPDATED_AT INTEGER )`;

5. 我推荐的项目结构(不把数据库写死在页面里)

很多人写 demo 喜欢把 getRdbStore 写在 UIAbility 里,能跑但不利于维护。
我更推荐:RdbManager(单例)+ DAO(数据访问层)。

5.1 RdbManager:负责“拿到 store + 建表 + 版本管理”

import{relationalStore}from'@kit.ArkData';import{BusinessError}from'@kit.BasicServicesKit';constDB_NAME='Student.db';constDB_VERSION=1;exportclassRdbManager{privatestaticinstance:RdbManager;privatestore?:relationalStore.RdbStore;staticgetInstance():RdbManager{if(!RdbManager.instance){RdbManager.instance=newRdbManager();}returnRdbManager.instance;}asyncinit(context:Context):Promise<relationalStore.RdbStore>{if(this.store)returnthis.store;constconfig:relationalStore.StoreConfig={name:DB_NAME,securityLevel:relationalStore.SecurityLevel.S3,encrypt:false,isReadOnly:false,};this.store=awaitnewPromise((resolve,reject)=>{relationalStore.getRdbStore(context,config,(err,store)=>{if(err)reject(err);elseresolve(store);});});// 首次创建数据库时 version = 0if(this.store.version===0){awaitthis.store.execute(SQL_CREATE_STUDENT_TABLE);this.store.version=DB_VERSION;}returnthis.store;}getStore():relationalStore.RdbStore{if(!this.store){thrownewError('RdbStore not initialized. Call init() first.');}returnthis.store;}}

:同一个数据库名,但不同 context 可能会产生多个数据库实例,所以建议统一在一个入口初始化(比如 EntryAbility / Application 启动时)。


6. StudentDao:增删改查(CRUD)

6.1 插入数据 insert()

import{relationalStore}from'@kit.ArkData';import{BusinessError}from'@kit.BasicServicesKit';exportinterfaceStudent{no:string;name:string;math?:number;english?:number;updatedAt:number;}exportclassStudentDao{constructor(privatestore:relationalStore.RdbStore){}asyncinsertStudent(s:Student):Promise<number>{constvalues:relationalStore.ValuesBucket={NO:s.no,NAME:s.name,MATH:s.math??null,ENGLISH:s.english??null,UPDATED_AT:s.updatedAt};try{// 冲突策略:学号唯一,重复就替换(你也可以改成 ON_CONFLICT_ABORT)returnawaitthis.store.insert('STUDENT',values,relationalStore.ConflictResolution.ON_CONFLICT_REPLACE);}catch(e){consterr=easBusinessError;console.error(`insertStudent failed, code=${err.code}, msg=${err.message}`);throwerr;}}}

官方有个点很好用:RDB 不需要 flush,insert 就直接落盘了。你博客可以强调一下。


6.2 修改 update()(用谓词锁定行)

asyncupdateScore(no:string,math:number,english:number):Promise<number>{constvalues:relationalStore.ValuesBucket={MATH:math,ENGLISH:english,UPDATED_AT:Date.now()};constpredicates=newrelationalStore.RdbPredicates('STUDENT');predicates.equalTo('NO',no);returnawaitnewPromise((resolve,reject)=>{this.store.update(values,predicates,(err,rows)=>{if(err)reject(err);elseresolve(rows);});});}

6.3 删除 delete()

async deleteByNo(no: string): Promise<number> { const predicates = new relationalStore.RdbPredicates('STUDENT'); predicates.equalTo('NO', no); return await new Promise((resolve, reject) => { this.store.delete(predicates, (err, rows) => { if (err) reject(err); else resolve(rows); }); }); }

6.4 查询 query() + ResultSet 遍历(重点:记得 close)

asyncqueryByNo(no:string):Promise<Student|null>{constpredicates=newrelationalStore.RdbPredicates('STUDENT');predicates.equalTo('NO',no);constrs=awaitnewPromise<relationalStore.ResultSet>((resolve,reject)=>{this.store.query(predicates,['NO','NAME','MATH','ENGLISH','UPDATED_AT'],(err,resultSet)=>{if(err)reject(err);elseresolve(resultSet);});});try{if(rs.goToNextRow()){return{no:rs.getString(rs.getColumnIndex('NO')),name:rs.getString(rs.getColumnIndex('NAME')),math:rs.getLong(rs.getColumnIndex('MATH')),english:rs.getLong(rs.getColumnIndex('ENGLISH')),updatedAt:rs.getLong(rs.getColumnIndex('UPDATED_AT')),};}returnnull;}finally{rs.close();// ✅ 非常重要}}

7. 事务:批量写入/更新一定要用(性能 + 原子性)

比如:一次导入 1000 个学生成绩,推荐写法:

asyncimportStudents(list:Student[]):Promise<void>{consttx=awaitthis.store.createTransaction();try{for(constsoflist){awaittx.insert('STUDENT',{NO:s.no,NAME:s.name,MATH:s.math??null,ENGLISH:s.english??null,UPDATED_AT:s.updatedAt},relationalStore.ConflictResolution.ON_CONFLICT_REPLACE);}awaittx.commit();}catch(e){awaittx.rollback();throwe;}}

我一般会在博客里写一句“人话”:

事务就像“打包提交”,要么全成功,要么全失败,同时还能减少频繁落盘带来的耗时。


8. 大数据量查询:放进 TaskPool(避免 UI 卡死)

官方建议:大数据查询放 TaskPool。思路就是:耗时操作不要堵主线程。

你可以在博客里写成“经验结论”:

  • 单次不要超过 5000 条
  • 分页/分批
  • TaskPool 异步查,查完再回到 UI 更新

(如果你要我补一段 TaskPool + 分页查询的完整示例,我也能继续给你配好。)


9. FTS 全文检索(中文支持 ICU 分词器)

如果你做“笔记/文章/错题解析”这种场景,全局搜索非常常用。
RDB 支持 FTS,中文分词建议icu zh_CN。

建 FTS 表:

awaitthis.store.execute('CREATE VIRTUAL TABLE IF NOT EXISTS note_fts USING fts4(title, content, tokenize=icu zh_CN)');

查询:

constrs=awaitthis.store.querySql('SELECT title FROM note_fts WHERE note_fts MATCH ?',['测试']);try{while(rs.goToNextRow()){consttitle=rs.getValue(rs.getColumnIndex('title'));console.info(`hit:${title}`);}}finally{rs.close();}

10. 数据库异常(14800011)怎么办?

官方提到:数据库在操作/存储中可能出现非预期异常(例如 14800011),需要重建并恢复数据。
我一般博客里会提醒两句:

  • 平时要做备份机制(手动备份也行)
  • 真遇到异常,走“重建 + restore”流程,保证业务能恢复

11. 备份 & 恢复(同路径)

备份:

this.store.backup('Backup.db',(err)=>{if(err)console.error(`backup failed:${err.code}`);elseconsole.info('backup success');});

恢复:

this.store.restore('Backup.db',(err)=>{if(err)console.error(`restore failed:${err.code}`);elseconsole.info('restore success');});

12. 删除数据库(慎用)

import{relationalStore}from'@kit.ArkData';relationalStore.deleteRdbStore(this.context,'Student.db',(err)=>{if(err)console.error(`delete failed:${err.code}`);elseconsole.info('delete success');});

13. 最后我给一个“选择建议总结”

  • 数据结构复杂、要筛选排序分页 →RelationalStore(RDB)
  • 配置开关类 → Preferences
  • 业务关系弱、想跨设备更友好 → KV-Store

相关新闻

  • Cesium快速入门20:Primitive的外观设置Appearance
  • 应用层复习总结
  • AFE为何物

最新新闻

  • 小程序安全通信机制深度解析:从签名算法到逆向分析实践
  • vLLM+llama-factory本地部署实战:生产级LLM落地操作手册
  • GLM-5.1开源实战:本地部署、量化推理与VS Code集成指南
  • 嵌入式开发板电压与时钟配置:从原理到实战排查指南
  • 终极FGO自动化战斗解决方案:Fate/Grand Automata深度使用指南
  • Hermes本地AI Agent架构升级实战:模块化、持久化与沙箱化

日新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号