news 2026/4/14 20:49:49

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

作者头像

张小明

前端开发工程师

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

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


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

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

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

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


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

2.1 谓词(RdbPredicates)

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

2.2 结果集(ResultSet)

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


3. 约束和坑点

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


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

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

4.1 表结构设计

表:STUDENT

字段建议:

建表 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。思路就是:耗时操作不要堵主线程

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

(如果你要我补一段 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),需要重建并恢复数据
我一般博客里会提醒两句:


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. 最后我给一个“选择建议总结”

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 14:23:02

Cesium快速入门19:Entity折线材质

上一节课我们把“面”的材质讲完了&#xff0c;今天换“线”——折线&#xff08;Polyline&#xff09;。 不管是道路、航线还是飞线特效&#xff0c;全靠下面几种现成材质&#xff0c;一句代码就能换皮肤。一、最朴素的红线先画两个点&#xff0c;宽度 5 像素&#xff0c;纯红…

作者头像 李华
网站建设 2026/4/12 0:13:09

C51_HC-05蓝牙通信

文章目录一、蓝牙   1、蓝牙的特点   2、蓝牙特性二、HC-05   1、简介   2、主要参数   3、引脚   4、模块原理图   5、工作模式     1&#xff09;、命令响应工作模式     2&#xff09;、自动连接工作模式     3&#xff09;、进入命令响应工作模式…

作者头像 李华
网站建设 2026/4/13 5:02:31

前端工程师必看:AI+前端+A/B测试 实战指南(小白友好版)

前端工程师必看&#xff1a;AI前端A/B测试 实战指南&#xff08;小白友好版&#xff09; 是不是总觉得“AI”“A/B测试”这些技术离自己很远&#xff1f;其实现在前端已经能直接对接AI能力、落地A/B测试了&#xff01;这篇文章会从**“是什么→前端要做什么→怎么实现”** 一步…

作者头像 李华
网站建设 2026/4/13 16:34:42

2、初探Puppet清单编写

初探Puppet清单编写 1. Puppet简介与重要性 在过去几年里,配置管理在IT领域变得越来越重要。特别是对于服务器操作而言,如果没有强大的管理基础设施,几乎无法进行。在众多可用的配置管理工具中,Puppet已成为最受欢迎和广泛使用的解决方案之一。它最初由Luke Kanies编写,…

作者头像 李华
网站建设 2026/4/13 5:48:34

7、Puppet资源类型与模块:深入剖析与实践应用

Puppet资源类型与模块:深入剖析与实践应用 1. Puppet资源管理基础 在Puppet的资源管理体系中,资源类型和提供者是核心概念。资源类型定义了Puppet在DSL(领域特定语言)中暴露的接口,同时负责输入值的验证、转换等工作。而提供者则封装了实际操作系统及其工具链的知识,实…

作者头像 李华
网站建设 2026/4/6 18:40:13

8、利用类和自定义类型模块化清单

利用类和自定义类型模块化清单 编写综合类 许多类的编写目的是让 Puppet 在代理平台上执行重大任务。以 Apache 类为例,我们可以构思一个能被任何机器的清单包含的类,并确保满足以下条件: - 安装防火墙软件并配置默认规则集。 - 安装恶意软件检测软件。 - 定时任务按设定…

作者头像 李华