0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

HarmonyOS開發(fā)案例:【搭建關系型數(shù)據(jù)庫】(4)

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-05-11 10:27 ? 次閱讀

本節(jié)將介紹如何調用關系型數(shù)據(jù)庫接口在本地搭建數(shù)據(jù)庫,并讀寫相應的用戶數(shù)據(jù)。

創(chuàng)建數(shù)據(jù)庫

要使用關系型數(shù)據(jù)庫存儲用戶數(shù)據(jù),首先要進行數(shù)據(jù)庫的創(chuàng)建,并提供基本的增、刪、查、改接口。

導入關系型數(shù)據(jù)庫模塊:

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

開發(fā)前請熟悉鴻蒙開發(fā)指導文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]
關系型數(shù)據(jù)庫提供以下兩個基本功能:

獲取RdbStore

首先要獲取一個RdbStore來操作關系型數(shù)據(jù)庫,代碼如下:

// 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);
    })
  })
}

封裝增、刪、改、查接口

關系型數(shù)據(jù)庫接口提供的增、刪、改、查操作均有callback和Promise兩種異步回調方式,本Codelab使用了callback異步回調,其中插入數(shù)據(jù)使用了insert()接口,實現(xiàn)代碼如下:

// 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();
      })
    }
  })
}

刪除數(shù)據(jù)使用了delete()接口,實現(xiàn)代碼如下:

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

更新數(shù)據(jù)使用了update()接口,實現(xiàn)代碼如下:

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

查找數(shù)據(jù)使用了query()接口,實現(xiàn)代碼如下:

// 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);
}

數(shù)據(jù)庫表結構

根據(jù)健康生活APP的使用場景和業(yè)務邏輯,定義了三個數(shù)據(jù)對象,并使用三張數(shù)據(jù)表來存儲,分別是健康任務信息表、每日信息表和全局信息表。

健康任務信息表

目前健康生活應用提供了6個基本的健康任務,分別是早起、喝水、吃蘋果、每日微笑、睡前刷牙和早睡。用戶可以選擇開啟或關閉某個任務,開啟的任務可以選擇是否開啟提醒,在指定的時間段內提醒用戶進行打卡。任務也可以選擇開啟的頻率,如只在周一到周五開啟等。需要記錄每項任務的目標值和實際完成值,在用戶打卡后判斷任務是否已經(jīng)完成,并記錄在數(shù)據(jù)庫中。因此,需要創(chuàng)建一張存儲每天的健康任務信息的表,表頭如下:

每日信息表

在主頁面,用戶可以查看當天健康任務的完成進度,需要創(chuàng)建一張表記錄當天開啟的任務個數(shù)和已經(jīng)完成的任務個數(shù),表頭如下:

全局信息表

用戶連續(xù)多日打卡完成所有創(chuàng)建的任務可以獲得相應的成就,因此,需要有一張表記錄連續(xù)打卡天數(shù)和已達成的成就項。另外,考慮應用多日未打開的情況,需要記錄應用第一次打開的日期和最后一次打開的日期以向數(shù)據(jù)庫回填數(shù)據(jù),表頭如下:

創(chuàng)建數(shù)據(jù)表

根據(jù)6.2中設計的表結構,創(chuàng)建對應的數(shù)據(jù)表,實現(xiàn)對相應數(shù)據(jù)的讀寫操作。

健康任務信息數(shù)據(jù)表

在獲取RdbStore后,需要使用executeSql接口執(zhí)行SQL語句來創(chuàng)建相應的表結構和初始化數(shù)據(jù),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
)

健康任務信息數(shù)據(jù)表需要提供插入數(shù)據(jù)的接口,以在用戶當天第一次打開應用時創(chuàng)建當天的健康任務信息,實現(xiàn)代碼如下:

// TaskInfoApi.ets
insertData(taskInfo: TaskInfo, callback: Function): void {
  // 根據(jù)輸入數(shù)據(jù)創(chuàng)建待插入的數(shù)據(jù)行
  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;
}

用戶開啟和關閉任務,改變任務的目標值、提醒時間、頻率等,用戶打卡后修改任務的實際完成值都是通過更新數(shù)據(jù)接口來實現(xiàn)的,代碼如下:

