鸿蒙HarmonyOS (开发进阶)关系型数据库场景

打印 上一主题 下一主题

主题 1899|帖子 1899|积分 5697

鸿蒙NEXT开发实战往期必看文章:
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发
“非常具体的” 鸿蒙HarmonyOS Next应用开发学习门路!(从零基础入门到精通)
HarmonyOS NEXT应用开发案例实践总联合(持续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(持续更新......)

场景形貌
关系型数据库(Relational Database,RDB)是一种基于关系模子来管理数据的数据库。关系型数据库基于SQLite组件提供了一套完备的对本地数据库进行管理的机制,对外提供了一系列的增、删、改、查等接口,也可以直接运行用户输入的SQL语句来满足复杂的场景需要。
场景一:基于RDB已提供API的数据库基础利用
场景二:基于executeSql、querySql实验增删改查复杂SQL语句
场景三:事务的利用
场景四:批量插入数据的不同实现方式及性能对比
场景五:数据库备份与规复
场景六:全文检索(FTS)利用思绪
方案形貌
场景一:基于RDB已提供API的数据库基础利用
方案
通过insert、update、delete、query接口及关系型数据库谓词predicates的数据库基础操作。RdbPredicates:数据库中用来代表数据实体的性子、特性或者数据实体之间关系的词项,重要用来定义数据库的操作条件。常用的方法有equalTo,notEqualTo,or,and,isnull,between,orderby,groupby等。
核心代码
文中的操作重要基于如下的数据库,具体数据库利用步骤可参考:关系型数据库利用指南。
  1. //创建数据库及相关表
  2. export default class Rdb{
  3. rdbStore?: relationalStore.RdbStore;
  4. context:Context = getContext();
  5. constructor(storeName: string)
  6. {
  7. // 数据库配置
  8. const STORE_CONFIG:relationalStore.StoreConfig = {
  9. name: storeName,
  10. securityLevel: relationalStore.SecurityLevel.S1,
  11. };
  12. // 获取数据库Store
  13. relationalStore.getRdbStore(this.context, STORE_CONFIG, (err: BusinessError, rdbStore: relationalStore.RdbStore) => {
  14. this.rdbStore = rdbStore;
  15. if (err) {
  16. console.error(`Get RdbStore failed, code is ${err.code},message is ${err.message}`);
  17. return;
  18. }
  19. console.info(`Get ${storeName} RdbStore successfully.`);
  20. })
  21. }
  22. CreateTable()
  23. {
  24. //建表语句
  25. const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS STUDENT (ID INTEGER PRIMARY KEY AUTOINCREMENT,NAME TEXT,AGE INTEGER,SALARY REAL)';
  26. if(this.rdbStore){
  27. this.rdbStore.executeSql(SQL_CREATE_TABLE);
  28. console.info(`CreateTable successfully.`);
  29. }
  30. }
  31. }
复制代码
通过ValuesBucket构建数据,insert接口插入。
  1. InsertData(name:string,age:number,salary:number)
  2. {
  3. // 插入数据
  4. const valueBucket: ValuesBucket = {
  5. 'NAME': name,
  6. 'AGE': age,
  7. 'SALARY': salary
  8. };
  9. if(this.rdbStore){
  10. this.rdbStore.insert('STUDENT', valueBucket, (err, rowId) => {
  11. if (err) {
  12. console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
  13. return;
  14. }
  15. console.info(`Succeeded in inserting data. rowId:${rowId}`);
  16. })
  17. }
  18. }
复制代码
通过predicates构建删除条件,删除name为Lisa的数据,delete接口实验删除。
  1. DeleteData()
  2. {
  3. let predicates = new relationalStore.RdbPredicates("STUDENT");
  4. predicates.equalTo("NAME", "Lisa");
  5. if(this.rdbStore != undefined) {
  6. (this.rdbStore as relationalStore.RdbStore).delete(predicates, (err, rows) => {
  7. if (err) {
  8. console.error(`Delete failed, code is ${err.code},message is ${err.message}`);
  9. return;
  10. }
  11. console.info(`Delete rows: ${rows}`);
  12. })
  13. }
  14. }
复制代码
通过update接口,根据predicates构造的条件,及valueBucket传入的数据修改数据。
  1. UpdateData(name:string,age:number,salary:number)
  2. {
  3. const valueBucket: ValuesBucket = {
  4. 'NAME': name,
  5. 'AGE': age,
  6. 'SALARY': salary
  7. };
  8. let predicates = new relationalStore.RdbPredicates("EMPLOYEE");
  9. predicates.equalTo("NAME", "Lisa");
  10. if(this.rdbStore != undefined) {
  11. (this.rdbStore as relationalStore.RdbStore).update(valueBucket, predicates, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE, (err, rows) => {
  12. if (err) {
  13. console.error(`Updated failed, code is ${err.code},message is ${err.message}`);
  14. return;
  15. }
  16. console.info(`Updated row count: ${rows}`);
  17. })
  18. }
  19. }
复制代码
通过query接口查询数据,返回resultSet结果集,并解析查询到的数据。
  1. QueryData(callback:Callback<string>)
  2. {
  3. // 配置谓词
  4. let predicates = new relationalStore.RdbPredicates("STUDENT");
  5. let jsonData:Array<ValuesBucket> = new Array<ValuesBucket>();
  6. if(this.rdbStore){
  7. this.rdbStore.query(predicates, ["ID", 'NAME', 'AGE', 'SALARY'], (err, resultSet) => {
  8. if (err) {
  9. console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
  10. return;
  11. }
  12. console.info(`ResultSet column names: ${resultSet.columnNames}, row count: ${resultSet.rowCount}`);
  13. if(resultSet.rowCount == -1){
  14. console.info("rowCount=-1")
  15. }
  16. // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
  17. while (resultSet.goToNextRow()) {
  18. const id = resultSet.getLong(resultSet.getColumnIndex("ID"));
  19. const name = resultSet.getString(resultSet.getColumnIndex("NAME"));
  20. const age = resultSet.getLong(resultSet.getColumnIndex("AGE"));
  21. const salary = resultSet.getDouble(resultSet.getColumnIndex("SALARY"));
  22. console.info(`id=${id}, name=${name}, age=${age}, salary=${salary}`);
  23. const valueBucket: ValuesBucket = {
  24. 'ID':id,
  25. 'NAME': name,
  26. 'AGE': age,
  27. 'SALARY': salary
  28. };
  29. jsonData.push(valueBucket)
  30. }
  31. // 释放数据集的内存
  32. resultSet.close();
  33. // console.info("JSON: " + JSON.stringify(jsonData))
  34. callback(JSON.stringify(jsonData))
  35. })
  36. }
  37. }
复制代码
向数据库添加数据


场景二:基于executeSql、querySql实验增删改查复杂SQL语句
方案
在实际利用过程中,复杂的SQL语句大概无法直接通过场景一提供的方式实现,此时需要开发者通过executeSql、querySql接口实验自定义SQL语句。executeSql可以或许实验包含指定参数但不返回值的SQL语句,如创建表、创建索引、数据库触发器等场景。querySql可以或许根据指定SQL语句查询数据库中的数据,并返回查询结果ResultSet结果集。如递归查询、子查询等场景。
核心代码
  1. //创建表
  2. const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT,NAME TEXT NOT NULL,AGE INTEGER,SALARY REAL)';
  3. //创建索引
  4. const CTEATE_INDEX ='CREATE INDEX idx_name ON EMPLOYEE (NAME)'
  5. this.rdbStore.executeSql(SQL_CREATE_TABLE);
  6. this.rdbStore.executeSql(CTEATE_INDEX);
  7. //数据库触发器
  8. const TRIGGER_SQL2 = "CREATE TRIGGER test AFTER INSERT ON EMPLOYEE" +
  9. " BEGIN" +
  10. " UPDATE EMPLOYEE SET SALARY = '123456' WHERE ID = 1;" +
  11. " END;"
