1 关系型数据库概述
关系数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。HarmoryOS关系数据库是基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。当应用卸载后,其相关数据库会被自动清除。
关系数据库对外提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的所有数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。
关系数据库运作机制如图所示。

2 实战:关系型数据库App开发
本项目以一个"账本"为例,使用关系数据库的相关接口实现对账单的增、删、改、查操作。
实现效果,如图所示:

2.1 操作RdbStore
首先要获取一个RdbStore来操作关系数据库。
在src/main/ets目录下创建名为"common/database"的目录,用于存储常用的数据库相关的类。在common/database目录中创建工具类Rdb,代码如下:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import { relationalStore } from '@kit.ArkData'; import CommonConstants from '../constants/CommonConstants'; export default class Rdb { private rdbStore: relationalStore.RdbStore | null = null ; private tableName: string; private sqlCreateTable: string; private columns: Array<string>; constructor (tableName: string, sqlCreateTable: string, columns: Array<string>) { this .tableName = tableName; this .sqlCreateTable = sqlCreateTable; this .columns = columns; } getRdbStore(callback: Function = () => { }) { if (!callback || typeof callback === 'undefined' || callback === undefined ) { console.info('getRdbStore() has no callback!'); return ; } if (this .rdbStore !== null ) { console.info('The rdbStore exists.'); callback(); return } let context: Context = getContext(this ) as Context; relationalStore.getRdbStore(context, CommonConstants.STORE_CONFIG, (err, rdb) => { if (err) { console.error(`gerRdbStore() failed, err: ${err}`); return ; } this .rdbStore = rdb; this .rdbStore.executeSql(this .sqlCreateTable); console.info('getRdbStore() finished.'); callback(); }); } ... ... } |
数据库所需要的配置存储在common/constants/CommonConstants.ets文件中,代码如下:
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import { relationalStore } from '@kit.ArkData'; import { AccountTable } from '../../viewmodel/ConstantsInterface'; export default class CommonConstants { /** * Rdb database config. */ static readonly STORE_CONFIG: relationalStore.StoreConfig = { name: 'database.db', securityLevel: relationalStore.SecurityLevel.S1 }; } |
为了对数据进行增、删、改、查操作,我们需要封装对应接口。关系数据库接口提供的增、删、改、查方法均有callback和Promise两种异步回调方式,项目中使用callback异步回调。代码如下:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| insertData(data: relationalStore.ValuesBucket, callback: Function = () => { }) { if (!callback || typeof callback === 'undefined' || callback === undefined ) { console.info('insertData() has no callback!'); return ; } let resFlag: boolean = false ; const valueBucket: relationalStore.ValuesBucket = data; if (this .rdbStore) { this .rdbStore.insert(this .tableName, valueBucket, (err, ret) => { if (err) { console.error(`insertData() failed, err: {err}\`); callback(resFlag); ****return**** ; } console.info(\`insertData() finished: {ret}`); callback(ret); }); } } deleteData(predicates: relationalStore.RdbPredicates, callback: Function = () => { }) { if (!callback || typeof callback === 'undefined' || callback === undefined ) { console.info('deleteData() has no callback!'); return ; } let resFlag: boolean = false ; if (this .rdbStore) { this .rdbStore.delete(predicates, (err, ret) => { if (err) { console.error(`deleteData() failed, err: {err}\`); callback(resFlag); ****return**** ; } console.info(\`deleteData() finished: {ret}`); callback(!resFlag); }); } } updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function = () => { }) { if (!callback || typeof callback === 'undefined' || callback === undefined ) { console.info('updateDate() has no callback!'); return ; } let resFlag: boolean = false ; const valueBucket: relationalStore.ValuesBucket = data; if (this .rdbStore) { this .rdbStore.update(valueBucket, predicates, (err, ret) => { if (err) { console.error(`updateData() failed, err: {err}\`); callback(resFlag); ****return**** ; } console.info(\`updateData() finished: {ret}`); callback(!resFlag); }); } } query(predicates: relationalStore.RdbPredicates, callback: Function = () => { }) { if (!callback || typeof callback === 'undefined' || callback === undefined ) { console.info('query() has no callback!'); return ; } if (this .rdbStore) { this .rdbStore.query(predicates, this .columns, (err, resultSet) => { if (err) { console.error(`query() failed, err: ${err}`); return ; } console.info('query() finished.'); callback(resultSet); resultSet.close(); }); } } |
2.2 账目信息表
由于需要记录账目的类型(收入/支出)、具体类别和金额,因此我们需要创建一张存储账目信息的表,SQL脚本如下:
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import { relationalStore } from '@kit.ArkData'; import { AccountTable } from '../../viewmodel/ConstantsInterface'; export default class CommonConstants { /** * Rdb database config. */ static readonly STORE_CONFIG: relationalStore.StoreConfig = { name: 'database.db', securityLevel: relationalStore.SecurityLevel.S1 }; /** * Account table config. */ static readonly ACCOUNT_TABLE: AccountTable = { tableName: 'accountTable', sqlCreate: 'CREATE TABLE IF NOT EXISTS accountTable(id INTEGER PRIMARY KEY AUTOINCREMENT, accountType INTEGER, ' + 'typeText TEXT, amount INTEGER)', columns: ['id', 'accountType', 'typeText', 'amount'] }; } |
AccountTable表的各字段含义如下:
id:主键
accountType:账目类型。0表示支出;1表示收入。
typeText:账目的具体类别,
amount:账目金额。
在src/main/ets目录下创建名为"viewmodel"目录,并在该目录下创建与上述脚本对应的AccountData类,代码如下:
|-----------------------------------------------------------------------------------------------------------------------------------|
| export default class AccountData { id: number = -1; accountType: number = 0; typeText: string = ''; amount: number = 0; } |
在viewmodel目录下创建ConstantsInterface类,AccountTable对应的表名称等信息,代码如下:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| export interface AccountTable { tableName: string; // 表名(如 'accountTable') sqlCreate: string; // 创建表的SQL语句(如 'CREATE TABLE ...') columns: string[]; // 表的列名数组(如 ['id', 'accountType', ...]) } |
2.3 操作账目信息表
创建针对账目信息表的操作类common/database/tables/AccountTable.ets。AccountTable类封装了增、删、改、查接口。代码如下:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import { relationalStore } from '@kit.ArkData'; import AccountData from '../../../viewmodel/AccountData'; import CommonConstants from '../../constants/CommonConstants'; import Rdb from '../Rdb'; export default class AccountTable { private accountTable = new Rdb(CommonConstants.ACCOUNT_TABLE.tableName, CommonConstants.ACCOUNT_TABLE.sqlCreate, CommonConstants.ACCOUNT_TABLE.columns); constructor (callback: Function = () => { }) { this .accountTable.getRdbStore(callback); } getRdbStore(callback: Function = () => { }) { this .accountTable.getRdbStore(callback); } insertData(account: AccountData, callback: Function) { const valueBucket: relationalStore.ValuesBucket = generateBucket(account); this .accountTable.insertData(valueBucket, callback); } deleteData(account: AccountData, callback: Function) { let predicates = new relationalStore.RdbPredicates(CommonConstants.ACCOUNT_TABLE.tableName); predicates.equalTo('id', account.id); this .accountTable.deleteData(predicates, callback); } updateData(account: AccountData, callback: Function) { const valueBucket: relationalStore.ValuesBucket = generateBucket(account); let predicates = new relationalStore.RdbPredicates(CommonConstants.ACCOUNT_TABLE.tableName); predicates.equalTo('id', account.id); this .accountTable.updateData(predicates, valueBucket, callback); } query(amount: number, callback: Function, isAll: boolean = true ) { let predicates = new relationalStore.RdbPredicates(CommonConstants.ACCOUNT_TABLE.tableName); if (!isAll) { predicates.equalTo('amount', amount); } this .accountTable.query(predicates, (resultSet: relationalStore.ResultSet) => { let count: number = resultSet.rowCount; if (count === 0 || typeof count === 'string') { console.log('Query no results!'); callback([]); } else { resultSet.goToFirstRow(); const result: AccountData[] = []; for (let i = 0; i < count; i++) { let tmp: AccountData = { id: 0, accountType: 0, typeText: '', amount: 0 }; tmp.id = resultSet.getDouble(resultSet.getColumnIndex('id')); tmp.accountType = resultSet.getDouble(resultSet.getColumnIndex('accountType')); tmp.typeText = resultSet.getString(resultSet.getColumnIndex('typeText')); tmp.amount = resultSet.getDouble(resultSet.getColumnIndex('amount')); result[i] = tmp; resultSet.goToNextRow(); } callback(result); } }); } } function generateBucket(account: AccountData): relationalStore.ValuesBucket { let obj: relationalStore.ValuesBucket = {}; obj.accountType = account.accountType; obj.typeText = account.typeText; obj.amount = account.amount; return obj; } |
AccountTable类是对Rdb基础数据库操作的进一步封装,针对accountTable表提供了标准化的CRUD接口,简化了上层业务对账户数据的数据库操作,如插入一条账户记录、按 ID 删除、按ID更新账户、查询账户等。其核心思想是通过复用Rdb的通用能力,结合 CommonConstants的配置,实现业务逻辑与数据库的解耦。
2.4 页面开发
为了简化程序,突出核心逻辑,我们的界面设计得非常简单,只是2个Text组件和4个Buton组件。4个Button组件用于触发增、删、改、查操作,而Text组件用于展示应用名称和每次操作后的结果。Index.ets代码如下:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| import AccountTable from '../common/database/tables/AccountTable'; import AccountData from '../viewmodel/AccountData'; @Entry @Component struct Index { @State message: string = '关系型数据库应用App'; @State data: string = ''; private accountTable = new AccountTable(); build() { Row() { Column() { Text(this .message) .fontSize(20) .fontWeight(FontWeight.Bold) Text(this .data) .fontSize(10) .fontWeight(FontWeight.Bold) // 增加 Button(('增加'), { type: ButtonType.Capsule }) .width(140) .fontSize(40) .fontWeight(FontWeight.Medium) .margin({ top: 20, bottom: 20 }) .onClick(() => { let newAccount: AccountData = { id: 1, accountType: 0, typeText: '购买华为Mate80', amount: 9000 }; this .accountTable.insertData(newAccount, () => { }) }) // 查询 Button(('查询'), { type: ButtonType.Capsule }) .width(140) .fontSize(40) .fontWeight(FontWeight.Medium) .margin({ top: 20, bottom: 20 }) .onClick(() => { this .accountTable.query(0, (result: AccountData[]) => { this .data = JSON.stringify(result); }, true ); }) // 修改 Button(('修改'), { type: ButtonType.Capsule }) .width(140) .fontSize(40) .fontWeight(FontWeight.Medium) .margin({ top: 20, bottom: 20 }) .onClick(() => { let newAccount: AccountData = { id: 1, accountType: 0, typeText: '华为Mate80 Pro Max', amount: 11000 }; this .accountTable.updateData(newAccount, () => { }) }) // 删除 Button(('删除'), { type: ButtonType.Capsule }) .width(140) .fontSize(40) .fontWeight(FontWeight.Medium) .margin({ top: 20, bottom: 20 }) .onClick(() => { let newAccount: AccountData = { id: 1, accountType: 0, typeText: '华为Mate80', amount: 9000 }; this .accountTable.deleteData(newAccount, () => { }) }) } .width('100%') } .height('100%') } |
上述代码在aboutToAppear生命周期阶段,初始化了数据库。
状态管理通过@State装饰器定义message标题和 data动态结果,当 data变化时自动刷新界面。
数据库操作依赖 AccountTable类,这里封装了数据库CRUD方法和 AccountData账目信息数据模型。
点击"增加"按钮时,创建固定 AccountData对象,这里定义id=1,类型1,名称"华为Mate80",金额9000,调用 insertData插入数据库。
点击"查询"按钮时,调用 query方法查询全部数据(isAll=true),将结果通过 JSON.stringify转为字符串后赋值给data,界面实时显示查询结果。
点击"修改"按钮时,调用 updateData按id=1更新数据库记录。
点击"删除"按钮时,调用 deleteData按id=1删除数据库记录。
2.5 运行演示
运行应用显示的界面效果如图所示:

当用户单击"增加"后再单击"查询"时,界面如图所示,证明数据已经成功写入数据库。

当用户单击"修改"后再单击"查询"时,界面如图所示,证明数据已经被修改并更新回数
据库。

当用户单击"删除"后再单击"查询"时,界面如图所示,证明数据已经从数据库中删除。

3 小结
本应用完整展示了HarmonyOS关系型数据库的开发流程,从数据库配置与初始化,到数据模型定义与表结构设计,再到CRUD接口封装,最后通过简单UI交互验证功能。核心目标是让开发者快速掌握RDB的基本用法,理解如何通过分层封装实现高效、可维护的本地数据管理,适用于记账、笔记、配置存储等需要本地持久化的应用场景。