当前位置: 首页 > news >正文

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

【鸿蒙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: numberpublic account: stringpublic password: stringconstructor(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 = ''; // 密码状态变量// 获取Contextprivate context = getContext(this) as common.UIAbilityContext;// 定义RdbStoreprivate 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.');// 当数据库创建时,数据库默认版本为0if (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) => {// 如果成功插入,则返回该行所在表中的行号,否则返回-1if (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> = []// 获取Contextprivate context = getContext(this) as common.UIAbilityContext;// 定义RdbStoreprivate 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.mrgr.cn/news/36008.html

相关文章:

  • 【计算机网络】TCP网络程序
  • 华为交换机配置命令
  • TCP连接如何保障数据传输安全
  • GEE ui界面实现:用户自画多边形, 按面积比例在多边形中自动生成样点,导出多边形和样点shp,以及删除上一组多边形和样点(有视频效果展示)
  • 【Webpack配置全解析】打造你的专属构建流程️(4)
  • 算法每日双题精讲——滑动窗口(长度最小的子数组,无重复字符的最长子串)
  • Java(基本数据类型)( ̄︶ ̄)↗
  • 实用的云手机软件有哪些?高性价比云手机推荐
  • 【数组】复习与企业真题
  • YOLOv9改进策略【损失函数篇】| Varifocal Loss,解决密集目标检测器训练中前景和背景类别间极端不平衡的问题
  • JavaScript类型判断(总结)
  • 关于宿主机功能正常docker容器重启后dns失效的解决办法
  • 大语言模型之LlaMA系列- LlaMA 2及LLaMA2_chat(上)
  • android13 系统默认设置静态IP
  • 二叉搜索树(来学包会) C++经验+1
  • GEE 案例:一种在不受云层影响并利用合成口径雷达(SAR)数据的情况下监测植被的方法(双极化SAR植被指数)
  • 【Python】的语言基础学习方法 快速掌握! 源码可分享!
  • 怎么批量制作文本或链接静态码?批量静态码在线的生成技巧
  • 【Webpack】Hash 码
  • 【环境踩坑系列】centos7安装python3.10.X
  • Mybatis-动态SQL
  • Skyeye 云这几年的经历
  • three.js BufferAttribute
  • 计算机网络(九) —— Tcp协议详解
  • python-比较月亮大小/数组下标/人见人爱a+b
  • Webpack 5的新特性:Asset Modules与Dynamic Import