复制代码
  1. //子查询
  2. const SubQuery_SQL = "SELECT * FROM STUDENT WHERE ID IN (SELECT ID FROM STUDENT WHERE NAME = 'John');"
复制代码
  1. //联合查询
  2. const JoinQuerySQL ="SELECT student.name, courses.name, courses.credits FROM students JOIN courses ON students.id = courses.student_id;"
复制代码
对于上述的复杂SQL语句,就可以利用executeSql、querySql接口实验,。
  1. myExecuteSql(sql:string)
  2. {
  3. if(this.rdbStore != undefined) {
  4. (this.rdbStore as relationalStore.RdbStore).executeSql(sql, (err) => {
  5. if (err) {
  6. console.error(`ExecuteSql failed, code is ${err.code},message is ${err.message}`);
  7. return;
  8. }
  9. console.info('ExecuteSql success');
  10. })
  11. }
  12. }
  13. myQuerySql(sql:string)
  14. {
  15. if(this.rdbStore){
  16. (this.rdbStore as relationalStore.RdbStore).querySql(sql, (err, resultSet) => {
  17. if (err) {
  18. console.error(`Query failed, code is ${err.code},message is ${err.message}`);
  19. return;
  20. }
  21. console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
  22. // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
  23. while (resultSet.goToNextRow()) {
  24. const name = resultSet.getString(resultSet.getColumnIndex("NAME"));
  25. const age = resultSet.getLong(resultSet.getColumnIndex("AGE"));
  26. console.info(`name=${name}, age=${age}`);
  27. }
  28. // 释放数据集的内存
  29. resultSet.close();
  30. })
  31. }
  32. }
