OpenHarmony RDB 关系型数据库实战开发
一、前言
前面讲解的 Preferences 仅适合存储轻量键值对,面对通讯录、备忘录、商品清单、大量结构化数据时能力不足。OpenHarmony 提供 RDB(关系型数据库),基于 SQLite 内核,支持标准 SQL 语句、数据表创建、增删改查、事务操作,是应用存储大批量结构化数据的首选方案。
本文从环境依赖、数据库初始化、数据表设计、基础 CRUD、分页查询、事务操作一步步讲解,搭配完整可运行案例,同时结合前文组件、弹窗完成综合业务实战。
适用场景:备忘录、联系人、本地账单、离线缓存列表等复杂数据存储。 依赖模块:
@ohos.data.rdb
二、核心概念与前置说明
- 核心对象
RdbStore:数据库实例,执行增删改查、事务的核心对象RdbHelper:数据库工具类,负责打开 / 创建数据库ResultSet:查询结果集,遍历读取查询数据
- 数据类型:支持整型、浮点型、字符串、布尔值,兼容 SQLite 字段规则
- 权限:本地数据库无需额外申请权限,应用沙盒内私有存储
三、基础使用:数据库与数据表初始化
3.1 导入模块 & 工具类封装
统一封装数据库工具,避免重复编写初始化代码,便于项目维护。
ets
import rdb from '@ohos.data.rdb'
import common from '@ohos.app.ability.common'
// 数据库全局工具类
class RdbUtil {
private static rdbStore: rdb.RdbStore | null = null
// 数据库名称
private static readonly DB_NAME = "app_database.db"
// 数据库版本
private static readonly DB_VERSION = 1
/**
* 初始化数据库 & 创建数据表
* @param context 应用上下文
*/
static async initDB(context: common.UIAbilityContext) {
if (this.rdbStore) {
return this.rdbStore
}
// 数据库配置
let config: rdb.RdbStoreConfig = {
name: this.DB_NAME,
securityLevel: rdb.SecurityLevel.S1
}
// 打开/创建数据库
this.rdbStore = await rdb.getRdbStore(context, config, this.DB_VERSION, (store) => {
// 数据库首次创建时执行:建表语句
const createTableSql = `
CREATE TABLE IF NOT EXISTS user_note (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
create_time TEXT
)
`
store.executeSql(createTableSql)
})
return this.rdbStore
}
// 获取数据库实例
static getStore(): rdb.RdbStore | null {
return this.rdbStore
}
}
- 数据表
user_note:简易备忘录表,id自增主键,title标题,content内容,create_time创建时间。 - 版本升级:如需修改表结构,递增
DB_VERSION并在版本回调中编写更新 SQL。
3.2 应用入口初始化数据库
在页面生命周期中完成数据库初始化,全局只需执行一次。
ets
@Entry
@Component
struct RdbIndex {
context = getContext(this) as common.UIAbilityContext
aboutToAppear() {
// 初始化数据库
RdbUtil.initDB(this.context)
}
build() {
Column() {
Text("RDB 关系型数据库演示")
.fontSize(22)
.fontWeight(FontWeight.Bold)
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
四、数据表 CRUD 基础操作
4.1 新增数据(插入记录)
使用 insert 方法插入单条数据,接收表名与数据对象。
ets
@Entry
@Component
struct RdbInsertDemo {
context = getContext(this) as common.UIAbilityContext
// 插入备忘录数据
async insertNote() {
let store = await RdbUtil.initDB(this.context)
if (!store) return
// 构造插入数据
let valueBucket: rdb.ValuesBucket = {
title: "第一条备忘录",
content: "学习 OpenHarmony RDB 数据库",
create_time: new Date().toLocaleString()
}
// 执行插入
let rowId = await store.insert("user_note", valueBucket)
console.info("插入成功,行ID:", rowId)
}
build() {
Column({ space: 20 }) {
Button("新增一条备忘录")
.width(220)
.onClick(() => this.insertNote())
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
4.2 查询数据(全量查询 + 条件查询)
使用 query 方法查询数据,通过 ResultSet 遍历结果集。
ets
@Entry
@Component
struct RdbQueryDemo {
@State noteList: string[] = []
context = getContext(this) as common.UIAbilityContext
// 查询全部数据
async queryAll() {
let store = await RdbUtil.initDB(this.context)
if (!store) return
// 查询条件:查所有字段、无筛选条件
let predicates = new rdb.RdbPredicates("user_note")
let resultSet = await store.query(predicates)
this.noteList = []
// 遍历结果集
while (resultSet.goToNextRow()) {
let id = resultSet.getLong(resultSet.getColumnIndex("id"))
let title = resultSet.getString(resultSet.getColumnIndex("title"))
let time = resultSet.getString(resultSet.getColumnIndex("create_time"))
this.noteList.push(`ID:${id} 标题:${title} 时间:${time}`)
}
// 关闭结果集,释放资源
resultSet.close()
}
build() {
Column({ space: 15 }) {
Button("查询全部备忘录").onClick(() => this.queryAll())
List({ space: 5 }) {
ForEach(this.noteList, (item: string) => {
ListItem() {
Text(item).fontSize(16).padding(10)
}
})
}
.layoutWeight(1)
.width("100%")
}
.width("100%")
.height("100%")
.padding(20)
}
}
条件查询示例(根据标题模糊查询)
ets
async queryByTitle(keyword: string) {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
// 模糊匹配
predicates.like("title", `%${keyword}%`)
let resultSet = await store.query(predicates)
// 遍历逻辑同上
resultSet.close()
}
4.3 修改数据
通过 RdbPredicates 设置条件,使用 update 方法更新字段。
ets
async updateNote() {
let store = RdbUtil.getStore()
if (!store) return
// 要更新的内容
let valueBucket: rdb.ValuesBucket = {
title: "修改后的标题",
content: "内容已更新"
}
// 更新条件:id = 1
let predicates = new rdb.RdbPredicates("user_note")
predicates.equalTo("id", 1)
let count = await store.update(valueBucket, predicates)
console.info("受影响行数:", count)
}
4.4 删除数据
支持按条件删除、清空整张表。
ets
// 按ID删除
async deleteNoteById(delId: number) {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
predicates.equalTo("id", delId)
let count = await store.delete(predicates)
console.info("删除行数:", count)
}
// 清空整张表
async clearTable() {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
await store.delete(predicates)
}
五、进阶用法:分页查询
数据量较大时,使用分页加载提升性能,核心依靠 predicates.offset() 和 predicates.limit()。
ets
/**
* 分页查询
* @param pageIndex 页码,从0开始
* @param pageSize 每页条数
*/
async queryByPage(pageIndex: number, pageSize: number) {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
predicates.offset(pageIndex * pageSize) // 偏移量
predicates.limit(pageSize) // 每页数量
predicates.orderByDesc("id") // 按ID倒序
let resultSet = await store.query(predicates)
// 遍历读取数据
resultSet.close()
}
六、进阶用法:事务操作
多条数据批量增删改时,使用事务保证数据一致性,要么全部执行成功,要么全部回滚。
ets
async batchInsertWithTransaction() {
let store = RdbUtil.getStore()
if (!store) return
try {
// 开启事务
await store.beginTransaction()
// 批量插入多条数据
let data1: rdb.ValuesBucket = { title: "事务笔记1", content: "测试事务", create_time: new Date().toLocaleString() }
let data2: rdb.ValuesBucket = { title: "事务笔记2", content: "测试事务", create_time: new Date().toLocaleString() }
await store.insert("user_note", data1)
await store.insert("user_note", data2)
// 提交事务
await store.commit()
console.info("事务执行成功")
} catch (err) {
// 异常回滚
await store.rollBack()
console.error("事务执行失败,已回滚", err)
}
}
七、综合实战:备忘录完整页面(RDB + 自定义弹窗 + List)
整合前面所学的 CustomDialog 自定义弹窗、List 列表、RDB 数据库,实现新增、查看、删除备忘录完整功能。
7.1 新增笔记弹窗
ets
@CustomDialog
struct AddNoteDialog {
controller: CustomDialogController
@DialogParam onSubmit: (title: string, content: string) => void
@State noteTitle: string = ""
@State noteContent: string = ""
build() {
Column({ space: 15 }) {
Text("新增备忘录").fontSize(20).fontWeight(FontWeight.Bold)
TextInput({ placeholder: "请输入标题" })
.width("100%")
.height(45)
.onChange(v => this.noteTitle = v)
TextInput({ placeholder: "请输入内容" })
.width("100%")
.height(80)
.onChange(v => this.noteContent = v)
Row({ space: 20 }) {
Button("取消")
.layoutWeight(1)
.backgroundColor("#999")
.onClick(() => this.controller.close())
Button("保存")
.layoutWeight(1)
.backgroundColor("#007DFF")
.onClick(() => {
this.onSubmit(this.noteTitle, this.noteContent)
this.controller.close()
})
}
}
.width(320)
.padding(20)
.backgroundColor(Color.White)
.borderRadius(12)
}
}
7.2 主页面(列表展示 + 增删操作)
ets
@Entry
@Component
struct NoteMainPage {
@State noteArr: Array<rdb.ValuesBucket> = []
context = getContext(this) as common.UIAbilityContext
// 弹窗控制器
addDialog: CustomDialogController = new CustomDialogController({
builder: AddNoteDialog({
onSubmit: (title: string, content: string) => {
this.saveNote(title, content)
}
}),
autoCancel: false
})
aboutToAppear() {
// 初始化数据库 + 加载列表
RdbUtil.initDB(this.context).then(() => {
this.loadNoteList()
})
}
// 保存笔记到数据库
async saveNote(title: string, content: string) {
let store = RdbUtil.getStore()
if (!store || !title) return
let bucket: rdb.ValuesBucket = {
title: title,
content: content,
create_time: new Date().toLocaleString()
}
await store.insert("user_note", bucket)
// 重新刷新列表
this.loadNoteList()
}
// 加载列表数据
async loadNoteList() {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
predicates.orderByDesc("id")
let resultSet = await store.query(predicates)
let tempList: Array<rdb.ValuesBucket> = []
while (resultSet.goToNextRow()) {
tempList.push({
id: resultSet.getLong(resultSet.getColumnIndex("id")),
title: resultSet.getString(resultSet.getColumnIndex("title")),
content: resultSet.getString(resultSet.getColumnIndex("content")),
create_time: resultSet.getString(resultSet.getColumnIndex("create_time"))
})
}
resultSet.close()
this.noteArr = tempList
}
// 删除单条笔记
async deleteItem(id: number) {
let store = RdbUtil.getStore()
if (!store) return
let predicates = new rdb.RdbPredicates("user_note")
predicates.equalTo("id", id)
await store.delete(predicates)
this.loadNoteList()
}
build() {
Column() {
// 顶部标题+新增按钮
Row() {
Text("我的备忘录")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)
Button("新增")
.width(80)
.onClick(() => this.addDialog.open())
}
.width("100%")
.padding(15)
// 笔记列表
List({ space: 8 }) {
ForEach(this.noteArr, (item: rdb.ValuesBucket) => {
ListItem() {
Row() {
Column({ space: 5 }) {
Text(item.title as string)
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text(item.content as string)
.fontSize(14)
.fontColor("#666")
Text(item.create_time as string)
.fontSize(12)
.fontColor("#999")
}
.layoutWeight(1)
Button("删除")
.width(70)
.backgroundColor("#f56c6c")
.onClick(() => this.deleteItem(item.id as number))
}
.width("100%")
.padding(12)
.backgroundColor(Color.White)
.borderRadius(8)
}
})
}
.layoutWeight(1)
.width("100%")
}
.width("100%")
.height("100%")
.backgroundColor("#F5F5F5")
}
}
八、开发规范与踩坑总结
8.1 使用规范
- 资源释放:
ResultSet使用完毕必须调用close(),避免内存泄漏。 - 数据库初始化:全局仅初始化一次,建议放在应用入口页面。
- 批量操作:多条增删改优先使用事务,提升执行效率、保证数据安全。
- 分表原则:不同业务数据拆分不同数据表,不要全部存在一张表。
- 版本管理:表结构变更时,递增数据库版本号,编写版本升级逻辑。

8.2 常见问题
- 表不存在:检查建表 SQL 语句是否正确,数据库初始化是否执行。
- 查询无数据:核对字段名、查询条件大小写,SQLite 默认区分字段名。
- 卡顿问题:大数据列表必须使用分页查询,禁止一次性查询全表。
- 线程阻塞:所有数据库操作均为异步,统一使用
async/await。
8.3 选型对比
表格
| 存储方案 | 适用场景 | 优缺点 |
|---|---|---|
| Preferences | 轻量配置、开关、登录状态 | 简单易用,不支持复杂结构 |
| RDB | 大量结构化数据、列表数据 | 支持 SQL、事务、分页,功能强大 |
九、系列总结 & 后续拓展
至此,整套 OpenHarmony 应用开发核心技术栈 全部完结: UI 组件 → 布局 / 懒加载 → 路由传参 → Preferences → 网络请求 → AppStorage 全局状态 → 自定义弹窗 → RDB 数据库
更多推荐


所有评论(0)