鸿蒙实战开发-使用关系型数据库实现对账单的增、删、改、查

2024-03-25 02:44

本文主要是介绍鸿蒙实战开发-使用关系型数据库实现对账单的增、删、改、查,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

介绍

本Codelab以记账为例,使用关系型数据库的相关接口实现了对账单的增、删、改、查操作。实现效果如图所示:

相关概念

  • 关系型数据库:基于关系模型来管理数据的数据库,提供了增、删、改、查等接口,也可运行输入的SQL语句满足复杂场景需要。

环境搭建

软件要求

  • DevEco Studio版本:DevEco Studio 3.1 Release。
  • OpenHarmony SDK版本:API version 9。

硬件要求

  • 开发板类型:润和RK3568开发板。
  • OpenHarmony系统:3.2 Release。

环境搭建

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  • 完成DevEco Device Tool的安装
  • 完成RK3568开发板的烧录

3.搭建开发环境。

  • 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  • 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。
  • 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇Codelab只对核心代码进行讲解,

├──entry/src/main/ets               // 代码区
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets     // 公共常量
│  │  ├──database
│  │  │  ├──tables
│  │  │  │  └──AccountTable.ets     // 账目数据表
│  │  │  └──Rdb.ets                 // RDB数据库
│  │  └──utils                      // 日志类
│  │     ├──ConsoleLogger.ets
│  │     ├──HiLogger.ets
│  │     ├──ILogger.ets
│  │     └──Logger.ets
│  ├──entryability
│  │  └──EntryAbility.ts            // 程序入口类
│  ├──pages
│  │  └──MainPage.ets               // 应用首页
│  ├──view
│  │  └──DialogComponent.ets        // 自定义弹窗
│  └──viewmodel
│     ├──AccountData.ets            // 账目类接口
│     ├──AccountItem.ets            // 账目资源类接口
│     ├──AccountList.ets            // 账目类型model
│     └──ConstantsInterface.ets     // 常量接口
└──entry/src/main/resources         // 资源文件夹

创建数据库

要使用关系型数据库存储用户数据,首先要进行数据库的创建,并提供基本的增、删、改、查接口。

导入关系型数据库模块:

import relationalStore from '@ohos.data.relationalStore';

关系型数据库提供以下两个基本功能:

首先要获取一个RdbStore实例来操作关系型数据库,代码如下

// Rdb.ets
getRdbStore(callback: Function = () => {
}) {// 如果已经获取到RdbStore则不做操作if (!callback || typeof callback == 'undefined' || callback == undefined) {Logger.verbose(`${CommonConstants.RDB_TAG}`, 'getRdbStore() has no callback!');return;}// 应用上下文,本Codelab使用API9 Stage模型的Contextlet context: Context = getContext(this) as Context;relationalStore.getRdbStore(context, CommonConstants.STORE_CONFIG, (err, rdb) => {if (err) {Logger.error(`${RDB_TAG}`, 'gerRdbStore() failed, err: ' + err);return;}this.rdbStore = rdb;// 获取到RdbStore后,需使用executeSql接口初始化数据库表结构和相关数据this.rdbStore.executeSql(this.sqlCreateTable);          Logger.verbose(`${RDB_TAG}`, 'getRdbStore() finished.');callback();});
}

关系型数据库接口提供的增、删、改、查操作均有callback和Promise两种异步回调方式,本Codelab使用了callback异步回调,其中插入数据使用了insert()接口,实现代码如下:

