HarmonyOS开发案例:【搭建关系型数据库】(4)

本节将介绍如何调用关系型数据库接口在本地搭建数据库,并读写相应的用户数据。

创建数据库

要使用关系型数据库存储用户数据,首先要进行数据库的创建,并提供基本的增、删、查、改接口。

导入关系型数据库模块:

import data_rdb from '@ohos.data.rdb';

开发前请熟悉鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。
关系型数据库提供以下两个基本功能:

获取RdbStore

首先要获取一个RdbStore来操作关系型数据库,代码如下:

// RdbHelperImp.ets
getRdb(context: Context): Promise<RdbHelper> {
  this.storeConfig = {
    name: this.mDatabaseName, securityLevel: dataRdb.SecurityLevel.S1
  };
  return new Promise<RdbHelper>((success, error) => {
    dataRdb.getRdbStore(context, this.storeConfig).then(dbStore => {
      this.rdbStore = dbStore;
      success(this);
    }).catch((err: Error) => {
      Logger.error(`initRdb err : ${JSON.stringify(err)}`);
      error(err);
    })
  })
}
封装增、删、改、查接口

关系型数据库接口提供的增、删、改、查操作均有callback和Promise两种异步回调方式,本Codelab使用了callback异步回调,其中插入数据使用了insert()接口,实现代码如下:

// RdbHelperImp.ets
insert(tableName: string, values: dataRdb.ValuesBucket | Array<dataRdb.ValuesBucket>): Promise<number> {
  return new Promise<number>((success, error) => {
    Logger.info(`insert tableName : ${tableName}, values : ${JSON.stringify(values)}`);
    if (!values) {
      Logger.info(`insert failed, values is undefined`);
      error(0);
      return;
    }
    if (values instanceof Array) {
      Logger.info(`insert values isArray = ${values.length}`);
      this.rdbStore.beginTransaction();
      this.saveArray(tableName, values).then(data => {
        Logger.info(`insert success, data : ${JSON.stringify(data)}`);
        success(data);
        this.rdbStore.commit();
      }).catch((err: Error) => {
        Logger.error(`insert failed, err : ${err}`);
        error(err);
        this.rdbStore.commit();
      })
    } else {
      this.rdbStore.insert(tableName, values).then(data => {
        Logger.info(`insert success id : ${data}`);
        success(data);
        his.rdbStore.commit();
      }).catch((err: Error) => {
        Logger.error(`insert failed, err : ${JSON.stringify(err)}`);
        error(err);
        this.rdbStore.commit();
      })
    }
  })
}

删除数据使用了delete()接口,实现代码如下:

// RdbHelperImp.ets
delete(rdbPredicates: dataRdb.RdbPredicates): Promise<number> {
  Logger.info(`delete rdbPredicates : ${JSON.stringify(rdbPredicates)}`);
  return this.rdbStore.delete(rdbPredicates);
}

更新数据使用了update()接口,实现代码如下:

// RdbHelperImp.ets
update(values: dataRdb.ValuesBucket, rdbPredicates: dataRdb.RdbPredicates): Promise<number> {
  return this.rdbStore.update(values, rdbPredicates);
}

查找数据使用了query()接口,实现代码如下:

// RdbHelperImp.ets
query(rdbPredicates: dataRdb.RdbPredicates, columns?: Array<string>): Promise<dataRdb.ResultSet> {
  Logger.info(`query rdbPredicates : ${JSON.stringify(rdbPredicates)}`);
  return this.rdbStore.query(rdbPredicates, columns);
}

数据库表结构

根据健康生活APP的使用场景和业务逻辑,定义了三个数据对象,并使用三张数据表来存储,分别是健康任务信息表、每日信息表和全局信息表。

健康任务信息表

目前健康生活应用提供了6个基本的健康任务,分别是早起、喝水、吃苹果、每日微笑、睡前刷牙和早睡。用户可以选择开启或关闭某个任务,开启的任务可以选择是否开启提醒,在指定的时间段内提醒用户进行打卡。任务也可以选择开启的频率,如只在周一到周五开启等。需要记录每项任务的目标值和实际完成值,在用户打卡后判断任务是否已经完成,并记录在数据库中。因此,需要创建一张存储每天的健康任务信息的表,表头如下:

