校园网站管理系统,域名的价格,深圳防疫措施优化调整,北京市建设部网站HarmonyOS 关系型数据库 RDB 数据持久化#xff08;ArkTS#xff09;实战#xff1a;建库建表、CRUD、事务、FTS、性能优化#xff0c;一篇搞懂 1. 场景#xff1a;为什么要用关系型数据库#xff08;RDB#xff09;#xff1f;鸿蒙开发者第四期活动
如果你的数据像“…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查询返回的数据不是一次性给你数组而是一个“游标”默认指向-1goToNextRow()才会移动到下一行用完一定要close()不然容易内存占用飙升3. 约束和坑点这些是官方重点强调的实际开发也很常见单次查询建议不超过 5000 条否则可能卡死大查询建议放到TaskPool里做SQL 拼接尽量简洁、合理分页/分批同一时间只能有一个写操作写连接不会动态扩充单条数据建议不要超过2MB超过可能“插入成功但读取失败”ArkTS 支持的基本类型number / string / boolean / 二进制(Uint8Array) / BigInt(用 UNLIMITED INT)默认日志模式WAL落盘方式FULL应用卸载后数据库文件和临时文件-wal/-shm会被清理4. 实战案例做一个“学生表”并完成全套 CRUDStage 模型ArkTS我用一个最经典的案例学生信息管理。4.1 表结构设计表STUDENT字段建议ID主键自增NO学号唯一NAME姓名MATH数学成绩ENGLISH英语成绩UPDATED_AT更新时间时间戳便于排序建表 SQLconstSQL_CREATE_STUDENT_TABLECREATE 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}fromkit.ArkData;import{BusinessError}fromkit.BasicServicesKit;constDB_NAMEStudent.db;constDB_VERSION1;exportclassRdbManager{privatestaticinstance:RdbManager;privatestore?:relationalStore.RdbStore;staticgetInstance():RdbManager{if(!RdbManager.instance){RdbManager.instancenewRdbManager();}returnRdbManager.instance;}asyncinit(context:Context):PromiserelationalStore.RdbStore{if(this.store)returnthis.store;constconfig:relationalStore.StoreConfig{name:DB_NAME,securityLevel:relationalStore.SecurityLevel.S3,encrypt:false,isReadOnly:false,};this.storeawaitnewPromise((resolve,reject){relationalStore.getRdbStore(context,config,(err,store){if(err)reject(err);elseresolve(store);});});// 首次创建数据库时 version 0if(this.store.version0){awaitthis.store.execute(SQL_CREATE_STUDENT_TABLE);this.store.versionDB_VERSION;}returnthis.store;}getStore():relationalStore.RdbStore{if(!this.store){thrownewError(RdbStore not initialized. Call init() first.);}returnthis.store;}}同一个数据库名但不同 context 可能会产生多个数据库实例所以建议统一在一个入口初始化比如 EntryAbility / Application 启动时。6. StudentDao增删改查CRUD6.1 插入数据 insert()import{relationalStore}fromkit.ArkData;import{BusinessError}fromkit.BasicServicesKit;exportinterfaceStudent{no:string;name:string;math?:number;english?:number;updatedAt:number;}exportclassStudentDao{constructor(privatestore:relationalStore.RdbStore){}asyncinsertStudent(s:Student):Promisenumber{constvalues:relationalStore.ValuesBucket{NO:s.no,NAME:s.name,MATH:s.math??null,ENGLISH:s.english??null,UPDATED_AT:s.updatedAt};try{// 冲突策略学号唯一重复就替换你也可以改成 ON_CONFLICT_ABORTreturnawaitthis.store.insert(STUDENT,values,relationalStore.ConflictResolution.ON_CONFLICT_REPLACE);}catch(e){consterreasBusinessError;console.error(insertStudent failed, code${err.code}, msg${err.message});throwerr;}}}官方有个点很好用RDB 不需要 flushinsert 就直接落盘了。你博客可以强调一下。6.2 修改 update()用谓词锁定行asyncupdateScore(no:string,math:number,english:number):Promisenumber{constvalues:relationalStore.ValuesBucket{MATH:math,ENGLISH:english,UPDATED_AT:Date.now()};constpredicatesnewrelationalStore.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): Promisenumber { 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 遍历重点记得 closeasyncqueryByNo(no:string):PromiseStudent|null{constpredicatesnewrelationalStore.RdbPredicates(STUDENT);predicates.equalTo(NO,no);constrsawaitnewPromiserelationalStore.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[]):Promisevoid{consttxawaitthis.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, tokenizeicu zh_CN));查询constrsawaitthis.store.querySql(SELECT title FROM note_fts WHERE note_fts MATCH ?,[测试]);try{while(rs.goToNextRow()){consttitlers.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}fromkit.ArkData;relationalStore.deleteRdbStore(this.context,Student.db,(err){if(err)console.error(delete failed:${err.code});elseconsole.info(delete success);});13. 最后我给一个“选择建议总结”数据结构复杂、要筛选排序分页 →RelationalStoreRDB配置开关类 → Preferences业务关系弱、想跨设备更友好 → KV-Store