【鸿蒙HarmonyOS NEXT】数据存储之关系型数据库RDS

news2024/9/25 17:42:59

【鸿蒙HarmonyOS NEXT】数据存储之关系型数据库RDS

  • 一、环境说明
  • 二、关系型数据库RDS介绍
  • 三、示例代码加以说明
  • 四、小结

一、环境说明

  1. DevEco Studio 版本:
    在这里插入图片描述

  2. API版本:以12为主
    在这里插入图片描述

二、关系型数据库RDS介绍

1. RDS关系型数据库简介

关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。关系型数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。支持通过ResultSet.getSendableRow方法获取Sendable数据,进行跨线程传递。
为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。
大数据量场景下查询数据可能会导致耗时长甚至应用卡死,建议如下:

  1. 单次查询数据量不超过5000条。
  • 在TaskPool中查询。
  • 拼接SQL语句尽量简洁。
  • 合理地分批次查询。
  • 该模块提供以下关系型数据库相关的常用功能:
  1. RdbPredicates: 数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
  2. RdbStore:提供管理关系数据库(RDB)方法的接口。
  3. ResultSet:提供用户调用关系型数据库查询接口之后返回的结果集合。

2. 基本概念

  • 谓词:数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数- 据库的操作条件。

  • 结果集:指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便地拿到用户想要的数据。

3. RDS应用场景:

关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。

4. 运作机制介绍:

关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。

在这里插入图片描述

三、示例代码加以说明

沿用【鸿蒙HarmonyOS NEXT】页面之间相互传递参数博文中的代码,进行测试。
使用场景模拟将用户登录的信息存储到关系型数据库中,并将RDS中的数据库抽取展示出来。