每日信息表

在主页面,用户可以查看当天健康任务的完成进度,需要创建一张表记录当天开启的任务个数和已经完成的任务个数,表头如下:

全局信息表

用户连续多日打卡完成所有创建的任务可以获得相应的成就,因此,需要有一张表记录连续打卡天数和已达成的成就项。另外,考虑应用多日未打开的情况,需要记录应用第一次打开的日期和最后一次打开的日期以向数据库回填数据,表头如下:

创建数据表

根据6.2中设计的表结构,创建对应的数据表,实现对相应数据的读写操作。

健康任务信息数据表

在获取RdbStore后,需要使用executeSql接口执行SQL语句来创建相应的表结构和初始化数据,SQL语句如下:

CREATE TABLE IF NOT EXISTS taskInfo(
  id INTEGER PRIMARY KEY AUTOINCREMENT, 
  date TEXT NOT NULL, 
  taskID INTEGER, 
  targetValue TEXT NOT NULL, 
  isAlarm BOOLEAN, 
  startTime TEXT NOT NULL, 
  endTime TEXT NOT NULL, 
  frequency TEXT NOT NULL, 
  isDone BOOLEAN, 
  finValue TEXT NOT NULL, 
  isOpen BOOLEAN
)

健康任务信息数据表需要提供插入数据的接口,以在用户当天第一次打开应用时创建当天的健康任务信息,实现代码如下:

// TaskInfoApi.ets
insertData(taskInfo: TaskInfo, callback: Function): void {
  // 根据输入数据创建待插入的数据行
  const valueBucket = generateBucket(taskInfo);
  RdbUtils.insert('taskInfo', valueBucket).then(result => {
    callback(result);
  });
  Logger.info('TaskInfoTable', `Insert taskInfo {${taskInfo.date}:${taskInfo.taskID}} finished.`);
}

其中generateBucket()代码如下:

// TaskInfoApi.ets
function generateBucket(taskInfo: TaskInfo): dataRdb.ValuesBucket {
  let valueBucket = {} as dataRdb.ValuesBucket;
  Const.TASK_INFO.columns?.forEach((item: string) => {
    if (item !== 'id') {
      switch (item) {
        case 'date':
          valueBucket[item] = taskInfo.date;
          break;
        case 'taskID':
          valueBucket[item] = taskInfo.taskID;
          break;
        case 'targetValue':
          valueBucket[item] = taskInfo.targetValue;
          break;
        case 'isAlarm':
          valueBucket[item] = taskInfo.isAlarm;
          break;
        case 'startTime':
          valueBucket[item] = taskInfo.startTime;
          break;
        case 'endTime':
          valueBucket[item] = taskInfo.endTime;
          break;
        case 'frequency':
          valueBucket[item] = taskInfo.frequency;
          break;
        case 'isDone':
          valueBucket[item] = taskInfo.isDone;
          break;
        case 'finValue':
          valueBucket[item] = taskInfo.finValue;
          break;
        case 'isOpen':
          valueBucket[item] = taskInfo.isOpen;
          break;
        default:
          break;
      }
    }
  });
  return valueBucket;
}

用户开启和关闭任务,改变任务的目标值、提醒时间、频率等,用户打卡后修改任务的实际完成值都是通过更新数据接口来实现的,代码如下:

// TaskInfoApi.ets
updateDataByDate(taskInfo: TaskInfo, callback: Function): void {
  const valueBucket = generateBucket(taskInfo);
  let tableName = Const.TASK_INFO.tableName;
  if (!tableName) {
    return;
  }
  let predicates = new dataRdb.RdbPredicates(tableName);
  // 根据date和taskID匹配要更新的数据行
  predicates.equalTo('date', taskInfo.date).and().equalTo('taskID', taskInfo.taskID);
  RdbUtils.update(valueBucket, predicates).then((result: number) => {
    callback(result);
  });
  Logger.info('TaskInfoTable', `Update data {${taskInfo.date}:${taskInfo.taskID}} finished.`);
}

