基于鸿蒙OS4的关系数据库使用学习——以浅易版备忘录为例 ...

打印 上一主题 下一主题

主题 849|帖子 849|积分 2547

概述

这里是我的项目地址,欢迎大家下载参考~有问题欢迎相互指出,谢谢!!
项目gitee地址
丑化说在前头,这个备忘录真的是十分大略,这一篇的写作目的不是为了能告诉读者备忘录怎么写和怎么美化,而是希望在现有鸿蒙OS4关系型数据库教学还比较少的情况下,官网教学也比较大略,把复杂的数据库设置抽离出来,让大家少走点弯路,让大家能更快的使用关系型数据库实现自己想要的业务目标。
笔者走了许多弯路了呜呜,希望大家多多点赞收藏~~
废话不多说,开干!!
这个学习案例,主要是参考b站大佬的备忘录设计和华为开辟者官网的例子记账本,大家也可以在了解根本关系型数据库怎么设置后详细学习
我们主要/scr/main的目录布局如下:
  1. ../scr/main目录下文件树结构展示
  2. .
  3. |-- constants
  4. |   `-- CommonConstants.ets
  5. |-- database
  6. |   |-- MemoTable.ets
  7. |   `-- Rdb.ets
  8. |-- ets
  9. |   |-- component
  10. |   |   `-- MemoItem.ets
  11. |   |-- entryability
  12. |   |   `-- EntryAbility.ts
  13. |   `-- pages
  14. |       `-- Index.ets
  15. |-- model
  16. |   |-- MemoModel.ets
  17. |   `-- TableInterface.ets
  18. |-- module.json5
  19. |-- resources
  20. |   |-- base
  21. |   |   |-- element
  22. |   |   |   |-- color.json
  23. |   |   |   `-- string.json
  24. |   |   |-- media
  25. |   |   |   `-- icon.png
  26. |   |   `-- profile
  27. |   |       `-- main_pages.json
  28. |   |-- en_US
  29. |   |   `-- element
  30. |   |       `-- string.json
  31. |   |-- rawfile
  32. |   `-- zh_CN
  33. |       `-- element
  34. |           `-- string.json
  35. `-- utils
  36.     `-- Logger.ets
复制代码
数据库的设置

我们先设置数据库
在./main/constants目录下,创建CommonConstants.ets文件,在这个文件里,我们存放Rdb数据库的根本设置,详细跟官网样例也差不多
  1. import relationalStore from '@ohos.data.relationalStore'
  2. import { MemoTable } from '../model/TableInterface'
  3. export default class CommonConstants {
  4.   // Rdb 数据库配置
  5.   static readonly STORE_CONFIG:relationalStore.StoreConfig = {
  6.     name:'database.db',
  7.     securityLevel:relationalStore.SecurityLevel.S1
  8.   }
  9.   static readonly MEMO_TABLE:MemoTable = {
  10.     tableName:'memoTable',
  11.     sqlCreate:'CREATE TABLE IF NOT EXISTS memoTable(id INTEGER PRIMARY KEY AUTOINCREMENT, updateTime INTEGER, content TEXT)',
  12.     columns:['id','updateTime','content']
  13.   }
  14.   // 一些日志控制参数
  15.   static readonly RDB_TAG = '[Debug.Rdb]';
  16.   static readonly TABLE_TAG = '[Debug.memoTable]';
  17.   static readonly INDEX_TAG = '[Debug.Index]';
  18. }