代码改写如下:

  1. 新增一个名为model的目录
    在这里插入图片描述

  2. model目录下创建名称为UserInfo的Ark TS文件,用于封装用户信息表的数据,代码如下:

    /**
     * 构造数据库表CLIENT_USER对应的实体类
     */
    export default class UserInfo {
      public id: number
      public account: string
      public password: string
    
      constructor(id: number, account: string, password: string) {
        this.id = id;
        this.account = account;
        this.password = password;
      }
    }
    
  3. LoginPage完整代码如下:

    import { router } from '@kit.ArkUI';
    
    // 引入Context相关
    import { common } from '@kit.AbilityKit';
    
    // 引入RDB相关
    import { relationalStore } from '@kit.ArkData';
    import { BusinessError } from '@kit.BasicServicesKit';
    
    
    @Preview
    @Entry
    @Component
    struct LoginPage {
      @State message: string = '登录页';
      @State btnMsg: string = '登录';
      @State account: string = ''; // 账号状态变量
      @State password: string = ''; // 密码状态变量
    
      // 获取Context
      private context = getContext(this) as common.UIAbilityContext;
      // 定义RdbStore
      private rdbStore: relationalStore.RdbStore | undefined = undefined;
    
      aboutToAppear(): void {
    
        // 构造StoreConfig对象
        const STORE_CONFIG: relationalStore.StoreConfig = {
          name: 'RdbTest.db', // 数据库文件名
          securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
          encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
          customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
          isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
        };
    
        // 判断数据库版本,如果不匹配则需进行升降级操作
        // 默认数据库版本为0,表结构:CLIENT_USER (ID INTEGER PRIMARY KEY AUTOINCREMENT, ACCOUNT TEXT NOT NULL, PASSWORD TEXT NOT NULL)'
        const SQL_CREATE_TABLE =
          'CREATE TABLE IF NOT EXISTS CLIENT_USER (ID INTEGER PRIMARY KEY AUTOINCREMENT, ACCOUNT TEXT NOT NULL, PASSWORD TEXT NOT NULL)'; // 建表Sql语句
    
        // 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作,使用callback异步回调。
        relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
          if (err) {
            console.error(`At LoginPage Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
            return;
          }
          console.info('At LoginPage Succeeded in getting RdbStore.');
          // 当数据库创建时,数据库默认版本为0
          if (store.version === 0) {
            store.executeSql(SQL_CREATE_TABLE); // 创建数据表
            // 设置数据库的版本,入参为大于0的整数
            store.version = 1;
          }
          // 赋值给当前的rdbStore对象,确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
          this.rdbStore = store
        });
      }
    
      build() {
        Column() {
          Text(this.message)
            .id('HelloWorld')
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .width('100%')
            .height(50)
            .textAlign(TextAlign.Center)
            .backgroundColor(0xF1F3F5)
    
          Image($r('app.media.startIcon'))
            .width(150)
            .height(150)
            .margin({ top: 40, bottom: 40 })
    
          TextInput({ placeholder: '请输入手机号' })
            .maxLength(11)// 最大长度
            .type(InputType.Number)// 输入类型为数字
            .inputStyle()// 应用自定义样式
            .onChange((value: string) => {
              this.account = value; // 更新账号状态
            })
          Line().lineStyle() // 应用自定义Line样式
    
          // 密码输入框
          TextInput({ placeholder: '请输入密码' })
            .maxLength(12)// 最大长度
            .type(InputType.Password)// 输入类型为密码
            .inputStyle()// 应用自定义样式
            .onChange((value: string) => {
              // TODO: 生产环境需要使用正则表达式对手机号进行验证
              this.password = value; // 更新密码状态
            })
          Line().lineStyle() // 应用自定义Line样式
    
    
          Button(this.btnMsg)
            .width('80%')
            .margin({ top: 100 })
            .height(50)
            .onClick(() => {
              if (this.account === undefined || this.account === '') {
                console.info('请输入账号')
                return
              }
    
              if (this.password === undefined || this.password === '') {
                console.info('请输入密码')
                return
              }
    
              // 使用ArkData RDB数据库 以建表方式存储用户信息数据
              if (this.rdbStore !== undefined) {
                // 构造insert函数所需ValuesBucket对象
                const userInfo: relationalStore.ValuesBucket = {
                  ACCOUNT: this.account,
                  PASSWORD: this.password
                };
                // 插入数据到指定表中
                this.rdbStore.insert('CLIENT_USER', userInfo, (err: BusinessError, rowId: number) => {
                  // 如果成功插入,则返回该行所在表中的行号,否则返回-1
                  if (rowId === -1) {
                    console.error(`At LoginPage,插入数据失败,错误代码:${err.code},错误提示:${err.message}`)
                    return
                  }
                  console.info(`At LoginPage,插入数据成功,rowId为 ${rowId}`)
                })
              }
    
              // 跳转到首页
              router.pushUrl({
                url: 'pages/HomePage',
                params: {
                  account: this.account,
                  password: this.password
                }
              })
            })
        }
        .height('100%')
        .width('100%')
        .padding(0)
      }
    }
    
    // TextInput组件的自定义样式扩展
    @Extend(TextInput)
    function inputStyle() {
      .placeholderColor(Color.Gray) // 占位符颜色
      .height(50) // 输入框高度
      .fontSize(15) // 字体大小
      .backgroundColor(0xF1F3F5) // 背景颜色
      .width('90%') // 宽度为父组件的100%
      .padding({ left: 12 }) // 左侧填充
      .margin({ top: 15 }) // 上方边距
    }
    
    // Line组件的自定义样式扩展
    @Extend(Line)
    function lineStyle() {
      .width('100%') // 宽度为父组件的100%
      .height(1) // 高度
      .backgroundColor(0xF1F3F5) // 背景颜色
    }
    
    
    
  4. HomePage完整代码如下:

    import { router } from '@kit.ArkUI';
    import { relationalStore } from '@kit.ArkData';
    import { common } from '@kit.AbilityKit'
    import { BusinessError } from '@kit.BasicServicesKit';
    import UserInfo from '../model/UserInfo';
    
    
    @Preview
    @Entry
    @Component
    struct HomePage {
      @State message: string = '首页';
      // 定义List组件所需展示的数组,初始化为null
      @State listUserInfo: Array<UserInfo> = []
      // 获取Context
      private context = getContext(this) as common.UIAbilityContext;
      // 定义RdbStore
      private rdbStore: relationalStore.RdbStore | undefined = undefined;
    
      aboutToAppear(): void {
        // 使用ArkData RDB数据库 获取用户数据
        // 构造StoreConfig对象
        const STORE_CONFIG: relationalStore.StoreConfig = {
          name: 'RdbTest.db', // 数据库文件名
          securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
          encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
          customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
          isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
        };
    
        // 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作,使用callback异步回调。
        relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
          if (err) {
            console.error(`At HomePage Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
            return;
          }
          console.info('At HomePage Succeeded in getting RdbStore.');
          // 当数据库创建时,数据库已更新版本为1
          // 赋值给当前的rdbStore对象,确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
          if (store.version === 1) {
            this.rdbStore = store
            // 测试查询出来看看
            //let clientUserPredicate = new relationalStore.RdbPredicates('CLIENT_USER') // CLIENT_USER 为表名称
            this.rdbStore.querySql('SELECT ID,ACCOUNT,PASSWORD FROM CLIENT_USER ORDER BY ID DESC;',
              (err: BusinessError, resultSet) => {
                if (err) {
                  console.error(`LoginPage 读取数据库表失败,错误代码:${err.code},错误信息为:${err.message}`)
                  return
                }
                console.info(`读取的结果为(对象形式): ${resultSet}`)
                // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
                while (resultSet.goToNextRow()) {
                  const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
                  const account = resultSet.getLong(resultSet.getColumnIndex('ACCOUNT'));
                  const passwd = resultSet.getString(resultSet.getColumnIndex('PASSWORD'));
                  console.info(`id=${id}, account=${account}, passwd=${passwd}`);
                  let userInfo = new UserInfo(id, account.toString(), passwd)
                  this.listUserInfo.push(userInfo)
                }
                // 释放数据集的内存
                resultSet.close();
              })
          }
        });
      }
    
      build() {
        Column() {
          Text(this.message)
            .fontSize(30)
            .width('100%')
            .height(50)
            .textAlign(TextAlign.Center)
            .backgroundColor(0xF1F3F5)
    
          Blank().height(10)
    
          List({ initialIndex: 0, space: 5 }) {
            ForEach(this.listUserInfo, (user: UserInfo, index: number) => {
              ListItem() {
                Row() {
                  Column() {
                    Image($r('app.media.goods_icon_svg')).width(80).height(60)
                  }.width('10%')
    
                  Column() {
                    Text(`ID: ${user.id}`).width('100%').padding({left: 10})
                    Text(`账号: ${user.account}`).width('100%').padding({left: 10})
                    Text(`密码:${user.password}`).width('100%').padding({left: 10})
                  }.width('90%')
                }
                .alignItems(VerticalAlign.Center)
                .width('100%')
              }
              .height(80)
              .width('100%')
              .backgroundColor(0xF1F3F5)
    
            }, (item: UserInfo, index: number) => index.toString())
          }
          .height('80%')
          .width('100%')
    
          Button('返回上一页')
            .width('80%')
            .margin({ top: 10 })
            .height(50)
            .onClick(() => {
              // 返回登录页面
              router.showAlertBeforeBackPage({ message: '确认返回上一页吗?' })
              router.back({
                url: 'pages/LoginPage',
                params: {
                  msg: 'homepage'
                }
              })
            })
        }
        .height('100%')
        .width('100%')
      }
    }
    