用户可以查看当天和以前某日的健康任务信息,需要提供查找数据接口,实现代码如下:

// TaskInfoApi.ets
query(date: string, isOpen: boolean = true, callback: Function): void {
  let tableName = Const.TASK_INFO.tableName;
  if (!tableName) {
    return;
  }
  let predicates = new dataRdb.RdbPredicates(tableName);
  predicates.equalTo('date', date);
  // 如果isOpen为true,则只查找开启的任务 
  if (isOpen) {
    predicates.equalTo('isOpen', true);
  }
  predicates.orderByAsc('taskID');  // 查找结果按taskID排序
  RdbUtils.query(predicates).then(resultSet => {
    let count = resultSet.rowCount;
    // 查找结果为空则返回空数组,否则返回查找结果数组
    if (count === 0 || typeof count === 'string') {
      Logger.error('TaskInfoTable', `${date} query no results!`);
      const result: TaskInfo[] = [];
      callback(result);
    } else {
      resultSet.goToFirstRow();
      const result: TaskInfo[] = [];
      for (let i = 0; i < count; i++) {
        let tmp = new TaskInfo(0, '', 0, '', false, '', '', '', false, '');
        tmp.isOpen = resultSet.getDouble(resultSet.getColumnIndex('isOpen')) ? true : false;
        tmp.id = resultSet.getDouble(resultSet.getColumnIndex('id'));
        tmp.date = resultSet.getString(resultSet.getColumnIndex('date'));
        tmp.taskID = resultSet.getDouble(resultSet.getColumnIndex('taskID'));
        tmp.targetValue = resultSet.getString(resultSet.getColumnIndex('targetValue'));
        tmp.isAlarm = resultSet.getDouble(resultSet.getColumnIndex('isAlarm')) ? true : false;
        tmp.startTime = resultSet.getString(resultSet.getColumnIndex('startTime'));
        tmp.endTime = resultSet.getString(resultSet.getColumnIndex('endTime'));
        tmp.frequency = resultSet.getString(resultSet.getColumnIndex('frequency'));
        tmp.isDone = resultSet.getDouble(resultSet.getColumnIndex('isDone')) ? true : false;
        tmp.finValue = resultSet.getString(resultSet.getColumnIndex('finValue'));
        result[i] = tmp;
        resultSet.goToNextRow();
      }
      callback(result);
    }
  });
}
每日信息数据表

创建每日信息数据表的SQL语句如下:

CREATE TABLE IF NOT EXISTS dayInfo(
  date TEXT NOT NULL PRIMARY KEY, 
  targetTaskNum INTEGER, 
  finTaskNum INTEGER
)

在当天第一次打开应用时需要初始化每日信息数据,页面需要根据用户编辑任务和打卡的情况来更新当天目标任务个数和完成任务个数,所以需要提供插入数据和更新数据的接口,写法与上一条中相应接口类似,不再赘述。

页面需要查找对应日期的目标任务个数和完成任务个数用以在页面显示任务进度,因此需要查找数据的接口。且页面在打开时需要显示当周每天任务的完成情况,因此需要允许一次调用查找一周的每日任务信息。实现代码如下:

// DayInfoApi.ets
queryList(dates: string[], callback: Function): void {
  let predicates: dataRdb.RdbPredicates = new dataRdb.RdbPredicates(Const.DAY_INFO.tableName ? Const.DAY_INFO.tableName : '');
  predicates.in('date', dates);  // 匹配日期数组内的所有日期
  RdbUtils.query(predicates).then(resultSet => {
    let count = resultSet.rowCount;
    if (count === 0) {
      Logger.info('DayInfoTable', 'query no results.');
      let result: DayInfo[] = [];
      callback(result);
    } else {
      resultSet.goToFirstRow();
      let result: DayInfo[] = [];
      for (let i = 0; i < count; i++) {
        let tmp = new DayInfo('', 0, 0);
        tmp.date = resultSet.getString(resultSet.getColumnIndex('date'));
        tmp.targetTaskNum = resultSet.getDouble(resultSet.getColumnIndex('targetTaskNum'));
        tmp.finTaskNum = resultSet.getDouble(resultSet.getColumnIndex('finTaskNum'));
        result[i] = tmp;
        resultSet.goToNextRow();
      }
      callback(result);
    }
  });
}
全局信息数据表

