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

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

时间:2024-10-18 15:21:06浏览次数:8  
标签:ets MVVM 数据库 stu let Student 上手 数据

作者:狼哥
团队:坚果派
团队介绍:坚果派由坚果等人创建,团队拥有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 Params

  return 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,delete
    extraData: []
  } as Params
  return 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)
    // 初始化SDK
    let 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及以上。

标签:ets,MVVM,数据库,stu,let,Student,上手,数据
From: https://www.cnblogs.com/army16/p/18474368

相关文章

  • ruoyi框架动态切换数据库
       需求背景最近需要一个小demo,项目中需要同时连接sqlserver和mysql数据库。操作教程1、pom.xml--修改common/pom.xml<!--动态数据源--><dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <v......
  • Sql Server数据库监听 c#代码
    usingAnfiniL.SqlServerTools.Data;usingSqlServerTools;usingSqlServerTools.Data;usingSystem;usingSystem.Collections.Generic;usingSystem.Data;usingSystem.Linq;usingSystem.Net;usingSystem.Text;namespacewindowsApiAcitonSimulation.Help{publiccla......
  • 轻松上手-图片压缩秘籍
    作者:狼哥团队:坚果派团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,目前已开发鸿蒙原......
  • 轻松上手-骨架屏后动画显示
    作者:狼哥团队:坚果派团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,目前已开发鸿蒙原......
  • 轻松上手-识图文字朗读
    作者:狼哥团队:坚果派团队介绍:坚果派由坚果等人创建,团队拥有12个华为HDE带领热爱HarmonyOS/OpenHarmony的开发者,以及若干其他领域的三十余位万粉博主运营。专注于分享HarmonyOS/OpenHarmony、ArkUI-X、元服务、仓颉。团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,目前已开发鸿蒙原......
  • sql server 数据库收缩
    原文链接:https://www.cnblogs.com/lgx5/p/17080302.html1.数据库的相关属性在MS中创建数据库时会为数据库分配初始的大小(如下图:数据库和日志两个文件),随着数据库的使用文件会逐渐增大。数据库文件大小的增加有两种方式:自动增长:在自动增长中可以设置每次的增长量,以及最大增长的......
  • 高效数据移动指南 | 如何快速实现数据库 Oracle 到 Apache Doris 的数据同步?
    在现代企业中,数据无处不在,贯穿于各个业务环节和系统之间。无论是跨系统的数据集成、多地域的数据协同,还是实时应用的数据同步,数据的一致性和及时性都至关重要。在数字化转型的过程中,如何确保不同系统、地域、设备之间的数据同步,成为了企业面临的重要挑战。本专题将基于实践经验,从......
  • 数据库中时间戳求差值
    在MySQL中,如果你有两个`TIMESTAMP`或`DATETIME`类型的数据,并希望计算它们之间的差值,可以使用几种方法来实现。以下是几种常见的方法: ###方法1:使用`TIMEDIFF()`函数`TIMEDIFF()`函数返回两个时间点之间的差异,结果是一个时间间隔(例如:`00:01:23`表示1分钟23秒)。......
  • 开源免费数据库监控工具,MySQL、Oracle、Postgres或MSSQL
    在信息系统项目中,UI、业务逻辑、数据库操作、文件操作、网络、API调用等许多环节都有可能产生性能问题,其中,数据库读写是最为常见的操作,我们也发现其实许多项目中的大部分瓶颈多来自于数据库操作。因此,在做性能优化的过程中,我们总是特别关注持久层的代码、数据库的设计和数据库的物......
  • 【软件资料】数据库设计规范,数据库设计说明书,数据库设计规范,数据库文档(Word资料下载)
     1编写目的2数据库策略2.1数据库对象长度策略2.2数据完整性策略2.3规范化设计与性能之间的权衡策略2.4字段类型的定义与使用策略3命名规范3.1数据库命名规则3.2数据库对象命名的一般原则3.3表空间(Tablespace)命名规则3.4表(Table)命名规则3.5字段命......