鸿蒙 ArkTS RelationalStore 实战全攻略:从零搭建企业级本地数据库方案
本文适合有一定 ArkTS 基础的开发者,覆盖 RelationalStore 的核心 API、Repository 封装、事务管理、数据迁移升级,以及常见坑位。代码基于 HarmonyOS SDK 5.0(API Level 12)。
为什么要用 RelationalStore?
鸿蒙提供了多种本地存储方案:
| | |
|---|
| | |
| | |
| RelationalStore(RDB) | 结构化数据,复杂查询 | GB 级 |
| | |
对于用户笔记、订单、聊天记录等场景,RelationalStore 是唯一正确选择——它基于 SQLite,支持事务、联表查询、索引优化,且提供了全套 TypeScript 类型声明。
一、权限配置与初始化
1.1 module.json5 权限声明
{ "module": { "requestPermissions": [ { "name": "ohos.permission.READ_MEDIA", "reason": "{tasks.length} 条成功`); } catch (err) { await store.rollBack(); // 回滚 console.error('[TaskRepository] 批量插入失败,已回滚', err); throw err; }}
最佳实践:任何涉及多条写操作(插入/更新/删除组合)的业务逻辑,都应该放在事务里。事务是数据一致性的最后防线。
四、数据库版本升级与数据迁移
应用迭代时常需要新增字段或新建表,错误的迁移方式会导致用户数据丢失。
// database/DbManager.ts(升级版)const DB_VERSION = 2; // 版本号递增const storeConfig: relationalStore.StoreConfig = { name: DB_NAME, securityLevel: relationalStore.SecurityLevel.S1,};// 迁移脚本:版本 n → 版本 n+1const MIGRATIONS: Record<number, string[]> = { // v1 → v2:task 表新增 tags 字段,新建 category 表 1: [ `ALTER TABLE task ADD COLUMN tags TEXT DEFAULT ''`, `CREATE TABLE IF NOT EXISTS category ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE )`, ],};export class DbManager { // ...(省略单例部分) async init(context: common.UIAbilityContext): Promise<void> { if (this.rdbStore) return; this.rdbStore = await relationalStore.getRdbStore(context, storeConfig); // 读取当前版本 const currentVersion = await this.rdbStore.version; if (currentVersion === 0) { // 全新安装:直接执行最新建表语句 await this.rdbStore.executeSql(CREATE_TABLE_TASK); await this.rdbStore.version = DB_VERSION; } else if (currentVersion < DB_VERSION) { // 逐步迁移 for (let v = currentVersion; v < DB_VERSION; v++) { const sqls = MIGRATIONS[v] ?? []; for (const sql of sqls) { await this.rdbStore.executeSql(sql); console.info(`[Migration] v{v + 1}: {Date.now()}`, content: '点击编辑内容', status: 0, priority: 2, }); await this.loadTasks(); } async toggleStatus(id: number, currentStatus: number) { const next = currentStatus === 2 ? 0 : currentStatus + 1; await repo.update(id, { status: next }); await this.loadTasks(); } async removeTask(id: number) { await repo.delete(id); await this.loadTasks(); } build() { Column() { Row() { Text('任务管理').fontSize(20).fontWeight(FontWeight.Bold) Blank() Button('+ 新增').onClick(() => this.addTask()) }.width('100%').padding(16) if (this.isLoading) { LoadingProgress().width(40).height(40) } else { List({ space: 8 }) { ForEach(this.tasks, (task: Task) => { ListItem() { Row() { Column() { Text(task.title) .fontSize(16) .decoration({ type: task.status === 2 ? TextDecorationType.LineThrough : TextDecorationType.None }) Text(`优先级: ${'★'.repeat(task.priority)}`) .fontSize(12).fontColor('#888') } .alignItems(HorizontalAlign.Start) .layoutWeight(1) Button(task.status === 2 ? '✓ 完成' : task.status === 1 ? '进行中' : '待办') .fontSize(12) .backgroundColor(task.status === 2 ? '#4CAF50' : '#FF9800') .onClick(() => this.toggleStatus(task.id!, task.status)) Button('删除').fontSize(12).fontColor('#F44336') .backgroundColor(Color.Transparent) .onClick(() => this.removeTask(task.id!)) } .padding(12) .backgroundColor('#FAFAFA') .borderRadius(8) } }) } .width('100%') .padding({ left: 16, right: 16 }) } } .width('100%').height('100%') }}
六、性能优化技巧
| | |
|---|
| 分页:predicates.limitAs(20).offsetAs(page * 20) | |
| 创建索引:CREATE INDEX idx_title ON task(title) | |
| | |
| store.querySql() | |
| SELECT COUNT(*) FROM task WHERE status=? | |
// 分页查询示例async findPage(page: number, pageSize: number = 20, status?: number): Promise<Task[]> { const predicates = new relationalStore.RdbPredicates(TABLE); if (status !== undefined) predicates.equalTo('status', status); predicates .orderByDesc('priority') .limitAs(pageSize) .offsetAs(page * pageSize); const cursor = await this.store.query(predicates, ['*']); const results = this.cursorToTasks(cursor); cursor.close(); return results;}// 原生 SQL 联表查询async findTasksWithCategory(): Promise<relationalStore.ResultSet> { const sql = ` SELECT t.*, c.name AS category_name FROM task t LEFT JOIN category c ON t.category_id = c.id WHERE t.status != 2 ORDER BY t.priority DESC LIMIT 50`; return await this.store.querySql(sql, []);}
七、常见坑位汇总
| | | |
|---|
| getRdbStore | | |
| | | |
| ALTER TABLE | | |
| | | beginTransaction |
| | JS camelCase vs SQL snake_case | ValuesBucket 统一用 snake_case,Model 层做转换 |
| | | |
总结
RelationalStore 是鸿蒙本地化数据存储的核心能力,掌握它意味着你的应用可以脱离网络独立运作。本文梳理了以下关键点:
- 初始化时机:在
UIAbility.onCreate 中初始化数据库 - Repository 模式
- 事务是批量操作的必选项
- 迁移脚本版本化管理
- 分页 + 索引
完整代码已上传至:GitHub - ArkTS-RelationalStore-Demo
如果本文对你有帮助,欢迎点赞收藏,评论区交流踩过的坑 👇
标签:HarmonyOS ArkTS 数据库 RelationalStore SQLite 鸿蒙开发