创建全局信息数据表的SQL语句如下:

CREATE TABLE IF NOT EXISTS globalInfo(
  id INTEGER PRIMARY KEY, 
  firstDate TEXT NOT NULL, 
  lastDate TEXT NOT NULL, 
  checkInDays INTEGER, achievements TEXT NOT NULL
)

全局信息数据表同样需要提供插入数据、更新数据和查找数据的接口,写法与本节前两条中相应接口类似,不再赘述。
HarmonyOS与OpenHarmony鸿蒙文档籽料:mau123789是v直接拿

搜狗高速浏览器截图20240326151450.png

数据库初始化

应用首次打开时,数据库中没有数据,要做数据库的初始化,写入一组空数据。另外,如果用户连续几天没有打开APP,再次打开时需要将数据回写至数据库。因此需要实现一个数据库接口,在应用打开时调用,进行上述操作。代码如下:

// DatabaseModel.ets
query(date: string, callback: Function) {
  let result: TaskInfo[] = [];
  let self = this;
  GlobalInfoApi.query((globalResult: GlobalInfo) => {
    if (!globalResult.firstDate) { // 如果找不到全局信息,则写入
      let globalInfo: GlobalInfo = new GlobalInfo(date, date, 0, '');
      GlobalInfoApi.insertData(globalInfo, (isDone: number) => {
        if (isDone) {
          Logger.info('AppStart', 'Insert globalInfo success: ' + JSON.stringify(globalInfo));
        }
      });
      self.insertGlobalTask();
      let dayInfo: DayInfo = new DayInfo(date, 0, 0);
      DayInfoApi.insertData(dayInfo, (isDone: number) => {
        if (isDone) {
          Logger.info('AppStart', 'Insert dayInfo success: ' + JSON.stringify(dayInfo));
        }
      })
      self.insertTask(date);
      callback(result, dayInfo);
    } else { // 如果找到全局信息,则查询当天的任务信息
      let newGlobalInfo = globalResult;
      let preDate = globalResult.lastDate;
      newGlobalInfo.lastDate = date;
      GlobalInfoApi.updateData(newGlobalInfo, (isDone: number) => {
        if (isDone) {
          Logger.info('AppStart', 'update globalInfo success: ' + JSON.stringify(newGlobalInfo));
        }
      });
      self.queryPreInfo(date, preDate, result, callback);
    }
  });
}
  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
关系型数据库是一种基于关系模型的数据库管理系统。关系模型使用表格来组织和表示数据,表格由行和列组成,每一行表示一个实体,每一列表示一个属性。关系型数据库采用结构化的方式存储和管理数据,具有数据一致性、数据完整性和数据安全性等特征。 在关系型数据库中,数据之间的关系由关系代数和关系演算来描述和操作。关系代数提供了一组操作符,如选择、投影、连接和除等,用于查询和更新数据;关系演算为描述数据之间的关系提供了一种基于数学逻辑的表达方式。 关系型数据库的特点包括:数据的结构性,即数据以表格形式存储,数据排列有序;数据的一致性,即数据必须满足特定的完整性约束;数据的持久性,即数据在数据库中持久保存,并通过事务保证数据的可靠性和一致性。 常见的关系型数据库有MySQL、Oracle、SQL Server、DB2等。这些数据库管理系统提供了丰富的功能和工具,如数据定义语言(DDL)、数据操纵语言(DML)、事务管理、数据备份与恢复等,以满足不同应用场景下的数据管理需求。 关系型数据库在企业和个人应用中被广泛使用,常用于管理和处理结构化数据。它能够提供高效的数据查询和更新操作,支持复杂的数据分析和关联查询,保证数据的一致性和完整性,为应用程序提供可靠的数据存储和管理功能。同时,关系型数据库也面临着一些挑战,如性能瓶颈、数据规模限制和复杂查询的优化等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值