鸿蒙next 关系型数据库 你不知道的事情

2024-12-22 23:38:55
122次阅读
0个评论

前言:

各位同学大家好,有一段时间没有更新文章了,最近在研究鸿蒙关系型数据也就是轻量级数据库sqlite 的使用,今天趁着这个机会分享给大家。

分享内容:

鸿蒙next中应用数据持久化

鸿蒙的应用数据持久化有3种
  • 1 通过用户首选项实现数据持久化
  • 2 通过键值型数据库实现数据持久化
  • 3 通过关系型数据库实现数据持久化

今天我们主要讲的是 通过关系型数据库实现数据持久化后面的前面两种方式我们之前文章有讲到的

鸿蒙next 数据持久化 你不知道的事情

场景介绍

关系型数据库基于SQLite组件,适用于存储包含复杂关系数据的场景,比如一个班级的学生信息,需要包括姓名、学号、各科成绩等,又或者公司的雇员信息,需要包括姓名、工号、职位等,由于数据之间有较强的对应关系,复杂程度比键值型数据更高,此时需要使用关系型数据库来持久化保存数据。

大数据量场景下查询数据可能会导致耗时长甚至应用卡死,如有相关操作可参考文档批量数据写数据库场景,且有建议如下:

  • 单次查询数据量不超过5000条。
  • TaskPool中查询。
  • 拼接SQL语句尽量简洁。
  • 合理地分批次查询。

基本概念

  • 谓词:数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。
  • 结果集:指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便地拿到用户想要的数据。

运作机制

关系型数据库对应用提供通用的操作接口,底层使用SQLite作为持久化存储引擎,支持SQLite具有的数据库特性,包括但不限于事务、索引、视图、触发器、外键、参数化查询和预编译SQL语句。

图1 关系型数据库运作机制

3d456a23eaba465cacea922c429131ff_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

约束限制

  • 系统默认日志方式是WAL(Write Ahead Log)模式,系统默认落盘方式是FULL模式。
  • 数据库中有4个读连接和1个写连接,线程获取到空闲读连接时,即可进行读取操作。当没有空闲读连接且有空闲写连接时,会将写连接当做读连接来使用。
  • 为保证数据的准确性,数据库同一时间只能支持一个写操作。
  • 当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除。
  • ArkTS侧支持的基本数据类型:number、string、二进制类型数据、boolean。
  • 为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。

具体实现

  • 插入数据

e50866cc02b34c24aca07bca9cbb1c40_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

81def1e765334ac09379ee3e9b938a29_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

  • 初始化数据库