测试步骤如下:

  1. 打开模拟器,并将代码部署到模拟器上,当模拟器正常运行代码后,输入用户名和密码
    在这里插入图片描述

  2. 点击手机模拟器上应用的登录按钮,跳转到了首页,然后再从首页返回登录页,查看控制台日志,内容如截图红色框或者首页文字所示:
    在这里插入图片描述

四、小结

通过上述的说明和示例演示,相信大家已经很清楚关系型数据库RDS读写数据的用法了。细心的读者朋友可能会问,如何删除数据呢?或者有何约束和限制呢?感兴趣的读者朋友可以对照鸿蒙官网上的示例尝试下,欢迎大家的留言,我们在留言区进行讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164332.html

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

相关文章

VMware提供虚拟硬盘并使得Oracle Linux集群共享块设备并绑定raw设备。

一、Vmware操作 nodeA : 1、创建SCSI控制器:类型为物理。 添加新磁盘。 类型为独立-持久。 nodeB: 新增磁盘,但是选择node A刚才创建的磁盘。 类型:独立-持久。 二、OS层操作 两台都要做绑定。 详细步骤 1. 创建 raw 设备节点 首先,确保 /dev/raw 目录存在。如果不…

5分钟精通Excel在go中的使用

一些简单操作可以在官方文档中找到&#xff0c;应该足够无经验的朋友们入门 介绍 - 《Excelize v2.2 中文文档》 - 书栈网 BookStack 这里贴一个中文版的链接&#xff08;以excelize库为例&#xff0c;相对其他库来说&#xff0c;体验很不错&#xff09;&#xff0c;不过要注…