复制代码
场景三:事务的利用
方案
数据库事务可以保证指一组数据库操作要么全部实验乐成,要么全部回滚。鸿蒙关系型数据库提供了事务相关接口 beginTransaction、commit、rollBack。本例通过模拟一组操作中,有一条操作失败后,回滚已经实验的SQL语句。
核心代码
  1. // 插入数据
  2. if (this.rdbStore != undefined) {
  3. //构建两条数据,其中第二条数据与表结构不符,会操作失败,数据库会回滚。
  4. const valueBucket: ValuesBucket = {
  5. NAME: name,
  6. AGE: age,
  7. SALARY: salary
  8. };
  9. const valueBucket2: ValuesBucket = {
  10. NAME: 'scd',
  11. SEX:'男',
  12. AGE: 18,
  13. SALARY: 18888
  14. };
  15. try {
  16. //开启事务
  17. this.rdbStore.beginTransaction();
  18. await this.rdbStore.insert('STUDENT', valueBucket)
  19. await this.rdbStore.insert('STUDENT', valueBucket2)
  20. //提交事务
  21. this.rdbStore.commit();
  22. }
  23. catch (err) {
  24. //回滚事务
  25. let code = (err as BusinessError).code;
  26. let message = (err as BusinessError).message
  27. console.error(`Transaction failed, code is ${code},message is ${message}`);
  28. this.rdbStore.rollBack()
  29. return;
  30. }
  31. }
  32. }
复制代码
上述代码中第一条数据插入乐成,第二条数据与表结构不符,插入时会失败,此时数据库会回滚,第一条插入的数据被取消,数据库不会发生厘革。


场景四:批量插入数据的不同实现方式及性能对比
方案
分别利用insert、事务insert、batchInsert三种方式各自插入5000条数据,统计各自使命耗时
核心代码
直接利用insert插入5000条数据。
  1. InsertDatas(name:string,age:number,salary:number)
  2. {
  3. for (let i = 1; i <= 5000; i++) {
  4. // 插入数据
  5. const valueBucket: ValuesBucket = {
  6. 'NAME': name+i,
  7. 'AGE': age,
  8. 'SALARY': salary
  9. };
  10. if(this.rdbStore){
  11. this.rdbStore.insert('STUDENT', valueBucket, (err, rowId) => {
  12. if (err) {
  13. console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
  14. return;
  15. }
  16. console.info(`Succeeded in inserting data. rowId:${rowId}`);
  17. })
  18. }
  19. }
  20. }
