本節(jié)將介紹如何調(diào)用關(guān)系型數(shù)據(jù)庫接口在本地搭建數(shù)據(jù)庫,并讀寫相應(yīng)的用戶數(shù)據(jù)。
創(chuàng)建數(shù)據(jù)庫
要使用關(guān)系型數(shù)據(jù)庫存儲(chǔ)用戶數(shù)據(jù),首先要進(jìn)行數(shù)據(jù)庫的創(chuàng)建,并提供基本的增、刪、查、改接口。
導(dǎo)入關(guān)系型數(shù)據(jù)庫模塊:
import data_rdb from '@ohos.data.rdb';
開發(fā)前請熟悉鴻蒙開發(fā)指導(dǎo)文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
關(guān)系型數(shù)據(jù)庫提供以下兩個(gè)基本功能:
獲取RdbStore
首先要獲取一個(gè)RdbStore來操作關(guān)系型數(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);
})
})
}
封裝增、刪、改、查接口
關(guān)系型數(shù)據(jù)庫接口提供的增、刪、改、查操作均有callback和Promise兩種異步回調(diào)方式,本Codelab使用了callback異步回調(diào),其中插入數(shù)據(jù)使用了insert()接口,實(shí)現(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()接口,實(shí)現(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()接口,實(shí)現(xiàn)代碼如下:
// RdbHelperImp.ets
update(values: dataRdb.ValuesBucket, rdbPredicates: dataRdb.RdbPredicates): Promise< number > {
return this.rdbStore.update(values, rdbPredicates);
}
查找數(shù)據(jù)使用了query()接口,實(shí)現(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ù)庫表結(jié)構(gòu)
根據(jù)健康生活A(yù)PP的使用場景和業(yè)務(wù)邏輯,定義了三個(gè)數(shù)據(jù)對象,并使用三張數(shù)據(jù)表來存儲(chǔ),分別是健康任務(wù)信息表、每日信息表和全局信息表。
健康任務(wù)信息表
目前健康生活應(yīng)用提供了6個(gè)基本的健康任務(wù),分別是早起、喝水、吃蘋果、每日微笑、睡前刷牙和早睡。用戶可以選擇開啟或關(guān)閉某個(gè)任務(wù),開啟的任務(wù)可以選擇是否開啟提醒,在指定的時(shí)間段內(nèi)提醒用戶進(jìn)行打卡。任務(wù)也可以選擇開啟的頻率,如只在周一到周五開啟等。需要記錄每項(xiàng)任務(wù)的目標(biāo)值和實(shí)際完成值,在用戶打卡后判斷任務(wù)是否已經(jīng)完成,并記錄在數(shù)據(jù)庫中。因此,需要?jiǎng)?chuàng)建一張存儲(chǔ)每天的健康任務(wù)信息的表,表頭如下:
每日信息表
在主頁面,用戶可以查看當(dāng)天健康任務(wù)的完成進(jìn)度,需要?jiǎng)?chuàng)建一張表記錄當(dāng)天開啟的任務(wù)個(gè)數(shù)和已經(jīng)完成的任務(wù)個(gè)數(shù),表頭如下:
全局信息表
用戶連續(xù)多日打卡完成所有創(chuàng)建的任務(wù)可以獲得相應(yīng)的成就,因此,需要有一張表記錄連續(xù)打卡天數(shù)和已達(dá)成的成就項(xiàng)。另外,考慮應(yīng)用多日未打開的情況,需要記錄應(yīng)用第一次打開的日期和最后一次打開的日期以向數(shù)據(jù)庫回填數(shù)據(jù),表頭如下:
創(chuàng)建數(shù)據(jù)表
根據(jù)6.2中設(shè)計(jì)的表結(jié)構(gòu),創(chuàng)建對應(yīng)的數(shù)據(jù)表,實(shí)現(xiàn)對相應(yīng)數(shù)據(jù)的讀寫操作。
健康任務(wù)信息數(shù)據(jù)表
在獲取RdbStore后,需要使用executeSql接口執(zhí)行SQL語句來創(chuàng)建相應(yīng)的表結(jié)構(gòu)和初始化數(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
)
健康任務(wù)信息數(shù)據(jù)表需要提供插入數(shù)據(jù)的接口,以在用戶當(dāng)天第一次打開應(yīng)用時(shí)創(chuàng)建當(dāng)天的健康任務(wù)信息,實(shí)現(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.`);
}
其中g(shù)enerateBucket()代碼如下:
// 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;
}
用戶開啟和關(guān)閉任務(wù),改變?nèi)蝿?wù)的目標(biāo)值、提醒時(shí)間、頻率等,用戶打卡后修改任務(wù)的實(shí)際完成值都是通過更新數(shù)據(jù)接口來實(shí)現(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.`);
}
用戶可以查看當(dāng)天和以前某日的健康任務(wù)信息,需要提供查找數(shù)據(jù)接口,實(shí)現(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,則只查找開啟的任務(wù)
if (isOpen) {
predicates.equalTo('isOpen', true);
}
predicates.orderByAsc('taskID'); // 查找結(jié)果按taskID排序
RdbUtils.query(predicates).then(resultSet = > {
let count = resultSet.rowCount;
// 查找結(jié)果為空則返回空數(shù)組,否則返回查找結(jié)果數(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
)
在當(dāng)天第一次打開應(yīng)用時(shí)需要初始化每日信息數(shù)據(jù),頁面需要根據(jù)用戶編輯任務(wù)和打卡的情況來更新當(dāng)天目標(biāo)任務(wù)個(gè)數(shù)和完成任務(wù)個(gè)數(shù),所以需要提供插入數(shù)據(jù)和更新數(shù)據(jù)的接口,寫法與上一條中相應(yīng)接口類似,不再贅述。
頁面需要查找對應(yīng)日期的目標(biāo)任務(wù)個(gè)數(shù)和完成任務(wù)個(gè)數(shù)用以在頁面顯示任務(wù)進(jìn)度,因此需要查找數(shù)據(jù)的接口。且頁面在打開時(shí)需要顯示當(dāng)周每天任務(wù)的完成情況,因此需要允許一次調(diào)用查找一周的每日任務(wù)信息。實(shí)現(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ù)組內(nèi)的所有日期
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é)前兩條中相應(yīng)接口類似,不再贅述。HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿
數(shù)據(jù)庫初始化
應(yīng)用首次打開時(shí),數(shù)據(jù)庫中沒有數(shù)據(jù),要做數(shù)據(jù)庫的初始化,寫入一組空數(shù)據(jù)。另外,如果用戶連續(xù)幾天沒有打開APP,再次打開時(shí)需要將數(shù)據(jù)回寫至數(shù)據(jù)庫。因此需要實(shí)現(xiàn)一個(gè)數(shù)據(jù)庫接口,在應(yīng)用打開時(shí)調(diào)用,進(jìn)行上述操作。代碼如下:
// 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 { // 如果找到全局信息,則查詢當(dāng)天的任務(wù)信息
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);
}
});
}
審核編輯 黃宇
-
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3846瀏覽量
64684 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2392瀏覽量
43048 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1982瀏覽量
30570
發(fā)布評論請先 登錄
相關(guān)推薦
HarmonyOS開發(fā)案例:【關(guān)系型數(shù)據(jù)庫】
![<b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發(fā)案</b>例:【<b class='flag-5'>關(guān)系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>】](https://file1.elecfans.com/web2/M00/D1/22/wKgaomYiW9GAOVkxAMBLtvnlUVM543.jpg)
關(guān)系型數(shù)據(jù)庫與非關(guān)系數(shù)據(jù)庫的區(qū)別淺析
HarmonyOS數(shù)據(jù)庫的相關(guān)資料下載
HarmonyOS關(guān)系型數(shù)據(jù)庫和對象關(guān)系數(shù)據(jù)庫的使用方法
DCS仿真系統(tǒng)的內(nèi)存-關(guān)系型數(shù)據(jù)庫系統(tǒng)的構(gòu)成
什么是關(guān)系型數(shù)據(jù)庫
什么是非關(guān)系型數(shù)據(jù)庫
hbase和關(guān)系型數(shù)據(jù)庫的區(qū)別
![hbase和<b class='flag-5'>關(guān)系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>的區(qū)別](https://file1.elecfans.com//web2/M00/A7/1E/wKgZomUMQnyAGntMAAAbXy3Bht0436.jpg)
數(shù)據(jù)庫設(shè)計(jì)開發(fā)案例教程之數(shù)據(jù)庫設(shè)計(jì)的資料介紹
![<b class='flag-5'>數(shù)據(jù)庫</b>設(shè)計(jì)<b class='flag-5'>開發(fā)案</b>例教程之<b class='flag-5'>數(shù)據(jù)庫</b>設(shè)計(jì)的資料介紹](https://file.elecfans.com/web1/M00/82/39/pIYBAFw4EO-AT-8JAAEaift3s7Y453.png)
基于SQLite的鴻蒙的關(guān)系型數(shù)據(jù)庫使用
![基于SQLite的鴻蒙的<b class='flag-5'>關(guān)系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>使用](https://file.elecfans.com/web1/M00/DB/BC/pIYBAGAHp26AaK2FAAE3c435YvM771.png)
OpenHarmony關(guān)系型數(shù)據(jù)庫概述
![OpenHarmony<b class='flag-5'>關(guān)系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>概述](https://file1.elecfans.com/web2/M00/81/ED/wKgZomQivFyAP7pkAACcKWbjBNk002.png)
關(guān)系型數(shù)據(jù)庫的基本原理(什么是關(guān)系型數(shù)據(jù)庫)
鴻蒙HarmonyOS開發(fā)實(shí)例:【分布式關(guān)系型數(shù)據(jù)庫】
![鴻蒙<b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發(fā)</b>實(shí)例:【分布式<b class='flag-5'>關(guān)系</b><b class='flag-5'>型</b><b class='flag-5'>數(shù)據(jù)庫</b>】](https://file1.elecfans.com/web2/M00/C8/C0/wKgaomYWnY2AXRLeAACi1aI8uJg476.jpg)
評論