沉浸式艺术创作:FLUX.1模型下的Java开发者体验之旅

文章目录 1 FLUX.1革命2 应用落地3 部署ComfyUI4 部署FLUX.15 导入工作流6 新的挑战 1 FLUX.1革命 FLUX.1&#xff1a;图像生成的新纪元 在人工智能的图像生成领域&#xff0c;FLUX.1模型的出现标志着一个新的时代。 由黑森林实验室&#xff08;Black Forest Labs&#xff0…

Android 车载应用开发指南 - CarService 详解(下)

车载应用正在改变人们的出行体验。从导航到娱乐、从安全到信息服务&#xff0c;车载应用的开发已成为汽车智能化发展的重要组成部分。而对于开发者来说&#xff0c;如何将自己的应用程序无缝集成到车载系统中&#xff0c;利用汽车的硬件和服务能力&#xff0c;是一个极具挑战性…

数据报文解析

数据报文解析 报文介绍 如下图所示&#xff0c;每一层把上传的协议包当作数据部分&#xff0c;加上自己的协议头部&#xff0c;组成自己的协议包。 一般说法&#xff1a;默认以太网协议包&#xff08;网络层从IP头部开始计算&#xff09;最大传输单元&#xff08;MTU&#x…

记一次sql查询优化

记一次sql查询优化 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 今天测试环境发现一个问题&#xff0c;就是测试同事在测试的时候&#xff0c;发现cpu一直居高不下&#xff0c;然…

CNN网络训练WISDM数据集:模型仿真及可视化分析

卷积神经网络&#xff08;CNN&#xff09;因其强大的特征提取能力和深度学习架构而备受推崇&#xff0c;CNN在处理图像数据时展现出的卓越性能&#xff0c;使其成为解决各种视觉识别任务的首选工具。WISDM数据集是一个广泛用于运动估计研究的基准数据集&#xff0c;它包含了多个…

EmguCV学习笔记 VB.Net 11.9 姿势识别 OpenPose

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

AMEYA360代理:兆易创新GD32A7系列全新一代车规级MCU介绍

兆易创新GigaDevice(股票代码 603986)今日宣布&#xff0c;重磅推出全新一代车规级MCU GD32A7系列。与上一代采用Arm Cortex-M4/M33的产品相比&#xff0c;GD32A7系列搭载了超高性能Arm Cortex-M7内核&#xff0c;提供GD32A71x/GD32A72x/GD32A74x等多款型号供用户选择。该系列产…

git 生成和查看密钥

项目场景&#xff1a; 在前端项目开发中&#xff0c;经常会用到git。一般的小公司很少去设置git令牌或者密钥&#xff1b;而在一些大公司&#xff0c;会用到这个。今天主要整理下git如何生成和查看密钥。 密钥 1、生成密钥 cat ~/.ssh/id_rsa.pub 2、查看密钥 ssh-keygen…

电容笔最建议买哪一款?2024百元价位性价比首选榜单,速速码住!

现在电容笔已经成为我们日常学习、工作和创作中不可或缺的辅助工具。无论是记笔记、做习题&#xff0c;还是进行绘画、设计&#xff0c;电容笔都发挥着关键作用。然而&#xff0c;市场上的电容笔品牌和款式繁多&#xff0c;价格也从几十元到上千元不等&#xff0c;这让消费者在…

