news 2026/6/15 21:07:43

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/6/15 9:32:32

AFE为何物

AFE 的全称是Analog Front-End&#xff0c;中文为模拟前端芯片&#xff0c;它是 BMS&#xff08;电池管理系统&#xff09;中负责电池核心参数采集与初步处理的关键专用芯片&#xff0c;也是高串数电池包&#xff08;如新能源车、储能电站电池&#xff09;的核心组件。 核心功…

作者头像 李华
网站建设 2026/6/15 10:25:43

Cesium快速入门20:Primitive的外观设置Appearance

前面我们学会了用 Entity 换皮肤&#xff1b;今天轮到 Primitive——自己捏几何体后&#xff0c;再给它穿什么样的“外衣”。 在 Cesium 里&#xff0c;这件外衣叫 Appearance&#xff08;外观&#xff09;。 它决定“像素怎么画、用什么材质、要不要光照”等所有渲染细节。 上…

作者头像 李华
网站建设 2026/6/15 10:28:18

单车慢跑中的节奏建议

单车定向的几个问题第21届智能车竞赛单车定向组比赛科目细则 01 单车定向中的慢跑 一、漫无目的 卓大单车还有如果这样跑可以吗&#xff1f; 我感觉这么跑的话就无线循环了&#xff0c;应该也在规则里ban了吧。 单车的两个公司的舵机可以混着用吗&#xff1f;   比如在k1车模…

作者头像 李华
网站建设 2026/6/15 10:27:40

昇腾Ascend C Add算子开发实战-从理论到代码的完整构建

目录 1 摘要 2 技术原理 2.1 架构设计理念解析 2.2 核心算法实现 2.2.1 核函数基础架构 2.2.2 三级流水线实现 2.3 性能特性分析 2.3.1 理论性能模型 2.3.2 实测性能数据 3 实战部分 3.1 完整可运行代码示例 3.2 分步骤实现指南 步骤1&#xff1a;环境配置与工程创…

作者头像 李华
网站建设 2026/6/15 10:25:39

5、Puppet 主节点与代理节点:全流程解析与性能优化

Puppet 主节点与代理节点:全流程解析与性能优化 在 Puppet 为中心的工作流里,服务器(甚至可能是工作站)配置的所有更改通常都源于 Puppet 主节点,并自动传播到代理节点。下面将详细介绍 Puppet 代理节点的生命周期、证书管理、运行方式、性能考量、不同运行方案对比、数据…

作者头像 李华