2026/4/18 11:49:04
网站建设
项目流程
驻马店住房和城乡建设部网站,学校网站怎么下载不了,龙山县建设局网站,邢台企业做网站哪儿好文章目录前言一、 数据库的创建与升降级机制二、 优雅的 CRUD 谓词 (Predicates) 与值桶 (ValuesBucket)三、 事务处理#xff1a;批量操作的性能救星四、 总结与最佳实践前言
在上一篇文章中#xff0c;我们介绍了用户首选项#xff08;Preferences#xff09;#xff0…文章目录前言一、 数据库的创建与升降级机制二、 优雅的 CRUD 谓词 (Predicates) 与值桶 (ValuesBucket)三、 事务处理批量操作的性能救星四、 总结与最佳实践前言在上一篇文章中我们介绍了用户首选项Preferences它非常适合用来存储“夜间模式开关”或“字体大小”这类简单的配置项。但是当我们的应用需求升级比如要开发一个“备忘录”应用里面有成百上千条笔记每条笔记都有标题、内容、创建时间和是否置顶的状态甚至用户还希望能够通过关键字搜索笔记或者按时间排序。这时候Preferences 就显得力不从心了。如果我们把几千条数据全部读到内存里再进行过滤性能会瞬间崩塌。为了解决结构化大数据的存储与查询问题鸿蒙 HarmonyOS 6 (API 20) 提供了强大的关系型数据库 (RDB)。它的底层是大家非常熟悉的SQLite但在 API 层面ArkUI 封装了一套面向对象的 TS 接口让我们无需编写繁琐的 SQL 语句就能轻松操作数据。同时它对事务Transaction的原生支持也为批量数据操作的性能和一致性提供了坚实保障。今天我们就来深入这一层看看如何像操作内存数组一样优雅地操作数据库。一、 数据库的创建与升降级机制在使用 RDB 之前我们首先要建立与磁盘数据库文件的连接。这不仅仅是打开一个文件那么简单它涉及到一个关键的生命周期管理创建onCreate与升级onUpgrade。在鸿蒙的relationalStore模块中我们需要配置一个StoreConfig对象指明数据库的文件名和安全等级。当应用第一次安装并启动时系统发现本地没有数据库文件就会触发onCreate回调。在这里是我们执行建表语句CREATE TABLE的最佳时机。我们通常会定义好标准的 SQL 字符串比如创建一个notes表包含 id主键、title标题、content内容等字段。随着应用版本的迭代数据库结构往往也会发生变化。比如 v2.0 版本我们需要给笔记增加一个置顶功能这就需要在表中新增一个is_pinned字段。这时候我们只需要将StoreConfig中的版本号从 1 改为 2。当用户更新应用后首次启动RDB 会检测到版本号不一致从而触发onUpgrade回调。我们需要在这个回调里执行ALTER TABLE语句来修改表结构。这种版本控制机制保证了我们的应用在不断迭代中用户的老数据依然能平滑迁移不会丢失。二、 优雅的 CRUD 谓词 (Predicates) 与值桶 (ValuesBucket)在传统的后端开发中我们习惯了手写INSERT INTO table ...或者SELECT * FROM table WHERE ...。但在鸿蒙开发中为了避免 SQL 注入风险并利用 TypeScript 的类型检查我们使用ValuesBucket和RDBPredicates来代替裸写 SQL。ValuesBucket值桶主要用于插入和更新操作。它本质上是一个键值对对象Key 是数据库的列名Value 是我们要存的数据。例如const note { title: 会议纪要, content: ... }我们直接把这个对象传给insert方法即可。而RDBPredicates谓词则是查询和删除操作的灵魂。它就像是一个构建查询条件的工厂。假设我们要查询“所有标题包含‘会议’且按时间倒序排列”的笔记我们不需要拼凑 SQL 字符串而是链式调用new RdbPredicates(notes).like(title, %会议%).orderByDesc(created_time)。这种面向对象的查询方式不仅代码可读性极高而且在编译阶段就能帮我们规避很多低级的语法错误。三、 事务处理批量操作的性能救星试想这样一个场景你需要从服务器同步 1000 条历史笔记到本地。如果你在一个ForEach循环里调用 1000 次insert方法你会发现应用界面卡顿严重写入速度慢得惊人。这是因为每一次insert操作底层 SQLite 都会默认开启并提交一个事务伴随着一次完整的磁盘 I/O。1000 次操作就是 1000 次磁盘读写这在移动设备上是极大的开销。为了解决这个问题我们需要手动管理事务 (Transaction)。我们可以调用beginTransaction()开启事务然后执行这 1000 次插入操作。此时所有的修改都暂时保存在内存缓冲区中。当循环结束后我们调用commit()系统才会一次性将所有数据写入磁盘。如果中间发生了错误我们可以调用rollBack()数据会瞬间回滚到操作前的状态保证数据的原子性。在实测中使用事务进行批量插入性能通常能提升一个数量级以上。import { relationalStore, ValuesBucket } from kit.ArkData; import { common } from kit.AbilityKit; import { promptAction } from kit.ArkUI; import { BusinessError } from kit.BasicServicesKit; // ------------------------------------------------------------- // 1. 定义数据模型 // ------------------------------------------------------------- interface Note { id: number; title: string; content: string; createTime: number; } // 数据库配置 const STORE_CONFIG: relationalStore.StoreConfig { name: Notes.db, securityLevel: relationalStore.SecurityLevel.S1, }; const TABLE_NAME notes; // 建表 SQL const SQL_CREATE_TABLE CREATE TABLE IF NOT EXISTS ${TABLE_NAME} ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT, createTime INTEGER ) ; // ------------------------------------------------------------- // 2. RDB 管理类 (单例) // ------------------------------------------------------------- class RdbManager { private static instance: RdbManager; private rdbStore: relationalStore.RdbStore | null null; private constructor() {} public static getInstance(): RdbManager { if (!RdbManager.instance) { RdbManager.instance new RdbManager(); } return RdbManager.instance; } /** * 初始化数据库 */ public async init(context: common.UIAbilityContext): Promisevoid { if (this.rdbStore) return; try { this.rdbStore await relationalStore.getRdbStore(context, STORE_CONFIG); // 初始化建表 // 实际项目中建议使用 version 版本号管理数据库升级 (onUpgrade) await this.rdbStore.executeSql(SQL_CREATE_TABLE); console.info([RdbManager] Initialized success); } catch (err) { console.error([RdbManager] Init failed: ${JSON.stringify(err)}); } } /** * 插入数据 */ public async insertNote(title: string, content: string): Promisenumber { if (!this.rdbStore) { console.error([RdbManager] Store not initialized); return -1; } // 【严格模式适配】 // ValuesBucket 本质是 Recordstring, ValueType // 必须确保 value 的类型符合 ArkTS 规范 const valueBucket: ValuesBucket { title: title, content: content, createTime: Date.now() }; try { const rowId await this.rdbStore.insert(TABLE_NAME, valueBucket); return rowId; } catch (err) { console.error([RdbManager] Insert failed: ${JSON.stringify(err)}); return -1; } } /** * 查询所有数据 */ public async queryAllNotes(): PromiseNote[] { if (!this.rdbStore) return []; const predicates new relationalStore.RdbPredicates(TABLE_NAME); predicates.orderByDesc(createTime); // 定义在 try 外面以便 finally 中关闭 let resultSet: relationalStore.ResultSet | null null; const notes: Note[] []; try { resultSet await this.rdbStore.query(predicates); // 遍历结果集 // resultSet.goToNextRow() 返回 boolean while (resultSet.goToNextRow()) { const idIndex resultSet.getColumnIndex(id); const titleIndex resultSet.getColumnIndex(title); const contentIndex resultSet.getColumnIndex(content); const timeIndex resultSet.getColumnIndex(createTime); notes.push({ // 这里的 getLong/getString 可能会在严格模式下有类型警告 // 但在当前 API 版本中是标准写法 id: resultSet.getLong(idIndex), title: resultSet.getString(titleIndex), content: resultSet.getString(contentIndex), createTime: resultSet.getLong(timeIndex) }); } } catch (err) { console.error([RdbManager] Query failed: ${JSON.stringify(err)}); } finally { // 【关键修复】确保资源释放防止内存泄漏 if (resultSet) { resultSet.close(); } } return notes; } /** * 删除数据 */ public async deleteNote(id: number): Promisenumber { if (!this.rdbStore) return -1; const predicates new relationalStore.RdbPredicates(TABLE_NAME); predicates.equalTo(id, id); try { return await this.rdbStore.delete(predicates); } catch (err) { console.error([RdbManager] Delete failed: ${JSON.stringify(err)}); return -1; } } } export const rdbManager RdbManager.getInstance(); // ------------------------------------------------------------- // 3. 页面 UI 实现 // ------------------------------------------------------------- Entry Component struct RdbDemoPage { State noteList: Note[] []; State newNoteTitle: string ; async aboutToAppear() { try { const context getContext(this) as common.UIAbilityContext; await rdbManager.init(context); await this.refreshList(); } catch (error) { console.error([Page] Init failed: ${JSON.stringify(error)}); } } async refreshList() { this.noteList await rdbManager.queryAllNotes(); } build() { Column() { // 标题 Text(本地数据库 RDB) .fontSize(24) .fontWeight(FontWeight.Bold) .margin({ top: 40, bottom: 20 }) // 输入区 Row({ space: 10 }) { TextInput({ text: this.newNoteTitle, placeholder: 输入笔记标题... }) .layoutWeight(1) .height(40) .backgroundColor(Color.White) .borderRadius(8) .onChange((value) { this.newNoteTitle value; }) Button(添加) .height(40) .backgroundColor(#0A59F7) .onClick(async () { if (!this.newNoteTitle.trim()) { promptAction.showToast({ message: 标题不能为空 }); return; } // 插入并刷新 await rdbManager.insertNote(this.newNoteTitle, 暂无详细内容); this.newNoteTitle ; // 清空输入框 await this.refreshList(); promptAction.showToast({ message: 保存成功 }); }) } .width(90%) .margin({ bottom: 20 }) // 列表区 List({ space: 12 }) { ForEach(this.noteList, (item: Note) { ListItem() { Row() { Column() { Text(item.title) .fontSize(16) .fontWeight(FontWeight.Bold) .fontColor(#333) Text(new Date(item.createTime).toLocaleString()) .fontSize(12) .fontColor(#999) .margin({ top: 4 }) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) // 删除按钮 Button(删除) .type(ButtonType.Normal) .backgroundColor(#FF4040) .fontColor(Color.White) .fontSize(12) .borderRadius(6) .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .onClick(async () { await rdbManager.deleteNote(item.id); await this.refreshList(); promptAction.showToast({ message: 已删除 }); }) } .width(100%) .padding(16) .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 4, color: #1A000000, offsetY: 2 }) } }, (item: Note) JSON.stringify(item)) // 使用稳健的键生成策略 } .layoutWeight(1) .width(95%) .scrollBar(BarState.Auto) // 空状态提示 if (this.noteList.length 0) { Column() { Text(暂无笔记) .fontSize(16) .fontColor(#999) .margin({ top: 60 }) } } } .width(100%) .height(100%) .backgroundColor(#F1F3F5) } }四、 总结与最佳实践关系型数据库 RDB 是鸿蒙应用处理复杂数据的基石。它填补了 Preferences 和文件存储之间的空白为我们提供了结构化查询的能力。在实际开发中建议将 RDB 的操作封装为一个单例的DatabaseManager对外暴露明确的业务方法如addNote,getNoteList而将底层的Predicates构建和 SQL 执行细节隐藏起来。同时务必注意所有数据库操作都是异步的Promise请确保在 UI 层面做好 Loading 状态的管理避免阻塞主线程。