async  initsqlite(){
  // 构造StoreConfig对象
  const STORE_CONFIG: relationalStore.StoreConfig = {
    name: 'RdbTest.db', // 数据库文件名
    securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
    encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
    customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
    isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
  };

  // 判断数据库版本,如果不匹配则需进行升降级操作
  // 默认数据库版本为0,表结构:CLIENT_USER (ID INTEGER PRIMARY KEY AUTOINCREMENT, ACCOUNT TEXT NOT NULL, PASSWORD TEXT NOT NULL)'
  const SQL_CREATE_TABLE =
    'CREATE TABLE IF NOT EXISTS CLIENT_USER (ID INTEGER PRIMARY KEY AUTOINCREMENT, ACCOUNT TEXT NOT NULL, PASSWORD TEXT NOT NULL)'; // 建表Sql语句

  // 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,
  // 然后通过RdbStore调用相关接口可以执行相关的数据操作,使用callback异步回调。
  relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
    if (err) {
      console.error(`At LoginPage Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
      return;
    }
    console.info('At LoginPage Succeeded in getting RdbStore.');
    // 当数据库创建时,数据库默认版本为0
    if (store.version === 0) {
      store.executeSql(SQL_CREATE_TABLE); // 创建数据表
      // 设置数据库的版本,入参为大于0的整数
      store.version = 1;
    }
    // 赋值给当前的rdbStore对象,确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
    this.rdbStore = store
  });
}
  • 插入数据

addUserInfo(){
  if (this.account === undefined || this.account === '') {
    console.info('请输入账号')
    return
  }
  if (this.password === undefined || this.password === '') {
    console.info('请输入密码')
    return
  }
  // 使用ArkData RDB数据库 以建表方式存储用户信息数据
  if (this.rdbStore !== undefined) {
    // 构造insert函数所需ValuesBucket对象
    const userInfo: relationalStore.ValuesBucket = {
      ACCOUNT: this.account,
      PASSWORD: this.password
    };
    // 插入数据到指定表中
    this.rdbStore.insert('CLIENT_USER', userInfo, (err: BusinessError, rowId: number) => {
      // 如果成功插入,则返回该行所在表中的行号,否则返回-1
      if (rowId === -1) {
        console.error(`At LoginPage,插入数据失败,错误代码:${err.code},错误提示:${err.message}`)
        return
      }
      console.info(`At LoginPage,插入数据成功,rowId为 ${rowId}`)
    })
  }
}

查询数据

  • 查询所有数据

f02227ea7aeb4ba5849e8be920b260e5_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

queryUserInfoData() {
  if(this.rdbStore!==undefined){
    // 测试查询出来看看
    //let clientUserPredicate = new relationalStore.RdbPredicates('CLIENT_USER') // CLIENT_USER 为表名称
    this.rdbStore.querySql('SELECT ID,ACCOUNT,PASSWORD FROM CLIENT_USER ORDER BY ID DESC;',
      (err: BusinessError, resultSet) => {
        if (err) {
          console.error(`LoginPage 读取数据库表失败,错误代码:${err.code},错误信息为:${err.message}`)
          return
        }
        console.info(`读取的结果为(对象形式): ${resultSet}`)
        // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
        while (resultSet.goToNextRow()) {
          const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
          const account = resultSet.getLong(resultSet.getColumnIndex('ACCOUNT'));
          const passwd = resultSet.getString(resultSet.getColumnIndex('PASSWORD'));
          console.info(`id=${id}, account=${account}, passwd=${passwd}`);
          let userInfo = new UserInfo(id, account.toString(), passwd)
          // if(this.listUserInfo===null||this.listUserInfo.length===0){
          //
          // }
          this.listUserInfo.push(userInfo)
        }
        // 释放数据集的内存
        resultSet.close();
      })
  }
}
  • 查询某个字段的数据

ea9a1a5a766b487c92460a74faf99d0d_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

yUserInfoByName(name:string){
  let predicates2 = new relationalStore.RdbPredicates('CLIENT_USER');
  predicates2.equalTo('ACCOUNT', name);
  if (this.rdbStore!=null||this.rdbStore!==undefined) {
    (this.rdbStore as relationalStore.RdbStore).query(predicates2, ['ID', 'ACCOUNT', 'PASSWORD' ], (err: BusinessError, resultSet) => {
      if (err) {
        console.error(`Failed to query data. Code:${err.code}, message:${err.message}`);
        return;
      }
      console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
      // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
      while (resultSet.goToNextRow()) {
        const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
        const account = resultSet.getLong(resultSet.getColumnIndex('ACCOUNT'));
        const passwd = resultSet.getString(resultSet.getColumnIndex('PASSWORD'));
        console.info(`id=${id}, account=${account}, passwd=${passwd}`);
        let userInfo = new UserInfo(id, account.toString(), passwd)
        this.listUserInfo.push(userInfo)
      }
      // 释放数据集的内存
      resultSet.close();
    })
  }
  
}

更新数据

  • 效果

c2dae826086d424f9dafbb1d158d7e27_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

updataUserInfo(){

  const userInfo: relationalStore.ValuesBucket = {
    ACCOUNT: "18674049006",
    PASSWORD: "1234562521"

  };
  let predicates1 = new relationalStore.RdbPredicates('CLIENT_USER'); // 创建表'EMPLOYEE'的predicates
  predicates1.equalTo('ACCOUNT', '18674049006'); // 匹配表'STORE_CONFIG'中'NAME'为'Lisa'的字段
  if (this.rdbStore !== undefined) {
    (this.rdbStore as relationalStore.RdbStore).update(userInfo, predicates1, (err: BusinessError, rows: number) => {
      if (err) {
        console.error(`Failed to update data. Code:${err.code}, message:${err.message}`);
        return;
      }
      console.info(`Succeeded in updating data. row count: ${rows}`);
    })
  }
  • 更新后查询数据

queryUserInfoData() {
 // 使用ArkData RDB数据库 获取用户数据
 // 构造StoreConfig对象
 const STORE_CONFIG: relationalStore.StoreConfig = {
   name: 'RdbTest.db', // 数据库文件名
   securityLevel: relationalStore.SecurityLevel.S1, // 数据库安全级别
   encrypt: false, // 可选参数,指定数据库是否加密,默认不加密
   customDir: 'customDir/subCustomDir', // 可选参数,数据库自定义路径。数据库将在如下的目录结构中被创建:context.databaseDir + '/rdb/' + customDir,其中context.databaseDir是应用沙箱对应的路径,'/rdb/'表示创建的是关系型数据库,customDir表示自定义的路径。当此参数不填时,默认在本应用沙箱目录下创建RdbStore实例。
   isReadOnly: false // 可选参数,指定数据库是否以只读方式打开。该参数默认为false,表示数据库可读可写。该参数为true时,只允许从数据库读取数据,不允许对数据库进行写操作,否则会返回错误码801。
 };

 // 获得一个相关的RdbStore,操作关系型数据库,用户可以根据自己的需求配置RdbStore的参数,然后通过RdbStore调用相关接口可以执行相关的数据操作,使用callback异步回调。
 relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
   if (err) {
     console.error(`At HomePage Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
     return;
   }
   console.info('At HomePage Succeeded in getting RdbStore.');
   // 当数据库创建时,数据库已更新版本为1
   // 赋值给当前的rdbStore对象,确保获取到RdbStore实例后,再进行数据库的增、删、改、查等操作
   if (store.version === 1) {
     this.rdbStore = store
     // 测试查询出来看看
     //let clientUserPredicate = new relationalStore.RdbPredicates('CLIENT_USER') // CLIENT_USER 为表名称
     this.rdbStore.querySql('SELECT ID,ACCOUNT,PASSWORD FROM CLIENT_USER ORDER BY ID DESC;',
       (err: BusinessError, resultSet) => {
         if (err) {
           console.error(`LoginPage 读取数据库表失败,错误代码:${err.code},错误信息为:${err.message}`)
           return
         }
         console.info(`读取的结果为(对象形式): ${resultSet}`)
         // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
         while (resultSet.goToNextRow()) {
           const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
           const account = resultSet.getLong(resultSet.getColumnIndex('ACCOUNT'));
           const passwd = resultSet.getString(resultSet.getColumnIndex('PASSWORD'));
           console.info(`id=${id}, account=${account}, passwd=${passwd}`);
           let userInfo = new UserInfo(id, account.toString(), passwd)
           // if(this.listUserInfo===null||this.listUserInfo.length===0){
           //
           // }
           this.listUserInfo.push(userInfo)
         }
         // 释放数据集的内存
         resultSet.close();
       })
   }
 });
}

