轻松上手-MVVM模式_关系型数据库_云函数T云数据库

news/2024/10/18 15:32:39/文章来源:https://www.cnblogs.com/army16/p/18474368
作者:狼哥
团队:坚果派
团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,目前已开发鸿蒙原生应用,三方库60+,欢迎交流。

注意

当前API12的端云一体化开发工程仅支持手动签名。

简介

此案例是一个简单的学生信息管理系统,使用到MVVM模式开发、本地使用关系型数据库存储数据、远程使用云数据库存储数据,并用云函数调用云数据库,应用启动时通过云函数调用云数据库,把云数据库数据更新到关系型数据库,应用切换到后台时,把本地关系型数据库数据,通过调用云函数T云数据库,把本地数据上传到云数据库。

知识点

  1. MVVM模式
  2. 关系型数据库
  3. 云函数T云数据库
  4. 案例讲解

1. MVVM模式

应用通过状态去渲染更新UI是程序设计中相对复杂,但又十分重要的,往往决定了应用程序的性能。程序的状态数据通常包含了数组、对象,或者是嵌套对象组合而成。在这些情况下,ArkUI采取MVVM = Model + View + ViewModel模式,其中状态管理模块起到的就是ViewModel的作用,将数据与视图绑定在一起,更新数据的时候直接更新视图。

  • Model层:存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理。
  • View层:在ArkUI中通常是@Component装饰组件渲染的UI。
  • ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量、LocalStorage和AppStorage中的数据。
    • 自定义组件通过执行其build()方法或者@Builder装饰的方法来渲染UI,即ViewModel可以渲染View。
    • View可以通过相应event handler来改变ViewModel,即事件驱动ViewModel的改变,另外ViewModel提供了@Watch回调方法用于监听状态数据的改变。
    • 在ViewModel被改变时,需要同步回Model层,这样才能保证ViewModel和Model的一致性,即应用自身数据的一致性。
    • ViewModel结构设计应始终为了适配自定义组件的构建和更新,这也是将Model和ViewModel分开的原因。

2. 关系型数据库

关系型数据库(Relational Database,RDB)是一种基于关系模型来管理数据的数据库。关系型数据库基于SQLite组件提供了一套完整的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。支持通过ResultSet.getSendableRow方法获取Sendable数据,进行跨线程传递。

为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。

大数据量场景下查询数据可能会导致耗时长甚至应用卡死,建议如下:

  • 单次查询数据量不超过5000条。
  • 在TaskPool中查询。
  • 拼接SQL语句尽量简洁。
  • 合理地分批次查询。

该模块提供以下关系型数据库相关的常用功能:

  • RdbPredicates: 数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
  • RdbStore:提供管理关系数据库(RDB)方法的接口。
  • ResultSet:提供用户调用关系型数据库查询接口之后返回的结果集合。

3. 云函数T云数据库

官方文档提供了云函数的Demo包,您只需要简单配置后,在创建云函数时将其上传,就可以快速在应用中通过云函数进行数据库添加,删除和修改的操作。

Demo包的结构如下图所示。

img

开发者只需简单的配置并上传函数后就可以通过指令进行数据库各种查询操作。

  • CloudDBZoneWrapper.js:用于配置数据库相关信息和对数据库操作的逻辑。
  • resource:存放认证凭证json文件和一些资源文件。

完整定制流程如下图所示。

4. 案例讲解