复制代码
我在学习的时间注意到,大佬在/main/model目录下,设置了TableInterface.ets文件,存放了一个MeMoTable接口
这种接口的设置,不用设置关键词function,我认识到大概一个是为了给CommonConstants类中的MEMO_TABLE内部元素设置类型,一个是降低代码耦合度便于后面维护
在/main/model目录下,新建TableInterface.ets文件,写一个MeMoTable接口
  1. ```typescript
  2. export interface MemoTable {
  3.   tableName: string
  4.   sqlCreate: string
  5.   columns: string[]
  6. }
复制代码
原来的界说如下
  1. interface 接口名{
  2.     接口代码:一般定义规则、方法等
  3.     方法名(形参:形参类型):方法返回值
  4. }
复制代码
进一步,大佬对数据库的操作进行了一层抽象,这个抽象的意思就是把很复杂的操作用几个函数给你概括起来了
   就像我们假如形容一辆车,我们原来按照比较机器的方法,要界说外壳轮廓、动力参数什么的,现在直接告诉你小轿车、发动机V8什么的,你就大概了解了,我想后者就是抽象,把复杂的东西简朴化
  在/main/database目录下,新建Rdb.ets文件,这个文件就是负责抽象复杂的数据库操作方法,不用来实现详细业务,只规定获取rdbStore对象,并用哪张表增删改查之类的流程
我们后续要使用数据库的数据操作时,数据操作也被简化为getRdbStore, insertData, deleteData, updateData, query方法
  1. import relationalStore from '@ohos.data.relationalStore';
  2. import CommonConstants from '../constants/CommonConstants';
  3. import Logger from '../utils/Logger';
  4. // 抽象的Rdb类,没有业务属性,可以基于这个类创建带有业务属性的一张或多张表
  5. export default class Rdb {
  6.   // 管理关系数据库(RDB)方法属性 类型是RdbStore 或 null
  7.   private rdbStore: relationalStore.RdbStore | null = null;
  8.   // 数据库表名称
  9.   private tableName: string;
  10.   // 创建SQLite表的语句字符串
  11.   private sqlCreateTable: string;
  12.   // 列名字段的数组
  13.   private columns: Array<string>;
  14.   // 实例化 Rdb 对象时,初始化表名、创建表的语句、列名字段的数组
  15.   constructor(tableName: string, sqlCreateTable: string, columns: Array<string>) {
  16.     this.tableName = tableName;
  17.     this.sqlCreateTable = sqlCreateTable;
  18.     this.columns = columns;
  19.   }
  20.   // 获取操作数据库RdbStore对象的方法,接收一个回调函数
  21.   getRdbStore(callback: Function = () => {
  22.   }) {
  23.     // 如果 回调函数为空、或undefined 打印错误日志,退出方法
  24.     if (!callback || typeof callback === 'undefined' || callback === undefined) {
  25.       Logger.info(CommonConstants.RDB_TAG, 'getRdbStore() has no callback!');
  26.       return;
  27.     }
  28.     // 如果属性rdbStore 不为空,说明操作对象已经初始化,打印日志,执行回调函数,退出方法
  29.     if (this.rdbStore !== null) {
  30.       Logger.info(CommonConstants.RDB_TAG, 'The rdbStore exists.');
  31.       callback();
  32.       return
  33.     }
  34.     // 执行到此处,说明回调函数不为空,且rdbStore尚未初始化
  35.     // 获取上下文
  36.     let context: Context = getContext(this) as Context;
  37.     /*
  38.       获取RdbStore接口的方法,接收三个参数
  39.       context  :应用上下文
  40.       config   :数据库配置
  41.       callback :回调函数,异步返回rdb对象
  42.     */
  43.     relationalStore.getRdbStore(context, CommonConstants.STORE_CONFIG, (err, rdb) => {
  44.       // 如果发生错误,打印日志,退出方法
  45.       if (err) {
  46.         Logger.error(CommonConstants.RDB_TAG, `gerRdbStore() failed, err: ${err}`);
  47.         return;
  48.       }
  49.       // 如果没有错误,为rdbStore属性赋值为rdb
  50.       this.rdbStore = rdb;
  51.       // 执行Sql语句,使用创建表的字符串
  52.       this.rdbStore.executeSql(this.sqlCreateTable);
  53.       // 打印日志
  54.       Logger.info(CommonConstants.RDB_TAG, 'getRdbStore() finished.');
  55.       // 执行方法入参的回调函数
  56.       callback();
  57.     });
  58.   }
  59.   // 插入数据的方法,入参为表格的存储键值对,和回调函数
  60.   insertData(data: relationalStore.ValuesBucket, callback: Function = () => {
  61.   }) {
  62.     // 如果回调函数为空、或undefined 打印错误日志,退出方法
  63.     if (!callback || typeof callback === 'undefined' || callback === undefined) {
  64.       Logger.info(CommonConstants.RDB_TAG, 'insertData() has no callback!');
  65.       return;
  66.     }
  67.     // 创建结果标识局部变量,默认为false
  68.     let resFlag: boolean = false;
  69.     // 创建存储键值对局部变量,默认为入参data
  70.     const valueBucket: relationalStore.ValuesBucket = data;
  71.     // 如果rdbStore存在则执行
  72.     if (this.rdbStore) {
  73.       // 执行rdbStore的插入数据方法,入参为表名、存储键值对、异步回调函数
  74.       this.rdbStore.insert(this.tableName, valueBucket, (err, ret) => {
  75.         if (err) {
  76.           // 如果发生错误,打印日志,执行入参的回调函数,并传入结果标识 false
  77.           Logger.error(CommonConstants.RDB_TAG, `insertData() failed, err: ${err}`);
  78.           callback(resFlag);
  79.           return;
  80.         }
  81.         // 如果操作成功,打印日志,执行回调函数,并传入返回ret(行ID)
  82.         Logger.info(CommonConstants.RDB_TAG, `insertData() finished: ${ret}`);
  83.         callback(ret);
  84.       });
  85.     }
  86.   }
  87.   // 删除数据的方法,接收谓词和回调函数两个参数
  88.   deleteData(predicates: relationalStore.RdbPredicates, callback: Function = () => {
  89.   }) {
  90.     // 如果回调函数为空、或undefined 打印错误日志,退出方法
  91.     if (!callback || typeof callback === 'undefined' || callback === undefined) {
  92.       Logger.info(CommonConstants.RDB_TAG, 'deleteData() has no callback!');
  93.       return;
  94.     }
  95.     // 创建结果标识局部变量,默认为false
  96.     let resFlag: boolean = false;
  97.     // 如果rdbStore存在则执行
  98.     if (this.rdbStore) {
  99.       // rdbStore的删除方法,入参为谓词、异步回调函数
  100.       this.rdbStore.delete(predicates, (err, ret) => {
  101.         // 如果发生错误,打印日志,执行入参的回调函数,并传入结果标识 false
  102.         if (err) {
  103.           Logger.error(CommonConstants.RDB_TAG, `deleteData() failed, err: ${err}`);
  104.           callback(resFlag);
  105.           return;
  106.         }
  107.         // 如果删除成功,打印日志,执行回调函数,并传入返回ret(行ID)
  108.         Logger.info(CommonConstants.RDB_TAG, `deleteData() finished: ${ret}`);
  109.         callback(!resFlag);
  110.       });
  111.     }
  112.   }
  113.   // 更新数据的方法,接收三个参数:谓词、存储键值对、回调函数
  114.   updateData(predicates: relationalStore.RdbPredicates, data: relationalStore.ValuesBucket, callback: Function = () => {
  115.   }) {
  116.     // 如果回调函数为空、或undefined 打印错误日志,退出方法
  117.     if (!callback || typeof callback === 'undefined' || callback === undefined) {
  118.       Logger.info(CommonConstants.RDB_TAG, 'updateDate() has no callback!');
  119.       return;
  120.     }
  121.     // 创建结果标识局部变量,默认为false
  122.     let resFlag: boolean = false;
  123.     // 创建存储键值对局部变量,默认为入参data
  124.     const valueBucket: relationalStore.ValuesBucket = data;
  125.     // 如果rdbStore存在则执行
  126.     if (this.rdbStore) {
  127.       // rdbStore的更新方法,接收三个参数,存储键值对、谓词、回调函数
  128.       this.rdbStore.update(valueBucket, predicates, (err, ret) => {
  129.         if (err) {
  130.           // 如果发生错误,打印日志,执行入参的回调函数,并传入结果标识 false
  131.           Logger.error(CommonConstants.RDB_TAG, `updateData() failed, err: ${err}`);
  132.           callback(resFlag);
  133.           return;
  134.         }
  135.         // 如果更新成功,打印日志,执行回调函数,并传入返回成功的标识 true
  136.         Logger.info(CommonConstants.RDB_TAG, `updateData() finished: ${ret}`);
  137.         callback(!resFlag);
  138.       });
  139.     }
  140.   }
  141.   // 查询数据的方法,接收两个参数,谓词、回调函数
  142.   query(predicates: relationalStore.RdbPredicates, callback: Function = () => {
  143.   }) {
  144.     // 如果回调函数为空、或undefined 打印错误日志,退出方法
  145.     if (!callback || typeof callback === 'undefined' || callback === undefined) {
  146.       Logger.info(CommonConstants.RDB_TAG, 'query() has no callback!');
  147.       return;
  148.     }
  149.     // 如果rdbStore存在则执行
  150.     if (this.rdbStore) {
  151.       // rdbStore的查询方法,接收三个参数,谓词,数据库列名的数组,异步回调函数
  152.       this.rdbStore.query(predicates, this.columns, (err, resultSet) => {
  153.         // 如果发生错误,打印日志,退出方法
  154.         if (err) {
  155.           Logger.error(CommonConstants.RDB_TAG, `query() failed, err:  ${err}`);
  156.           return;
  157.         }
  158.         // 如果没有错误,打印日志
  159.         Logger.info(CommonConstants.RDB_TAG, 'query() finished.');
  160.         // 执行入参的回调函数,传入查询结果
  161.         callback(resultSet);
  162.         // 关闭结果集
  163.         resultSet.close();
  164.       });
  165.     }
  166.   }
  167. }
复制代码
数据实现

接下来我们来写详细的数据
我们在/main/model目录下创建MeMoModel.ets,设置数据类,设置类MeMoModel,这个类用来管理添加的备忘录的根本信息
  1. class MemoModel {
  2.   id: number
  3.   updateTime: number
  4.   content: string
  5.   constructor(id: number, updateTime: number, content: string) {
  6.     this.id = id
  7.     this.updateTime = updateTime
  8.     this.content = content
  9.   }
  10. }
  11. export default MemoModel
复制代码
我们在/main/database目录下创建MemoTable.ets,这个是用来进行备忘录详细业务逻辑操作的
我们一开始先实例化Rdb类,然后依次获取getRdbStore,insertData,deleteData,updateData,query方法,我们在这里的方法里,可以详细设置我们业务数据怎么处理的,比如在query里,我们是根据须要组装变量tmp,并且加到result中
  1. import relationalStore from '@ohos.data.relationalStore';
  2. import MemoModel from '../model/MemoModel'
  3. import CommonConstants from '../constants/CommonConstants';
  4. import Rdb from './Rdb';
  5. // 笔记的数据库类,基于Rdb类,有业务属性
  6. export default class MemoTable {
  7.   // 通过Rdb类实例化memoTable对象,使用常量文件“CommonConstants”中配置的参数
  8.   private memoTable = new Rdb(CommonConstants.MEMO_TABLE.tableName, CommonConstants.MEMO_TABLE.sqlCreate,
  9.     CommonConstants.MEMO_TABLE.columns);
  10.   // 构造函数,执行getRdbStore方法
  11.   constructor(callback: Function = () => {
  12.   }) {
  13.     this.memoTable.getRdbStore(callback);
  14.   }
  15.   // 通过memoTable对象,执行原Rdb类中的getRdbStore方法,为memoTable对象获取rdbStore操作对象
  16.   getRdbStore(callback: Function = () => {
  17.   }) {
  18.     // 透传回调函数
  19.     this.memoTable.getRdbStore(callback);
  20.   }
  21.   // 插入数据的方法,接收单条memo笔记对象,和回调函数
  22.   insertData(memo: MemoModel, callback: Function) {
  23.     // 通过工具函数,将单条memo笔记,转化为存储健值对
  24.     const valueBucket: relationalStore.ValuesBucket = generateBucket(memo);
  25.     // 执行memoTable的插入数据方法,透传回调函数
  26.     this.memoTable.insertData(valueBucket, callback);
  27.   }
  28.   // 删除数据的方法,接收单条memo笔记对象,和回调函数
  29.   deleteData(memo: MemoModel, callback: Function) {
  30.     // 初始化操作数据库的谓词对象
  31.     let predicates = new relationalStore.RdbPredicates(CommonConstants.MEMO_TABLE.tableName);
  32.     // 配置谓词以匹配数据表的id列中值的字段,为笔记对象的id
  33.     predicates.equalTo('id', memo.id);
  34.     // 使用谓词执行memoTable的删除方法,透传回调函数
  35.     this.memoTable.deleteData(predicates, callback);
  36.   }
  37.   // 更新数据的方法,接收单条memo笔记对象,和回调函数
  38.   updateData(memo: MemoModel, callback: Function) {
  39.     // 通过工具函数,将单条memo笔记,转化为存储健值对
  40.     const valueBucket: relationalStore.ValuesBucket = generateBucket(memo);
  41.     // 初始化操作数据库的谓词对象
  42.     let predicates = new relationalStore.RdbPredicates(CommonConstants.MEMO_TABLE.tableName);
  43.     // 配置谓词以匹配数据表的id列中值的字段,为笔记对象的id
  44.     predicates.equalTo('id', memo.id);
  45.     // 使用谓词执行memoTable的更新方法,透传回调函数
  46.     this.memoTable.updateData(predicates, valueBucket, callback);
  47.   }
  48.   // 查询全部笔记的方法
  49.   query(callback: Function) {
  50.     // 初始化操作数据库的谓词对象
  51.     let predicates = new relationalStore.RdbPredicates(CommonConstants.MEMO_TABLE.tableName);
  52.     // 使用谓词执行memoTable的查询方法,此时谓词匹配未指定(equalTo),所以将查询全部
  53.     this.memoTable.query(predicates, (resultSet: relationalStore.ResultSet) => {
  54.       // 创建局部变量,存储结果的行数
  55.       let count: number = resultSet.rowCount;
  56.       // 如果查询的结果为0,执行回调函数,传入空数组
  57.       if (count === 0 || typeof count === 'string') {
  58.         console.log(`${CommonConstants.TABLE_TAG}` + 'Query no results!');
  59.         callback([]);
  60.       } else {
  61.         // 如果有结果,将结果集指向第一行
  62.         resultSet.goToFirstRow();
  63.         // 创建局部变量,存储最后将返回的结果数组
  64.         const result: MemoModel[] = [];
  65.         // 使用循环为结果添加内容
  66.         for (let i = 0; i < count; i++) {
  67.           let tmp: MemoModel = {
  68.             id: 0, updateTime: 0, content: ''
  69.           };
  70.           tmp.id = resultSet.getDouble(resultSet.getColumnIndex('id'));
  71.           tmp.updateTime = resultSet.getDouble(resultSet.getColumnIndex('updateTime'));
  72.           tmp.content = resultSet.getString(resultSet.getColumnIndex('content'));
  73.           result[i] = tmp;
  74.           // 转至下一行
  75.           resultSet.goToNextRow();
  76.         }
  77.         // 执行回调函数,把所有的查询结果传入
  78.         callback(result);
  79.       }
  80.     });
  81.   }
  82. }
  83. // 工具函数,将memo笔记的数据结构,转化为存储键值对
  84. function generateBucket(memo: MemoModel): relationalStore.ValuesBucket {
  85.   let obj: relationalStore.ValuesBucket = {};
  86.   obj.updateTime = memo.updateTime
  87.   obj.content = memo.content
  88.   return obj;
  89. }
复制代码
页面设置

我们先在Index.ets目录下,设置一个简朴的页面,可以或许实现备忘录根本样式,并且能添加大略卡片样式的列表
  1. @Entry
  2. @Component
  3. struct Index {
  4.   // 缓存的原始数组
  5.   originMemoList: MemoModel[] = []
  6.   // 页面中展示的memo列表
  7.   @State memoList: MemoModel[] = []
  8.   // 新建暂存内容
  9.   @State addContent: string = ''
  10.   @State number: number = 1
  11.   // 当前编辑的memo索引
  12.   editingMemoIndex: number = -1
  13.   build() {
  14.    Stack() {
  15.      // 主体页面
  16.      Column() {
  17.        Row() {
  18.          Text("MeMo")
  19.            .fontSize(20)
  20.            .fontColor(Color.White)
  21.            .margin({ left:20})
  22.          Blank()
  23.          Button() {
  24.            Text("我是按钮")
  25.              .fontSize(20)
  26.              .fontColor(Color.White)
  27.          }
  28.          .height(40)
  29.          .width(150)
  30.          .onClick(() => {
  31.            this.number = this.number + 1
  32.          })
  33.        }
  34.        .width('100%')
  35.        .height(60)
  36.        .linearGradient({
  37.          direction:GradientDirection.Bottom,
  38.          colors:[[Color.Black, 0.5], [Color.Gray, 1]]
  39.        })
  40.        .shadow({
  41.          radius:10
  42.        })
  43.        Divider()
  44.          .color('#604637')
  45.        // 笔记列表
  46.        Scroll() {
  47.          Column() {
  48.             Text(String(this.number))
  49.               .fontSize(40)
  50.            List({space: 10}) {
  51.              ForEach(this.memoList, (item: MemoModel, index) => {
  52.                 ListItem() {
  53.                   Row() {
  54.                     Text(item.content)
  55.                     Text(String(item.updateTime))
  56.                   }
  57.                 }.card()
  58.              })
  59.            }
  60.          }
  61.        }
  62.      }
  63.      .width('100%')
  64.      .height('100%')
  65.    }
  66.   }
  67. }
复制代码
手机模仿器界面如下

注意看,我这里是不能用它自带的Previewer进行展示的,我的Previewer会呈空缺

我感觉很少人说过这个,网上超少,而且这个情况是我一旦使用了数据库之后,他立马就会变成空缺,只能用手机模仿器显示,我想大概Previewer不能涉及有数据相关的操作,而真机模仿就可以。
功能展示

功能很简朴,如下,并且应用关闭了之后,数据也不会丢失,会继续保存下来。



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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

三尺非寒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表