news 2026/4/16 19:45:32

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/16 17:50:32

14、充分利用语言的完整工具集

充分利用语言的完整工具集 在配置管理中,我们常常需要高效地管理和分配资源。Puppet 提供了一系列强大的功能,让我们可以更灵活地处理各种资源。下面将详细介绍 Puppet 中的资源标签、资源导出与导入、资源参数覆盖以及资源默认值等重要特性。 资源标签的使用 Puppet 会隐…

作者头像 李华
网站建设 2026/4/16 17:53:06

AI算力:驱动智能时代多元计算与高效调度的基础

各行各业正被人工智能发展浪潮以前所未有的力道进行重塑&#xff0c;而且支撑其运作的底层基础是算力。换种通俗易懂的说法&#xff0c;AI算力是指用来执行人工智能算法&#xff0c;对海量数据予以处理以及解析所需要的计算能力。它不是一项简单的硬件指标&#xff0c;而是包含…

作者头像 李华
网站建设 2026/4/15 15:26:30

AI知识库的构建:从数据采集、处理到高效检索的全流程解析

AI知识库的搭建以及运用属于一项具备系统性的工程项目&#xff0c;它的关键之处在于怎样以高效且精准地把领域方面的知识给予人工智能系统&#xff0c;从而让其拥有专业领域范畴内的认知以及推理能力。这个进程一般被称作“知识库投喂”或者“知识灌注”&#xff0c;它并非仅仅…

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

Git工作流程指南: 基础 Git 操作

基础 Git 操作 仓库初始化 # 创建新的仓库 git init# 克隆已经存在的仓库 git clone <repository_URL>文件操作 # 将文件添加到暂存区&#xff08;Staging Area&#xff09; git add <file_name> # 添加单个文件 git add . …

作者头像 李华
网站建设 2026/4/8 20:09:34

Modelsim联合高云ip仿真流程

一、改变工作器件库打开modelsim&#xff0c;调整工作器件库&#xff1a;modelsim --> File --> Change Directory --> 选择modelsim的安装目录C:\modeltech64_10.7&#xff08;我装在C盘&#xff09;&#xff0c;点击选择文件夹&#xff0c;如下图。二、创建libirary…

作者头像 李华
网站建设 2026/4/12 15:40:57

69.学习复盘-21天核心知识点梳理与记忆技巧-附思维导图

69 学习复盘:21 天核心知识点梳理与记忆技巧 你好,欢迎来到第 69 讲,也是我们整个课程的倒数第三讲。 在过去的 20 多天里,我们一起经历了一段信息量巨大、充满挑战但也收获满满的 DDD 学习之旅。我们从最基础的概念,一路走到了复杂的企业级案例和面试技巧。 在即将结束…

作者头像 李华