跟老卫学HarmonyOS开发:ArkTS关系型数据库开发

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 本节以“账本”为例,使用关系型数据库接口实现账单的增、删、改、查操作。通过创建ArkTSRdb应用,演示如何操作RdbStore进行数据管理,并结合界面按钮实现交互功能。

本节以一个“账本”为例,使用关系型数据库的相关接口实现了对账单的增、删、改、查操作。

为了演示该功能,创建一个名为“ArkTSRdb”的应用,源码见文末。

操作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';

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();
    });
  }
}

账目信息的表示

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

export default class CommonConstants {
  // ...

  /**
   * Account table config.
   */
  static readonly ACCOUNT_TABLE: TableConfig = {
    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']
  };
}

interface TableConfig {
  tableName: string;
  sqlCreate: string;
  columns: Array<string>;
}

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;
}

操作账目信息表

创建针对账目信息表的操作类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;
}

设计界面

为了简化程序,突出核心逻辑,我们的界面设计的非常简单,只是一个Text组件和四个Button组件。四个Button组件用于触发增、删、改、查操作,而Text组件用于展示每次操作后的结果。修改Index代码如下:

import AccountTable from '../common/database/tables/AccountTable';
import AccountData from '../viewmodel/AccountData';

@Entry
@Component
struct Index {
   
  @State message: string = 'Hello World'
  private accountTable = new AccountTable();

  build() {
   
    Row() {
   
      Column() {
   
        Text(this.message)
          .fontSize(50)
          .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: '苹果', amount: 0 };
            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.message = 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: 1, typeText: '栗子', amount: 1 };
            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: 1, typeText: '栗子', amount: 1 };
            this.accountTable.deleteData(newAccount, () => {
   
            })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

上述代码,在aboutToAppear生命周期阶段,初始化了数据库。点击“新增”会将预设好的数据“{ id: 1, accountType: 0, typeText: '苹果', amount: 0 }”写入到数据库。点击“修改”会将预设好的“{ id: 1, accountType: 1, typeText: '栗子', amount: 1 }”的数据更新到数据库。点击“删除”则会将预设好的“{ id: 1, accountType: 1, typeText: '栗子', amount: 1 }”的数据从数据库删除。

运行

运行应用显示的界面效果如下图10-3所示。

当用户点击“增加”后再点击“查询”时,界面如下图10-4所示,证明数据已经成功写入数据库。

当用户点击“修改”后再点击“查询”时,界面如下图10-5所示,证明数据已经被修改并更新回数据库。

当用户点击“删除”后再点击“查询”时,界面如下图10-6所示,证明数据已经从数据库删除。

源码

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
2月前
|
存储 关系型数据库 数据库
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
本文通过一个 Agentic RAG 应用的完整构建流程,展示了如何借助 RDS Supabase 快速搭建具备知识处理与智能决策能力的 AI 应用,展示从数据准备到应用部署的全流程,相较于传统开发模式效率大幅提升。
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
3月前
|
存储 IDE 定位技术
【HarmonyOS 5】鸿蒙组件&模板服务详解 - 助力高效开发的利器
在移动应用开发领域,效率与质量始终是开发者追求的核心目标。鸿蒙系统作为新兴的操作系统,为开发者提供了丰富且强大的开发资源,其中鸿蒙组件&模板服务更是成为开发者快速构建高质量应用的得力助手。
126 0
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
|
29天前
|
移动开发 网络协议 小程序
鸿蒙NEXT即时通讯/IM系统RinbowTalk v2.4版发布,基于MobileIMSDK框架、ArkTS编写
RainbowTalk是一套基于开源即时通讯讯IM框架 MobileIMSDK 的产品级鸿蒙NEXT端IM系统。纯ArkTS编写、全新开发,没有套壳、也没走捷径,每一行代码都够“纯血”。与姊妹产品RainbowChat和RainbowChat-Web 技术同源,历经考验。
64 1
|
2月前
|
缓存 移动开发 网络协议
纯血鸿蒙NEXT即时通讯/IM系统:RinbowTalk正式发布,全源码、纯ArkTS编写
RainbowTalk是一套基于MobileIMSDK的产品级鸿蒙NEXT端IM系统,目前已正式发布。纯ArkTS、从零编写,无套壳、没走捷径,每一行代码都够“纯”(详见:《RainbowTalk详细介绍》)。 MobileIMSDK是一整套开源IM即时通讯框架,历经10年,超轻量级、高度提炼,一套API优雅支持 UDP 、TCP 、WebSocket 三种协议,支持 iOS、Android、H5、标准Java、小程序、Uniapp、鸿蒙NEXT,服务端基于Netty编写。
171 1
HarmonyOS NEXT仓颉开发语言实战案例:图片预览器
本文介绍了如何使用仓颉语言实现图片放大预览器。通过弹窗组件`CustomDialogController`与`Swiper`容器结合,实现全屏图片浏览效果,支持多图切换与点击关闭功能,适配动态广场场景下的图片预览需求。
|
移动开发 Ubuntu 网络协议
嵌入式linux/鸿蒙开发板(IMX6ULL)开发 (二)Ubuntu操作入门与Linux常用命令(中)
嵌入式linux/鸿蒙开发板(IMX6ULL)开发 (二)Ubuntu操作入门与Linux常用命令
314 1
嵌入式linux/鸿蒙开发板(IMX6ULL)开发 (二)Ubuntu操作入门与Linux常用命令(中)
|
XML Web App开发 开发框架
鸿蒙开发入门 | 开发第一个鸿蒙应用+页面跳转
准备好鸿蒙开发环境后,接下来就需要创建鸿蒙项目,掌握项目的创建过程以及配置。项目创建好后,需要把项目运行在模拟器上,鸿蒙的模拟和安卓模拟器有些不同,鸿蒙提供远程模拟器和本地模拟器,通过登录华为账号登录在线模拟器,使用DevEco Studio可将项目部署到远程模拟器中。
1559 1
鸿蒙开发入门 | 开发第一个鸿蒙应用+页面跳转

热门文章

最新文章