ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

华为鸿蒙开发高级篇06-数据持久化进阶:RDB 实战(

华为鸿蒙开发高级篇06-数据持久化进阶:RDB 实战( 华为鸿蒙开发高级篇06-数据持久化进阶RDB 实战笔记 App 数据层案例高级系列第 6 篇 · 案例笔记 App 数据层——增删改查、全文检索、分页查询、数据库升级迁移与加密。 目标掌握鸿蒙关系型数据库relationalStore / RDB的生产级用法表设计、事务、迁移、加密、查询优化并沉淀一份数据层仓库模板。一、案例背景Preferences/KVStore 适合轻量数据一旦需要结构化 条件查询 分页 全文检索笔记、订单、日志就要上关系型数据库RDB。笔记 App 是典型场景笔记列表按时间倒序分页、按标题/正文检索、收藏置顶、离线编辑后同步。本文用笔记 App 数据层做案例覆盖 RDB 的核心生产问题。二、核心原理relationalStore 是什么鸿蒙 RDB 基于 SQLite提供 SQL 能力应用 ── RdbStore连接── SQL 执行 ── ResultSet结果集 │ ├── 事务beginTransaction/commit/rollBack ├── 升级迁移onUpgrade 按版本迭代 └── 加密securityLevel/encrypt 配置关键认知RdbStore 是单例的一个数据库一个 store不要每处新建。增删改走 SQL查询走 ResultSetArkTS 严格类型下结果要逐列收口成模型。版本号驱动迁移onUpgrade按oldVersion → newVersion依次执行 ALTER/CREATE。三、实际开发代码笔记 App 数据层3.1 建库建表// data/NoteDb.ets import { relationalStore } from kit.ArkData import { common } from kit.AbilityKit export interface Note { id: number title: string content: string isFavorite: number // 0/1 createdAt: number // 时间戳毫秒 updatedAt: number } // 数据库版本升级迁移时递增 const DB_VERSION 2 const DB_NAME note.db export class NoteDb { private store: relationalStore.RdbStore | undefined async init(context: common.Context): Promisevoid { const config: relationalStore.StoreConfig { name: DB_NAME, securityLevel: relationalStore.SecurityLevel.S1 } this.store await relationalStore.getRdbStore(context, config) // 建表幂等IF NOT EXISTS await this.store.executeSql( CREATE TABLE IF NOT EXISTS note ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL DEFAULT , is_favorite INTEGER NOT NULL DEFAULT 0, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ) ) // 常用查询索引 await this.store.executeSql( CREATE INDEX IF NOT EXISTS idx_note_updated ON note(updated_at DESC) ) } }3.2 增删改查CRUDexport class NoteDb { // 增返回新笔记 id async insert(note: { title: string; content: string }): Promisenumber { const now Date.now() const values new relationalStore.ValuesBucket() values.put(title, note.title) values.put(content, note.content) values.put(created_at, now) values.put(updated_at, now) const rowId await this.store!.insert(note, values) return rowId } // 改 async update(note: Note): Promisevoid { const values new relationalStore.ValuesBucket() values.put(title, note.title) values.put(content, note.content) values.put(is_favorite, note.isFavorite) values.put(updated_at, Date.now()) const predicates new relationalStore.RdbPredicates(note) predicates.equalTo(id, note.id) await this.store!.update(values, predicates) } // 删 async remove(id: number): Promisevoid { const predicates new relationalStore.RdbPredicates(note) predicates.equalTo(id, id) await this.store!.delete(predicates) } // 查单条ResultSet → 模型收口 async getById(id: number): PromiseNote | undefined { const predicates new relationalStore.RdbPredicates(note) predicates.equalTo(id, id) const rs await this.store!.query(predicates) try { if (rs.goToFirstRow()) { return this.toNote(rs) } return undefined } finally { rs.close() // 铁律ResultSet 用完必须 close } } }3.3 分页查询 全文检索export class NoteDb { // 分页按更新时间倒序isFavorite 置顶 async queryPage(page: number, pageSize: number, keyword?: string): PromiseNote[] { const predicates new relationalStore.RdbPredicates(note) if (keyword keyword.trim()) { // 简单 LIKE 检索小数据量够用大数据量可换 FTS5见延伸 predicates.like(title, %${keyword.trim()}%) .or() .like(content, %${keyword.trim()}%) } // 收藏优先 更新时间倒序 predicates.orderByDesc(is_favorite) .orderByDesc(updated_at) .limitAs(pageSize) .offsetAs((page - 1) * pageSize) const rs await this.store!.query(predicates) const list: Note[] [] try { while (rs.goToNextRow()) { list.push(this.toNote(rs)) } } finally { rs.close() } return list } // ResultSet → 模型逐列收口ArkTS 严格类型 private toNote(rs: relationalStore.ResultSet): Note { return { id: rs.getLong(rs.getColumnIndex(id)), title: rs.getString(rs.getColumnIndex(title)), content: rs.getString(rs.getColumnIndex(content)), isFavorite: rs.getLong(rs.getColumnIndex(is_favorite)), createdAt: rs.getLong(rs.getColumnIndex(created_at)), updatedAt: rs.getLong(rs.getColumnIndex(updated_at)) } } }3.4 事务批量操作如批量导入// 批量导入全部成功或全部回滚 async batchImport(notes: { title: string; content: string }[]): Promisenumber { const store this.store! store.beginTransaction() try { let count 0 for (const n of notes) { await this.insert(n) count } store.commit() // 全部成功才提交 return count } catch (e) { store.rollBack() // 任一失败回滚 throw e } }3.5 数据库升级迁移async init(context: common.Context): Promisevoid { const config: relationalStore.StoreConfig { name: DB_NAME, securityLevel: relationalStore.SecurityLevel.S1 } this.store await relationalStore.getRdbStore(context, config) // v1首次建表 await this.store.executeSql( CREATE TABLE IF NOT EXISTS note (...) ) // v2 起新增字段示例如 tags 标签列 // 判断是否已存在列查 pragma table_info缺少才 ALTER const hasTags await this.hasColumn(note, tags) if (!hasTags) { await this.store.executeSql(ALTER TABLE note ADD COLUMN tags TEXT NOT NULL DEFAULT \\) } } private async hasColumn(table: string, column: string): Promiseboolean { const rs await this.store!.querySql(PRAGMA table_info(${table})) try { while (rs.goToNextRow()) { if (rs.getString(rs.getColumnIndex(name)) column) return true } return false } finally { rs.close() } }说明也可以用StoreConfig.versiononUpgrade回调做版本化迁移上面检查列 ALTER的写法对已上线的库更稳妥二者按工程习惯选用。3.6 加密与安全// 涉及敏感数据本地私密笔记时开启加密库 const config: relationalStore.StoreConfig { name: note_secure.db, securityLevel: relationalStore.SecurityLevel.S1, // 加密库需先引入加密依赖如 sqlite 加密扩展按官方文档配置 encrypt: true }注意加密依赖与初始化方式随 SDK 版本不同以官方文档为准securityLevel决定数据跨设备共享的等级。3.7 数据层对外暴露Repository 模式// data/NoteRepository.ets —— 页面只认这个门面 export class NoteRepository { private db: NoteDb new NoteDb() private initialized: boolean false async ensureInit(context: common.Context) { if (!this.initialized) { await this.db.init(context) this.initialized true } } async addNote(title: string, content: string): Promisenumber { return this.db.insert({ title, content }) } async page(page: number, size: number, keyword?: string): PromiseNote[] { return this.db.queryPage(page, size, keyword) } async toggleFavorite(note: Note): Promisevoid { note.isFavorite note.isFavorite ? 0 : 1 await this.db.update(note) } // ... remove / update / batchImport / search }页面通过AppStorage或模块级单例持有 RepositoryViewModel 调它即可与第 2 篇 MVVM 衔接。四、优化与踩坑问题处理ResultSet 泄漏导致内存涨finally { rs.close() }是铁律查询一律收口主线程建库/大量写入卡顿首次建库和批量写入放 TaskPool/Worker见第 4 篇LIKE 检索慢大数据量换 FTS5 全文索引或改用MATCH升级迁移出错丢数据迁移前备份/事务包裹线上发布前用旧版本数据做升级演练并发写冲突事务包裹批量写单条写走主 store 串行即可ArkTS 严格类型报错ResultSet 列名/类型不匹配最常见逐列getXxx(getColumnIndex(...))收口五、小结与延伸RDB 四件套建库建表幂等、CRUDResultSet 收口、事务批量一致性、迁移版本化/检查列。案例沉淀NoteDb数据访问NoteRepository门面接上第 2 篇的 ViewModel 即完整 MVVM 数据链路。延伸FTS5 全文检索中文分词需自定义 tokenizer、与网络层缓存联动第 5 篇的磁盘缓存、加密库接入、分布式 RDB第 8 篇进阶。下一篇预告多媒体实战——相机拍照录像与 AVPlayer 播放器简易相机 播放器案例。
返回列表