// 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);
  // 根據(jù)date和taskID匹配要更新的數(shù)據(jù)行
  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.`);
}

用戶可以查看當天和以前某日的健康任務信息,需要提供查找數(shù)據(jù)接口,實現(xiàn)代碼如下:

// 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;
    // 查找結果為空則返回空數(shù)組,否則返回查找結果數(shù)組
    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);
    }
  });
}

每日信息數(shù)據(jù)表

創(chuàng)建每日信息數(shù)據(jù)表的SQL語句如下:

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

在當天第一次打開應用時需要初始化每日信息數(shù)據(jù),頁面需要根據(jù)用戶編輯任務和打卡的情況來更新當天目標任務個數(shù)和完成任務個數(shù),所以需要提供插入數(shù)據(jù)和更新數(shù)據(jù)的接口,寫法與上一條中相應接口類似,不再贅述。

頁面需要查找對應日期的目標任務個數(shù)和完成任務個數(shù)用以在頁面顯示任務進度,因此需要查找數(shù)據(jù)的接口。且頁面在打開時需要顯示當周每天任務的完成情況,因此需要允許一次調用查找一周的每日任務信息。實現(xiàn)代碼如下:

// 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);  // 匹配日期數(shù)組內的所有日期
  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);
    }
  });
}

全局信息數(shù)據(jù)表

創(chuàng)建全局信息數(shù)據(jù)表的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
)

全局信息數(shù)據(jù)表同樣需要提供插入數(shù)據(jù)、更新數(shù)據(jù)和查找數(shù)據(jù)的接口,寫法與本節(jié)前兩條中相應接口類似,不再贅述。
HarmonyOSOpenHarmony鴻蒙文檔籽料:mau123789是v直接拿

搜狗高速瀏覽器截圖20240326151450.png

數(shù)據(jù)庫初始化

應用首次打開時,數(shù)據(jù)庫中沒有數(shù)據(jù),要做數(shù)據(jù)庫的初始化,寫入一組空數(shù)據(jù)。另外,如果用戶連續(xù)幾天沒有打開APP,再次打開時需要將數(shù)據(jù)回寫至數(shù)據(jù)庫。因此需要實現(xiàn)一個數(shù)據(jù)庫接口,在應用打開時調用,進行上述操作。代碼如下:

// 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);
    }
  });
}

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)庫

    關注

    7

    文章

    3879

    瀏覽量

    65524
  • 鴻蒙
    +關注

    關注

    57

    文章

    2467

    瀏覽量

    43614
  • HarmonyOS
    +關注

    關注

    79

    文章

    2005

    瀏覽量

    31768
收藏 0人收藏

    評論

    相關推薦

    HarmonyOS開發(fā)案例:【關系數(shù)據(jù)庫

    使用關系數(shù)據(jù)庫的相關接口實現(xiàn)了對賬單的增、刪、改、查操作。
    的頭像 發(fā)表于 04-22 14:58 ?848次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發(fā)案</b>例:【<b class='flag-5'>關系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>】

    關系數(shù)據(jù)庫與非關系數(shù)據(jù)庫的區(qū)別淺析

    關系數(shù)據(jù)庫的一個劣勢就是 阻抗失諧(impedance mismatch):關系模型和內存中的數(shù)據(jù)結構之間存在差異
    發(fā)表于 06-03 06:03

    HarmonyOS數(shù)據(jù)庫的相關資料下載

    、HarmonyOS數(shù)據(jù)庫篇之RDB關系數(shù)據(jù)庫3、HarmonyOS
    發(fā)表于 03-28 11:13

    HarmonyOS關系數(shù)據(jù)庫和對象關系數(shù)據(jù)庫的使用方法

    extends OrmDatabase { }2.表:被開發(fā)者用@Entity注解的實體類,且繼承了OrmObject的類,對應關系數(shù)據(jù)庫中的表。// 定義了一個實體類User.j
    發(fā)表于 03-29 14:10

    DCS仿真系統(tǒng)的內存-關系數(shù)據(jù)庫系統(tǒng)的構成

    對內存數(shù)據(jù)庫關系數(shù)據(jù)庫進行分析,設計完成內存—關系數(shù)據(jù)庫系統(tǒng),并在實際的DCS 仿真系統(tǒng)中進行使用。本文介紹了所
    發(fā)表于 09-07 15:39 ?15次下載

    什么是關系數(shù)據(jù)庫

    什么是關系數(shù)據(jù)庫 關系數(shù)據(jù)庫簡介   關系
    發(fā)表于 06-17 07:38 ?9160次閱讀

    什么是非關系數(shù)據(jù)庫

    什么是非關系數(shù)據(jù)庫 談到非關系數(shù)據(jù)庫設計的難點,朱海峰說:“我們可以從一些場景來看這個問題
    發(fā)表于 06-17 15:49 ?3193次閱讀

    hbase和關系數(shù)據(jù)庫的區(qū)別

    hbase和關系數(shù)據(jù)庫的區(qū)別就是對于傳統(tǒng)數(shù)據(jù)庫,增加列對于一個項目來講,改變是非常大的。但是對于nosql,插入列和刪除列,跟傳統(tǒng)數(shù)據(jù)庫
    發(fā)表于 12-27 15:51 ?1.2w次閱讀
    hbase和<b class='flag-5'>關系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>的區(qū)別

    數(shù)據(jù)庫設計開發(fā)案例教程之數(shù)據(jù)庫設計的資料介紹

    本文檔的主要內容詳細介紹的是數(shù)據(jù)庫設計開發(fā)案例教程之數(shù)據(jù)庫設計的資料介紹主要內容包括了:1 數(shù)據(jù)庫設計概述,2 需求分析,3 概念結構設計,4
    發(fā)表于 01-11 11:20 ?17次下載
    <b class='flag-5'>數(shù)據(jù)庫</b>設計<b class='flag-5'>開發(fā)案</b>例教程之<b class='flag-5'>數(shù)據(jù)庫</b>設計的資料介紹

    基于SQLite的鴻蒙的關系數(shù)據(jù)庫使用

    HarmonyOS關系數(shù)據(jù)庫基于SQLite組件提供了一套完整的對本地數(shù)據(jù)庫進行管理的機制,對外提供了一系列的增、刪、改、查接口,也可以直
    的頭像 發(fā)表于 01-20 11:48 ?4602次閱讀
    基于SQLite的鴻蒙的<b class='flag-5'>關系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>使用

    OpenHarmony關系數(shù)據(jù)庫概述

    關系數(shù)據(jù)庫(Relational Database, 以下簡稱RDB)是一種基于關系模型來管理數(shù)據(jù)數(shù)
    的頭像 發(fā)表于 03-28 18:08 ?1265次閱讀
    OpenHarmony<b class='flag-5'>關系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>概述

    關系數(shù)據(jù)庫的基本原理(什么是關系數(shù)據(jù)庫

    什么是關系數(shù)據(jù)庫?關系數(shù)據(jù)庫,簡稱 RDBMS是由許多數(shù)
    的頭像 發(fā)表于 07-10 09:06 ?1599次閱讀

    鴻蒙HarmonyOS開發(fā)實例:【分布式關系數(shù)據(jù)庫

    使用[@ohos.data.relationalStore]接口和[@ohos.distributedDeviceManager]?接口展示了在eTS中分布式關系數(shù)據(jù)庫的使用,在增、刪、改、查的基本操作外,還包括分布式
    的頭像 發(fā)表于 04-11 09:52 ?1269次閱讀
    鴻蒙<b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發(fā)</b>實例:【分布式<b class='flag-5'>關系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>】

    鴻蒙開發(fā)接口數(shù)據(jù)管理:【@ohos.data.rdb (關系數(shù)據(jù)庫)】

    關系數(shù)據(jù)庫(Relational Database,RDB)是一種基于關系模型來管理數(shù)據(jù)數(shù)據(jù)庫
    的頭像 發(fā)表于 06-10 18:35 ?1669次閱讀

    關系數(shù)據(jù)庫和非關系區(qū)別

    關系數(shù)據(jù)庫和非關系數(shù)據(jù)庫在多個方面存在顯著差異,主機推薦小編為您整理發(fā)布
    的頭像 發(fā)表于 01-10 09:58 ?415次閱讀

    電子發(fā)燒友

    中國電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術信息
    • 參加活動獲取豐厚的禮品