// Rdb.ets
insertData(data: relationalStore.ValuesBucket, callback: Function = () => {
}) {if (!callback || typeof callback == 'undefined' || callback == undefined) {Logger.verbose(`${CommonConstants.RDB_TAG}`, '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) {Logger.error(`${CommonConstants.RDB_TAG}`, 'insertData() failed, err: ' + err);callback(resFlag);return;}Logger.verbose(`${CommonConstants.RDB_TAG}`, 'insertData() finished: ' + ret);callback(ret);});}
}

删除数据使用了delete()接口,实现代码如下:

// Rdb.ets
deleteData(predicates: relationalStore.RdbPredicates, callback: Function = () => {
}) {if (!callback || typeof callback == 'undefined' || callback == undefined) {Logger.verbose(`${CommonConstants.RDB_TAG}`, 'deleteData() has no callback!');return;}let resFlag: boolean = false;if (this.rdbStore) {this.rdbStore.delete(predicates, (err, ret) => {if (err) {Logger.error(`${CommonConstants.RDB_TAG}`, 'deleteData() failed, err: ' + err);callback(resFlag);return;}Logger.verbose(`${CommonConstants.RDB_TAG}`, 'deleteData() finished: ' + ret);callback(!resFlag);});}
}

更新数据使用了update()接口,实现代码如下:

// Rdb.ets
updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function = () => {
}) {if (!callback || typeof callback == 'undefined' || callback == undefined) {Logger.verbose(`${CommonConstants.RDB_TAG}`, '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) {Logger.error(`${CommonConstants.RDB_TAG}`, 'updateData() failed, err: ' + err);callback(resFlag);return;}Logger.verbose(`${CommonConstants.RDB_TAG}`, 'updateData() finished: ' + ret);callback(!resFlag);});}
}

查找数据使用了query()接口,实现代码如下:

// Rdb.ets
query(predicates: relationalStore.RdbPredicates, callback: Function = () => {
}) {if (!callback || typeof callback == 'undefined' || callback == undefined) {Logger.verbose(`${CommonConstants.RDB_TAG}`, 'query() has no callback!');return;}if (this.rdbStore) {this.rdbStore.query(predicates, this.columns, (err, resultSet) => {if (err) {Logger.error(`${CommonConstants.RDB_TAG}`, 'query() failed, err: ' + err);return;}Logger.verbose(`${CommonConstants.RDB_TAG}`, 'query() finished.');callback(resultSet);resultSet.close();});}
}

本Codelab需要记录账目的类型(收入/支出)、具体类别和金额,因此需要创建一张存储账目信息的表,表头如下:

创建该表的SQL语句为:

CREATE TABLE IF NOT EXISTS accountTable(id INTEGER PRIMARY KEY AUTOINCREMENT, accountType INTEGER, typeText TEXT, amount INTEGER
)

该表需要封装增、删、改、查接口,代码如下:

// AccountTable.ets
// 插入数据
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);// 根据id匹配待删除的数据行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);// 根据id匹配待删除的数据行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(`${CommonConstants.TABLE_TAG}` + '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);}});
}

功能实现

首先创建应用主页面,主要包括使用Search组件创建的搜索栏和使用List组件创建的账目清单,如下图所示:

在打开应用时,需要查询数据库中存储的账目并显示在主页面,因此生命周期函数aboutToAppear()应写为:

// Mainpage.ets
aboutToAppear() {this.AccountTable.getRdbStore(() => {  // 获取数据库this.AccountTable.query(0, (result: AccountData[]) => {  // 查询数据库中的全部账目this.accounts = result;}, true);});
}

点击右上角的“编辑”图标,主页面变为如下效果:

可以选中需要删除的账目,点击下方“删除”图标后删除对应账目,对应代码如下:

// Mainpage.ets
deleteListItem() {for (let i = 0; i < this.deleteList.length; i++) {  // 删除每一项选中的账目并更新页面上的账目清单let index = this.accounts.indexOf(this.deleteList[i]);this.accounts.splice(index, 1);this.AccountTable.deleteData(this.deleteList[i], () => {});}this.deleteList = [];this.isEdit = false;
}

搜索栏在键入文本并回车时,实现搜索功能:

// Mainpage.ets
.onSubmit((searchValue: string) => {if (searchValue === '') {  // 如果搜索栏为空,查找全部账目this.AccountTable.query(0, (result: AccountData[]) => {this.accounts = result;}, true);} else {this.AccountTable.query(Number(searchValue), (result: AccountData[]) => {  // 查找金额为val的账目this.accounts = result;}, false);}
})

右下角的“添加”按钮可以打开一个自定义弹窗,并在弹窗里新建账目信息:

// Mainpage.ets
.onClick(() => {this.isInsert = true;  // 插入模式this.newAccount = { id: 0, accountType: 0, typeText: '', amount: 0 };this.dialogController.open();
})

点击账目清单中的某个账目,也可以打开自定义弹窗,并修改账目信息:

// Mainpage.ets
selectListItem(item: AccountData) {this.isInsert = false;  // 编辑模式this.index = this.accounts.indexOf(item);this.newAccount = {id: item.id,accountType: item.accountType,typeText: item.typeText,amount: item.amount};
}

自定义弹窗由使用Tabs组件创建的账目类别、使用TextInput组件创建的输入栏和确定按钮组成,如下图所示:

点击“确定”按钮会调用accept()函数,根据isInsert的值来进行账目的添加或修改,代码如下:

// Mainpage.ets
accept(isInsert: boolean, newAccount: AccountData): void {if (isInsert) {  // 插入模式,往数据库插入一个新账目Logger.verbose(`${CommonConstants.INDEX_TAG}`, 'The account inserted is: ' + JSON.stringify(newAccount));this.AccountTable.insertData(newAccount, (id: number) => {newAccount.id = id;this.accounts.push(newAccount);});} else {  // 编辑模式,更新当前选中的账目并写入数据库,刷新页面的账目清单this.AccountTable.updateData(newAccount, () => {});let list = this.accounts;this.accounts = [];list[this.index] = newAccount;this.accounts = list;this.index = -1;}
}


通过本Codelab的学习,您已经学会了关系型数据库的基本用法。

总结

您已经完成了本次Codelab的学习,并学会了使用关系型数据库的相关接口实现对数据的增、删、改、查操作。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》

这篇关于鸿蒙实战开发-使用关系型数据库实现对账单的增、删、改、查的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/843615

相关文章

如何使用Docker部署FTP和Nginx并通过HTTP访问FTP里的文件

《如何使用Docker部署FTP和Nginx并通过HTTP访问FTP里的文件》本文介绍了如何使用Docker部署FTP服务器和Nginx,并通过HTTP访问FTP中的文件,通过将FTP数据目录挂载到N... 目录docker部署FTP和Nginx并通过HTTP访问FTP里的文件1. 部署 FTP 服务器 (

Vue中动态权限到按钮的完整实现方案详解

《Vue中动态权限到按钮的完整实现方案详解》这篇文章主要为大家详细介绍了Vue如何在现有方案的基础上加入对路由的增、删、改、查权限控制,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、数据库设计扩展1.1 修改路由表(routes)1.2 修改角色与路由权限表(role_routes)二、后端接口设计

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

Qt实现发送HTTP请求的示例详解

《Qt实现发送HTTP请求的示例详解》这篇文章主要为大家详细介绍了如何通过Qt实现发送HTTP请求,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、添加network模块2、包含改头文件3、创建网络访问管理器4、创建接口5、创建网络请求对象6、创建一个回复对

Python中配置文件的全面解析与使用

《Python中配置文件的全面解析与使用》在Python开发中,配置文件扮演着举足轻重的角色,它们允许开发者在不修改代码的情况下调整应用程序的行为,下面我们就来看看常见Python配置文件格式的使用吧... 目录一、INI配置文件二、YAML配置文件三、jsON配置文件四、TOML配置文件五、XML配置文件

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

C++实现回文串判断的两种高效方法

《C++实现回文串判断的两种高效方法》文章介绍了两种判断回文串的方法:解法一通过创建新字符串来处理,解法二在原字符串上直接筛选判断,两种方法都使用了双指针法,文中通过代码示例讲解的非常详细,需要的朋友... 目录一、问题描述示例二、解法一:将字母数字连接到新的 string思路代码实现代码解释复杂度分析三、

grom设置全局日志实现执行并打印sql语句

《grom设置全局日志实现执行并打印sql语句》本文主要介绍了grom设置全局日志实现执行并打印sql语句,包括设置日志级别、实现自定义Logger接口以及如何使用GORM的默认logger,通过这些... 目录gorm中的自定义日志gorm中日志的其他操作日志级别Debug自定义 Loggergorm中的

MySQL InnoDB引擎ibdata文件损坏/删除后使用frm和ibd文件恢复数据

《MySQLInnoDB引擎ibdata文件损坏/删除后使用frm和ibd文件恢复数据》mysql的ibdata文件被误删、被恶意修改,没有从库和备份数据的情况下的数据恢复,不能保证数据库所有表数据... 参考:mysql Innodb表空间卸载、迁移、装载的使用方法注意!此方法只适用于innodb_fi