OSError: [Errno 16] Device or resource busy: ‘.nfs*‘报错解决办法

目录 1 项目场景&问题描述&#xff1a;2 原因分析&#xff1a;2.1 问题背景&#xff1a; 3 解决方案&#xff1a;3.1 创建存放临时文件的目录3.2 使用该目录3.2.1 设置环境变量 TMPDIR3.2.2 运行时设置&#xff08;推荐&#xff09;3.2.3 代码中设置 4 总结 1 项目场景&…

Redis 键值对数据库学习

目录 一、介绍 二、安装以及连接 三、设置连接密码 四、连接报错 五、redis 操作字符串以及过期时间 六、 redis 列表操作 七、redis 集合操作 八、hash 哈希操作 九、redis 发布和订阅操作 十、RDB和AOF的两种数据持久化机制 十一、 其他机器连接redis 十二、 pyt…

【Linux】解锁文件描述符奥秘,高效缓存区的实战技巧

fd和缓冲区 1. 文件描述符fd1.1. 概念与本质1.2. 打开文件的管理1.3. 一切皆文件的理解1.4. 分配规则1.5. 重定向的本质1.5.1. dup2 2. FILE中的缓冲区2.1. 概念2.2. 存在的原因2.3. 类型(刷新方案)2.4. 存放的位置2.4.1. 代码证明、现象解释 2.5. 模拟C标准库中的方法 1. 文件…

LiveGBS流媒体平台GB/T28181功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大

LiveGBS流媒体平台GB/T28181功能-支持电子放大拉框放大直播视频拉框放大录像视频流拉框放大电子放大 1、直播播放2、录像播放3、搭建GB28181视频直播平台 1、直播播放 国标设备-》查看通道-》播放 &#xff0c;左键单击可以拉取矩形框&#xff0c;放大选中的范围&#xff0c;释…

IPD如何解决产品开发的典型问题

IPD&#xff08;Integrated Product Development&#xff0c;集成产品开发&#xff09;是一种领先的、成熟的产品开发的管理思想和管理模式。它是根据大量成功的产品开发管理实践总结出来的&#xff0c;并被大量实践证明的高效的产品开发模式。从汉捷咨询二十多年来为五百多家企…

如何通过代理使用 Squid: 综合指南

文章目录 一、简介二、什么是 Squid?三、为什么在代理服务器中使用 Squid&#xff1f;四、设置代理五、使用代理设置 Squid5.1. 第一步5.2. 第二步5.3. 第三步 六、在网络搜索中使用代理&#xff1a;实用代码示例6.1. 使用代理的 cURL6.2. 使用代理的 Python 请求 七、结论 一…

嘉立创EDA-- 线宽、过孔和电流大小对比图

导线宽度和电流大小如何来考虑 1 电流大小需要考虑问题 1、允许的温升&#xff1a;如果能够允许的铜线升高的温度越高&#xff0c;那么允许通过的电流自然也就越高 2、走线的线宽&#xff1a;线越宽 &#xff0c;导线横截面积越大&#xff0c;电阻越小&#xff0c;发热越小&a…

磨具生产制造9人共用一台工作站

随着技术的不断进步与工业自动化的深入发展&#xff0c;如何优化生产流程、提高设备利用率成为了众多企业面临的重大课题。那么在磨具生产制造中实现9人共用一台工作站呢&#xff1f; 一、背景与挑战 在磨具制造行业&#xff0c;高精度、高效率的生产要求与复杂多变的工艺流程…

smartctl 命令:查看硬盘健康状态

一、命令简介 ​smartctl​ 命令用于获取硬盘的 SMART 信息。 介绍硬盘SMART 硬盘的 SMART (Self-Monitoring, Analysis, and Reporting Technology) 技术用于监控硬盘的健康状态&#xff0c;并能提供一些潜在故障的预警信息。通过查看 SMART 数据&#xff0c;用户可以了解硬…