4.1 效果图
4.2 代码结构
├──AppScope/resources                         
│  └──rawfile
│     └──agconnect-services.json              // 认证凭证
├──entry/src/main/ets                         // 代码区
│  ├──common
│  │  └──sql.ets                      		  // sql常量
│  ├──db
│  │  ├──cloud
│  │  │  └──StudentCloud.ets                  // 调用云函数操作
│  │  └──rdb
│  │     ├──RDB.ets                  		  // 关系型数据库获取
│  │     └──StudentDAO.ets                    // 调用关系型数据库操作
│  ├──entryability
│  │  └──EntryAbility.ets 
│  ├──model
│  │  └──Student.ets                          // 学生实例类
│  └──pages
│  │  └──Index.ets                            // 首页
│  └──view
│  │  ├──ListContent.ets                      // 列表内容项
│  │  └──ListTitle.ets                        // 列表标题项
│  └──viewmodel
│     ├──AddIcon.ets                      	  // 新增学生信息按钮
│     ├──Content.ets                      	  // 显示学生信息列表
│     ├──DialogContent.ets                    // 新增或编辑学生信息对话框
│     └──Header.ets                        	  // 页面标题
└──entry/src/main/resources                   // 应用资源目录
4.3 首页布局
Stack({alignContent: Alignment.BottomStart}) {Column() {// 标题Header({ title: "学生信息" })// 学生信息列表Content({data: this.data, updck: (stu: Student) => {}, delck: (stu: Student) => {}})}.height('100%').width('100%')// 增加按钮AddIcon({okck: () => {}})}.width('100%').height('100%')
4.4 页面内容列表
@Component
export struct Content {@Link data: Student[];updck: (stu: Student) => void = () => {}delck: (stu: Student) => void = () => {}build() {List() {ListTitle()ForEach(this.data, (item: Student) => {ListContent(item, this.updck, this.delck)}, (item: Student) => JSON.stringify(item))}.width('100%').layoutWeight(1).divider({strokeWidth: 1, color: '#ffecebeb'})}
}
@Builder
export function ListTitle() {Row() {Text('姓名').width('20%').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)Text('年龄').width('15%').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)Text('学校').width('30%').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)Text('操作').width('35%').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White).textAlign(TextAlign.Center)}.width('100%').height(50).padding(5).backgroundColor('rgba(0,0,0,0.4)').justifyContent(FlexAlign.SpaceBetween)
}
@Builder
export function ListContent(stu: Student, updck:(stu: Student)=>void, delck:(stu:Student)=>void) {Row() {Text(stu.name).fontSize(20).width('20%')Text(stu.age+'').fontSize(20).width('15%')Text(stu.school).fontSize(20).width('30%')Row() {Text('更新').padding(10).borderRadius(5).fontColor(Color.White).backgroundColor(Color.Blue).onClick(() => {updck(stu)})Text('删除').padding(10).borderRadius(5).fontColor(Color.White).backgroundColor(Color.Red).onClick(() => {delck(stu)})}.width('35%').justifyContent(FlexAlign.SpaceEvenly)}.width('100%').padding(5).justifyContent(FlexAlign.SpaceBetween)
}
4.5 RDB关系型数据库初始化
import { relationalStore } from '@kit.ArkData'const config: relationalStore.StoreConfig = {name: 'stu.db',securityLevel: relationalStore.SecurityLevel.S2
}
export async function getRdb():Promise<relationalStore.RdbStore> {return relationalStore.getRdbStore(globalThis.context, config);
}
4.6 StudentDAO关系型数据库操作
// 查询所有学生信息
export async function findAllStudents(): Promise<Student[]> {let store = await getRdb();let query = new relationalStore.RdbPredicates(TABLE_NAME);let rs = await store.query(query);let students = new Array<Student>();while (rs.goToNextRow()) {let stu = new Student(rs.getLong(rs.getColumnIndex("id")), rs.getString(rs.getColumnIndex("name")), rs.getLong(rs.getColumnIndex("age")), rs.getString(rs.getColumnIndex("school")));students.push(stu)}return students;
}
// 编辑学生信息
export async function updateStudent(stu: Student): Promise<number> {let store = await getRdb();let query = new relationalStore.RdbPredicates(TABLE_NAME);query.equalTo("id", stu.id);let values: relationalStore.ValuesBucket = {"name": stu.name,"age": stu.age,"school": stu.school}return store.update(values, query);
}
// 删除学生信息
export async function deleteStudent(stu: Student): Promise<number> {let store = await getRdb();let query = new relationalStore.RdbPredicates(TABLE_NAME);query.equalTo("id", stu.id);return store.delete(query);
}
// 新增学生信息
export async function addStudent(stu: Student): Promise<number> {let store = await getRdb();let values: relationalStore.ValuesBucket = {"name": stu.name,"age": stu.age,"school": stu.school}console.debug(TAG, `xx 插入到数据库内容:${JSON.stringify(values)}`)return store.insert(TABLE_NAME, values);
}
4.7 StudentCloud云函数操作
/*** 上传学生信息到云数据库* @returns*/
export async function uploadStudents(): Promise<number> {console.debug(TAG, '开始上传本地数据到云数据库')let students: Student[] = await findAllStudents()console.debug(TAG, `上传数据到云数据库条数:${JSON.stringify(students.length)}`)//@TODO 调用云函数上传全部学生信息let params: Params = {action: "upsert",extraData: students} as Paramsreturn new Promise<number>((resolve, reject) => {cloudFunction.call({name: 'upload-students', data: params}).then(async (result) => {console.debug(TAG, `上传本地数据到云数据库结果:${JSON.stringify(result)}`)resolve(1)}).catch(async (err: BusinessError) => {console.debug(TAG, `上传本地数据到云数据库异常:${JSON.stringify(err)}`)reject(0)})})
}/*** 同步云数据库学生信息到本地* @returns*/
export async function downloadStudents(): Promise<number> {console.debug(TAG, '开始下载云数据库学生信息到本地')//@TODO 调用云函数下载全部学生信息let params: Params = {action: "all", // all,upsert,deleteextraData: []} as Paramsreturn new Promise<number>((resolve, reject) => {cloudFunction.call({name: 'upload-students', data: params}).then(async (res) => {let cloudStudents: Student[] = res.result as Student[];console.debug(TAG, `下载云数据库学生信息人数:${cloudStudents.length}`)return batchUpdateStudent(cloudStudents)}).catch(async (err: BusinessError) => {console.debug(TAG, `下载云数据库学生信息异常:${JSON.stringify(err)}`)reject(-1)})})
}
4.8 EntryAbility初始化关系型数据库与AGC
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {hilog.info(0x0000, TAG, '%{public}s', 'Ability onCreate');globalThis.context = this.context;// 初始化数据库和表let store:relationalStore.RdbStore = await getRdb();store.executeSync(CREATE_TABLE)// 初始化SDKlet input = await this.context.resourceManager.getRawFileContent('agconnect-services.json')let jsonString  = util.TextDecoder.create('utf-8', {ignoreBOM: true}).decodeWithStream(input, {stream: false});initialize(this.context, JSON.parse(jsonString));}
4.9 同步与下载数据

在onCreate回调函数同步云数据库数据到本地关系型数据库

    // 同步云数据库数据到本地数据库downloadStudents().then((num: number) => {hilog.info(0x0000, TAG, '%{public}s', `同步云数据库数据到本地数据库结果是:${num}`);}).catch((err: BusinessError) => {hilog.info(0x0000, TAG, '%{public}s', `同步云数据库数据到本地数据库异常:${JSON.stringify(err)}`);})

在onBackground回调函数上传本地关系型数据库数据到云数据库

// 上传本地数据到云数据库uploadStudents().then((num: number) => {hilog.info(0x0000, TAG, '%{public}s', `上传数据到云数据库结果是:${num}`);}).catch((err: BusinessError) => {hilog.info(0x0000, TAG, '%{public}s', `上传数据到云数据库异常:${JSON.stringify(err)}`);})

总结

通过学习此案例,学会使用MVVM模式开发,学会关系型数据库开发,学会调用云函数同步本地数据到云数据库与下载云数据库数据到本地数据库,在MVVM模式开发里,学到如何在@Builder注解function组件里传参事件,关于云函数调用云数据库操作,可以参考另一篇文章 轻松上手-Serverless模板-云存储-云数据库-云函数

约束与限制

1.本示例仅支持标准系统上运行,支持设备:华为手机。

2.HarmonyOS系统:HarmonyOS NEXT Developer Beta1及以上。

3.DevEco Studio版本:DevEco Studio NEXT Developer Beta1及以上。

4.HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta1 SDK及以上。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

九宫格自由流转拼图游戏

作者:狼哥 团队:坚果派 团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁…

数据预处理-DataFrame切片

此Blog仅作为日常学习工作中记录使用,Blog中有不足之处欢迎指出 以kaggle中房屋预测的训练集为例,说明DataFrame切片常用操作 一、读入数据 import numpy as np import pandas as pdfile_path = ***\kaggle_house_pred_train.csv data = pd.read_csv(file_path)data.columns …

轻松上手-Navigation路由 H5

作者:狼哥 团队:坚果派 团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁…

Audio

音频文件导入 常用格式wav mp3 aiff音频文件属性设置Force To Mono:多声道转单声道; Normalize:强制为单声道时,混合过程中被标准化 Load In Background:在后台加载时,不阻塞主线程 Ambisonic:立体混响声,非常适合360度视频和XR应用程序,如果音频文件包含立体混响声编…

RenderTexture

基础概念 RenderTexture 在Unity中,RenderTexture是一种纹理,它可以在运行时动态地生成图像数据,并且可以作为着色器的输入或者显示在游戏世界的任何材质上。简单来说,RenderTexture让你能够将场景或特定相机的渲染结果捕获到一个纹理中,然后这个纹理可以被用作其他渲染过…

九点共圆及其圆心证明(证明过程已更新)

九点共圆及其圆心证明主要思路:固定 \(P,L\),证明其它七个点均在以 \(PL\) 为直径的圆上。 条件的来源会备注在括号内。背景可能影响观感,建议打开极简模式阅读。这是一个三角形 \(\triangle ABC\),设 \(BC,AC,AB\) 边上垂足分别为 \(D,E,F\),其边上中点分别为 \(L,M,N\),…

【喜讯】全球电商大数据平台推出F类免费API接口服务!

好消息,全球电商大数据平台再次上线多个类别接口,首次推出F类免费API接口服务,为所有追求数据赋能、寻求创新突破的企业和个人,带来了前所未有的机遇。好消息,值此之际;全球电商大数据平台再次上线多个类别接口,首次推出F类免费API接口服务,为所有追求数据赋能、寻求创…

c# winform在线升级clickonce

说明:在线升级前提 1,一个可以访问在线的地址,2,发布前要在项目属性发布里配置好相关设置 一,可以在IIS上布署一个可以访问的地址 二,发布前配置

SQLSEVER 实现货币数字转中文汉字

SQLSEVER 实现数字转换成中文(货币) -- ============================================= -- Author: LearnerPing -- Create date: 2024/10/18 -- Description: Change Number to Chinese -- ============================================= Create FUNCTION GetNumberToChi…

javascript渲染OFD的库

目前使用javascript开发的OFD的渲染库主要有两个: ofd.js和liteofd,其中ofd.js开发比较早,liteofd是最近刚出现的js库。首先结论是ofd.js渲染效果没有liteofd好,因为ofd.js目前有一些效果没有支持,比如对字体没有比较好的解析和支持。 liteofd相对ofd.js效果更好,并且提供…

高等数学 6.1 定积分的元素法

在定积分的应用中,经常采用所谓的元素法。为了说明这种方法,先回顾一下曲边梯形的面积问题。 设 \(f(x)\) 在区间 \([a, b]\) 上连续且 \(f(x) \geqslant 0\) ,求以曲线 \(y = f(x)\) 为曲边、底为 \([a, b]\) 的曲边梯形的面积 \(A\) 。把这个面积 \(A\) 表示为定积分 \[A …

详解 JuiceFS 在多云架构下的数据同步与一致性

随着大模型流行,GPU 算力资源正变得日益稀缺,传统的“算力跟着存储跑”的策略需要转变为“存储跟着算力跑”。为了确保数据一致性和管理的便捷性,企业通常在特定地区的公有云上选择对象存储作为所有模型数据的集中存储点。当进行计算任务调度时,往往需要人工介入,手动进行…