复制代码
在事务中利用insert插入5000条数据
  1. InsertDataByTransaction(name:string,age:number,salary:number){
  2. if (this.rdbStore != undefined) {
  3. try {
  4. //开启事务
  5. this.rdbStore.beginTransaction();
  6. for (let i = 1; i <= 5000; i++) {
  7. // 插入数据
  8. const valueBucket: ValuesBucket = {
  9. 'NAME': name+i,
  10. 'AGE': age,
  11. 'SALARY': salary
  12. };
  13. if(this.rdbStore){
  14. this.rdbStore.insert('STUDENT', valueBucket, (err, rowId) => {
  15. if (err) {
  16. console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`);
  17. return;
  18. }
  19. console.info(`Succeeded in inserting data. rowId:${rowId}`);
  20. })
  21. }
  22. }
  23. //提交事务
  24. this.rdbStore.commit();
  25. }
  26. catch (err) {
  27. //回滚事务
  28. let code = (err as BusinessError).code;
  29. let message = (err as BusinessError).message
  30. console.error(`Transaction failed, code is ${code},message is ${message}`);
  31. this.rdbStore.rollBack()
  32. return;
  33. }
  34. }
  35. }
复制代码
利用barchInsert插入5000条数据
  1. //批量添加
  2. BatchInsert(name:string,age:number,salary:number){
  3. {
  4. if (this.rdbStore == null) {
  5. console.error(`Create Store1.db failed! store1 is null`);
  6. return;
  7. }
  8. }
  9. let valueBucketArray: Array<relationalStore.ValuesBucket> = new Array();
  10. for (let i=1; i <=5000; i++) {
  11. const valueBucket: relationalStore.ValuesBucket = {
  12. 'NAME': name+i,
  13. 'AGE': age,
  14. 'SALARY': salary
  15. }
  16. valueBucketArray.push(valueBucket);
  17. }
  18. try {
  19. this.rdbStore.batchInsert("STUDENT", valueBucketArray ); // 该接口内部使用事务
  20. console.info(`Insert data successfully!`);
  21. } catch (err) {
  22. console.error(`Insert datae failed! err code:${err.code}, err message:${err.message}`)
  23. }
  24. }
复制代码


对上述三个方法进行耗时统计,根据测试结果,事务中批量添加数据与直接for循环添加数据耗时相差无几,而利用batchInsert时,时间显着更快,且batchInsert接口内部利用了事务,因此建议批量插入场景利用batchInsert。
场景五:数据库备份与规复
方案
在数据库的利用过程中,数据库大概会由于数据丢失、数据损坏、脏数据等而不可用情况,为了预防这种情况,可以通过backup接口,提前备份数据库数据到本地文件中,当发生意外后,可以通过restore接口,从指定的数据库备份文件规复数据库。
核心代码
  1. //备份数据库
  2. myBackup(){
  3. if(this.rdbStore){
  4. (this.rdbStore as relationalStore.RdbStore).backup("dbBackup.db", (err) => {
  5. if (err) {
  6. console.error(`Backup failed, code is ${err.code},message is ${err.message}`);
  7. return;
  8. }
  9. console.info(`Backup success.`);
  10. })
  11. }
  12. }
  13. //恢复数据库
  14. myRestore(backupFileName:string){
  15. // value:备份数据库名
  16. if(this.rdbStore){
  17. (this.rdbStore as relationalStore.RdbStore).restore(backupFileName, (err) => {
  18. if (err) {
  19. console.error(`Restore failed, code is ${err.code},message is ${err.message}`);
  20. return;
  21. }
  22. console.info(`Restore success.`);
  23. })
  24. }
  25. }
复制代码
测试过程中,先通过myBackup备份数据库,数据库文件路径生成数据库备份文件dbBackup.db。


随后利用delete删除name为testA、testB数据后,此时数据库已无相关数据。


再利用myRestore规复数据库后,数据库规复到备份时状态。


注:数据库文件路径可通过context.databaseDir获取。如:/data/app/el2/100/database/{bundleName}/entry/rdb
场景六:全文检索(FTS)利用思绪
方案
HarmonyOS关系型数据库底层利用的是Sqlite,在Sqite中FTS的核心是倒排索引,它是一种将词汇映射到出现该词汇的文档集合的数据结构。在创建FTS虚拟表时,SQLite会为每个词汇生成一个倒排索引,记录该词汇在哪些文档(即数据库记录)中出现。倒排索引使得全文搜刮可以或许快速找到包含特定词汇的文档,而无需遍历整个数据库。
FTS虚拟表(Full-Text Search Virtual Table)是SQLite中实现全文搜刮的一种特殊表结构。它用于存储全文索引数据,包括倒排索引的信息。固然FTS虚拟表在查询时表现得像平凡的SQLite表,但其实现和存储方式与平凡表有很大不同。
利用全文检索时,创建FTS表需要利用CREATE VIRTUAL TABLE语句,实验全文搜刮需要利用MATCH关键字,当前HarmonyOS关系型数据库并没有直接提供相关接口,但是数据库底层是支持的,因此可以通过executeSql、querySql实验相关SQL语句。
核心代码
  1. //备份数据库myBackup(){if(this.rdbStore){(this.rdbStore as relationalStore.RdbStore).backup("dbBackup.db", (err) => {if (err) {console.error(`Backup failed, code is ${err.code},message is ${err.message}`);return;}console.info(`Backup success.`);})}}//规复数据库myRestore(backupFileName:string){// value:备份数据库名if(this.rdbStore){(this.rdbStore as relationalStore.RdbStore).restore(backupFileName, (err) => {if (err) {console.error(`Restore failed, code is ${err.code},message is ${err.message}`);return;}console.info(`Restore success.`);})//创建虚拟表const sql1:string = "CREATE VIRTUAL TABLE products_fts USING fts5(NAME , AGE)"this.rdbTest.myExecuteSql(sql1)//虚拟表添加数据 InsertDataToVIRTUAL(){let sql:string = "INSERT INTO products_fts(NAME , AGE) SELECT NAME,AGE FROM STUDENT"this.myExecuteSql(sql)}//利用全文检索查询数据let sql:string = "SELECT * FROM products_fts WHERE AGE MATCH 18"this.rdbTest.myQuerySql(sql)
  2. myExecuteSql(sql:string)
  3. {
  4. if(this.rdbStore != undefined) {
  5. (this.rdbStore as relationalStore.RdbStore).executeSql(sql, (err) => {
  6. if (err) {
  7. console.error(`ExecuteSql failed, code is ${err.code},message is ${err.message}`);
  8. return;
  9. }
  10. console.info('ExecuteSql success');
  11. })
  12. }
  13. }
  14. myQuerySql(sql:string)
  15. {
  16. if(this.rdbStore){
  17. (this.rdbStore as relationalStore.RdbStore).querySql(sql, (err, resultSet) => {
  18. if (err) {
  19. console.error(`Query failed, code is ${err.code},message is ${err.message}`);
  20. return;
  21. }
  22. console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
  23. // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
  24. while (resultSet.goToNextRow()) {
  25. const name = resultSet.getString(resultSet.getColumnIndex("NAME"));
  26. const age = resultSet.getLong(resultSet.getColumnIndex("AGE"));
  27. console.info(`name=${name}, age=${age}`);
  28. }
  29. // 释放数据集的内存
  30. resultSet.close();
  31. })
  32. }
  33. }}}
复制代码
查询虚拟表中AGE"字段匹配关键词"18"的记录,虚拟表查询结果:




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表