删除数据

  • 删除操作

deleteUserInfo(){
  let predicates1 = new relationalStore.RdbPredicates('CLIENT_USER'); // 创建表'EMPLOYEE'的predicates
  predicates1.equalTo('ACCOUNT', '18674049006'); // 匹配表'STORE_CONFIG'中'NAME'为'Lisa'的字段
  if (this.rdbStore !== undefined) {
    (this.rdbStore as relationalStore.RdbStore).delete(predicates1, (err: BusinessError, rows: number) => {
      if (err) {
        console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`);
        return;
      }
      console.info(`Delete rows: ${rows}`);
    })
  }
}
  • 删除后查询数据

queryUserInfoData() {
   if(this.rdbStore!==undefined){
     // 测试查询出来看看
     //let clientUserPredicate = new relationalStore.RdbPredicates('CLIENT_USER') // CLIENT_USER 为表名称
     this.rdbStore.querySql('SELECT ID,ACCOUNT,PASSWORD FROM CLIENT_USER ORDER BY ID DESC;',
       (err: BusinessError, resultSet) => {
         if (err) {
           console.error(`LoginPage 读取数据库表失败,错误代码:${err.code},错误信息为:${err.message}`)
           return
         }
         console.info(`读取的结果为(对象形式): ${resultSet}`)
         // resultSet是一个数据集合的游标,默认指向第-1个记录,有效的数据从0开始。
         while (resultSet.goToNextRow()) {
           const id = resultSet.getLong(resultSet.getColumnIndex('ID'));
           const account = resultSet.getLong(resultSet.getColumnIndex('ACCOUNT'));
           const passwd = resultSet.getString(resultSet.getColumnIndex('PASSWORD'));
           console.info(`id=${id}, account=${account}, passwd=${passwd}`);
           let userInfo = new UserInfo(id, account.toString(), passwd)
           // if(this.listUserInfo===null||this.listUserInfo.length===0){
           //
           // }
           this.listUserInfo.push(userInfo)
         }
         // 释放数据集的内存
         resultSet.close();
       })
   }

}
  • 效果图

de828730487b450399cb6ed092984d90_tplv-73owjymdk6-jj-mark-v1_0_0_0_0_5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5Z2a5p6c5rS-X3hxOTUyNw==_q75

最后总结:

鸿蒙next中的 关系型数据库其实还是基于我们的sqlite ,轻量级数据库,我们在做数据持久化的时候需要选择适合我我们的,如果只是少量数据 。我们也可以选择用户首选项来存储数据库, 例如登录的账号 密码 token 等等。 如果数据量比较大,我们就要选择关系型数据库来进行存储做数据持久化。今天的文章就分享到这里,有兴趣的同学可以下载代码来继续研究。 今天的文章就讲到这里有兴趣的 关注我B站教程 了解更多鸿蒙开发的知识 可以关注坚果派公众号 。 谢谢

课程地址

www.bilibili.com/cheese/play…

项目内容:

  • 1 常用布局组件的学习
  • 2 网络请求工具类封装
  • 3 arkui 生命周期启动流程
  • 4 日志工具类的封装
  • 5 自定义组合组件的封装
  • 6 路由导航跳转的使用
  • 7 本地地数据的缓存 以及缓存工具类的封装
  • 8 欢迎页面的实现
  • 9 登录案例和自动登录效果实现
  • 10 请求网络数据分页上拉加载 下拉刷新的实现
  • 11 list数据懒加载实现
  • 12 webview组件的使用

如果使用更多好用的鸿蒙next三方库

友情链接

harmony-utils 一款功能丰富且极易上手的HarmonyOS工具库,借助众多实用工具类,致力于助力开发者迅速构建鸿蒙应用,能够满足各种不同的开发需求。

harmony-dialog 一款极为简单易用的零侵入弹窗,仅需一行代码即可轻松实现,无论在何处都能够轻松弹出。

收藏00

登录 后评论。没有帐号? 注册 一个。