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

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

相关文章

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当