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

news2024/11/16 8:46:58

介绍

本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模型的Context
  let 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.coloradmin.cn/o/1541263.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

简述C语言文件操作

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文(平均质量分79)&#xff0c;分享…

java-基于springboot+vue实现的旅游信息管理系统功能介绍

开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 项目关键技术 1、JSP技术 JSP(Java…

深入探索JDK动态代理:从入门到原理的全面解析

文章目录 基本概念入门案例实现JDK动态代理的步骤入门实操拓展--动态生成代理类的几种方法方式一&#xff1a;通过getProxyClass方法获取代理实例方式二&#xff1a;通过newProxyInstance方法获取代理实例&#xff08;常用&#xff01;&#xff09;方式三&#xff1a;通过Lambd…

二十三 超级数据查看器 讲解稿 设置

二十三 超级数据查看器 讲解稿 设置 ​点击此处 以新页面 打开B站 播放当前教学视频 点击访问app下载页面 百度手机助手 下载地址 大家好&#xff0c;这节课我们讲一下&#xff0c;超级数据查看器的设置功能。 首先&#xff0c;我们打开超级数据查看器&#xff0c; 我…

AcWing 796. 子矩阵的和

这个题的重点是仿照一维的数组&#xff0c;所以a[N][N]也是从1索引开始的。画个图举个例子就非常清晰了 之所以不好理解是因为没画格子&#xff0c;一个格子代表一个点&#xff0c;就很好理解了。 java代码&#xff1a; import java.io.*; public class Main{static int N 1…

Java二阶知识点总结(七)SVN和Git

SVN 1、SVN和Git的区别 SVN是集中式的&#xff0c;也就是会有一个服务器保存所有代码&#xff0c;拉取代码的时候只能从这个服务器上拉取&#xff1b;Git是分布式的&#xff0c;也就是说每个人都保存有所有代码&#xff0c;如果要获取代码&#xff0c;可以从其他人手上获取SV…

使用 STL 容器发生异常的常见原因分析与总结

目录 1、概述 2、使用STL列表中的元素越界 3、遍历STL列表删除元素时对迭代器自加处理有问题引发越界 4、更隐蔽的遍历STL列表删除元素时引发越界的场景 5、多线程同时操作STL列表时没有加锁导致冲突 6、对包含STL列表对象的结构体进行memset操作导致STL列表对象内存出异…

操作符的属性:优先级、结合性

操作符的属性&#xff1a;优先级、结合性 优先级结合性 C语言的操作符有2个重要的属性&#xff1a;优先级、结合性&#xff0c;这两个属性决定了表达式求值的计算顺序。 优先级 优先级指的是&#xff0c;如果⼀个表达式包含多个运算符&#xff0c;哪个运算符应该优先执行。各…

RabbitMQ3.x之一_WindowServer2019中安装RabbitMQ详细教程

RabbitMQ3.x之一_WindowServer2019中安装RabbitMQ详细教程 文章目录 RabbitMQ3.x之一_WindowServer2019中安装RabbitMQ详细教程1. 安装环境说明1. WindowServer20192. ErLang与RabbitMQ对应版本 2 安装Erlang1. 安装Erlang2. ErLnag环境变量配置3. 查看是否安装成功 3. 安装Rab…

数据结构面试常见问题之串的模式匹配(KMP算法)系列-大师改进

&#x1f600;前言 KMP算法是一种改进的字符串匹配算法&#xff0c;由D.E.Knuth&#xff0c;J.H.Morris和V.R.Pratt提出&#xff0c;因此人们称它为克努特—莫里斯—普拉特操作&#xff08;简称KMP算法&#xff09; KMP算法的优势: 提高了匹配效率&#xff0c;时间复杂度为O(m…

【C++】用哈希桶模拟实现unordered_set和unordered_map

目录 一、哈希介绍1.1 哈希概念1.2 哈希冲突解决1.2.1 闭散列1.2.2 开散列 二、哈希桶2.1 实现哈希桶2.1.1 构造节点和声明成员变量2.1.2 构造与析构2.1.3 仿函数2.1.4 查找2.1.5 插入2.1.6 删除 2.2 kv模型哈希桶源代码 三、改造哈希桶3.1 beginend3.2 迭代器3.2.1 前置 3.3 改…

Linux_ubuntu中进行断点调试

文章目录 一、安装gdb调试器&#xff1a;二、使用gcc编译程序&#xff1a;三、使用gdb对程序进行调试&#xff1a;1.设置断点&#xff1a;使用break命令或简写为b来设置断点2.调试运行——run&#xff1a;3.继续执行——continue/c&#xff1a;4.单步执行&#xff1a;5.监视变量…

6.windows ubuntu 子系统 测序数据质量控制。

上一个分享&#xff0c;我们对测序数据进行了质量评估&#xff0c;接下来我们需要对数据进行数据质量控制。 数据预处理&#xff08;Data Preprocessing&#xff09;&#xff1a;包括去除接头序列&#xff08;adapter trimming&#xff09;、去除低质量序列&#xff08;qualit…

【Spring Boot 源码学习】共享 MetadataReaderFactory 上下文初始化器

《Spring Boot 源码学习系列》 共享 MetadataReaderFactory 上下文初始化器 一、引言二、往期内容三、主要内容3.1 源码初识3.2 CachingMetadataReaderFactoryPostProcessor3.2.1 register 方法3.2.1 configureConfigurationClassPostProcessor 方法 3.3 ConfigurationClassPos…

SpringMVC结合设计模式:解决MyBatisPlus传递嵌套JSON数据的难题

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

Learn OpenGL 24 点光源阴影

点光源阴影 上个教程我们学到了如何使用阴影映射技术创建动态阴影。效果不错&#xff0c;但它只适合定向光&#xff0c;因为阴影只是在单一定向光源下生成的。所以它也叫定向阴影映射&#xff0c;深度&#xff08;阴影&#xff09;贴图生成自定向光的视角。 本节我们的焦点是…

Java进阶—GC回收(垃圾回收)

1. 什么是垃圾回收 垃圾回收(Garbage Collection&#xff0c;GC)是Java虚拟机(JVM)的一项重要功能&#xff0c;用于自动管理程序中不再使用的内存。在Java中&#xff0c;程序员不需要手动释放内存&#xff0c;因为GC会自动检测并回收不再使用的对象&#xff0c;从而减少内存泄…

Java基础【上】韩顺平(反射、类加载、final接口、抽象类、内部类)

涵盖知识点&#xff1a;反射、类加载、单例模式、final、抽象类、接口、内部类&#xff08;局部内部类、匿名内部类、成员内部类、静态内部类&#xff09; P711 反射机制原理 创建如下目录结构&#xff0c;在模块下创建src文件夹&#xff0c;文件夹要设置为Sources文件夹&…

Git使用:实现文件在不同设备之间进行同步

一、注册Gitee&#xff0c;创建远程仓库 注册网址&#xff1a;登录 - Gitee.com 打开Gitee&#xff0c;注册完进行登录&#xff0c;点击右上角【】创建一个仓库 新建仓库&#xff1a; 点击创建&#xff0c;仓库创建完毕。 二、下载Git安装包&#xff0c;并创建本地仓库 下载网…

正则表达式具体用法大全~持续更新

# 正则表达式&#xff1a; ## 单字符匹配&#xff1a; python # 匹配某个字符串&#xff1a; # text "abc" # ret re.match(b,text) # print(ret.group()) # 点&#xff08;.&#xff09;&#xff1a;匹配任意的字符(除了\n)&#xff1a; # text "\nabc&quo…