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

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

相关文章

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE