HarmonyOS NEXT通过关系型数据库实现数据的持久化

news/2025/2/25 12:31:20

场景介绍

应用中,我们有些数据比较复杂,而且需要频繁的增删改查,这时候就不适合使用首选项来进行存储和管理了,HarmonyOS NEXT中的关系型数据库基于SQLite组件,适用于存储包含复杂关系数据。

基本概念

  • 谓词数据库中用来代表数据实体的性质、特征或者数据实体之间关系的词项,主要用来定义数据库的操作条件。

  • 结果集:指用户查询之后的结果集合,可以对数据进行访问。结果集提供了灵活的数据访问方式,可以更方便地拿到用户想要的数据

运作机制

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

约束限制

  • 系统默认日志方式是WAL(Write Ahead Log)模式,系统默认落盘方式是FULL模式。

  • 数据库中有4个读连接和1个写连接,线程获取到空闲读连接时,即可进行读取操作。当没有空闲读连接且有空闲写连接时,会将写连接当做读连接来使用。

  • 为保证数据的准确性,数据库同一时间只能支持一个写操作。

  • 当应用被卸载完成后,设备上的相关数据库文件及临时文件会被自动清除。

  • ArkTS侧支持的基本数据类型:number、string、二进制类型数据、boolean。

  • 为保证插入并读取数据成功,建议一条数据不要超过2M。超出该大小,插入成功,读取失败。

开发步骤

  1. 通过getRdbStore获取一个relationalStore.RdbStore类型的对象,用于管理数据库,对数据库的建库,建表,升降级等操作。
  2. 获取到RdbStore后可以调用execute方法创建表,execute接收一条SQL语句。
  3. 调用insert方法插入一条数据或者使用batchInsert批量插入数据。
  4. 根据谓词指定的实例对象,对数据进行修改或删除。
  5. 使用ResultSet获取执行完操作的查询结果。

1.创建RdbStore类型的对象,用于对数据库的建库,建表,升降级等操作

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

    };

    relationalStore.getRdbStore(context, STORE_CONFIG, (error: BusinessError, store: relationalStore.RdbStore) => {
      this.appRDB = store;
      if (error) {
        hilog.error(0x0000, TAG, `Get RdbStore failed, code is ${error.code}, message is ${error.message}`);
        return;
      }
      if (store.version === 0) {
        //创建表
        this.createUserTable();
        store.version = 1;
      }
    });
  }
}

2.通过execute创建表

  // 创建用户表
  createUserTable() {
    this.appRDB?.execute(CommonConstants.CREATE_USER_TABLE_SQL).then(() => {
      hilog.info(0x0000, TAG, `execute create user table sql success`);
      //初始化表
      this.initTable();
    }).catch((error: BusinessError) => {
      hilog.error(0x0000, TAG, `execute sql failed, code is ${error.code}, message is ${error.message}`);
    });
  }

3.调用insert方法插入一条数据或者使用batchInsert批量插入数据

假设需要创建一个学生数据表,刚刚建表完成后,我们现在批量插入一批数据,批量插入需要传入表名,和一个List<ValuesBucket>类型的参数,代码如下:

//初始化表
  initTable() {
    const user1: relationalStore.ValuesBucket = {
      'ID': 0,
      'NAME': 'Jack',
      'AGE': 18,
      'SCORE': 90,
    }
    const user2: relationalStore.ValuesBucket = {
      'ID': 1,
      'NAME': 'Tonny',
      'AGE': 19,
      'SCORE': 100,
    }
    const user3: relationalStore.ValuesBucket = {
      'ID': 2,
      'NAME': 'kirk',
      'AGE': 20,
      'SCORE': 60,
    }
    let valueBuckets = new Array(user1, user2, user3);
    //向表中批量添加数据,关系型数据库没有显式的flush操作实现持久化,数据插入即保存在持久化文件。
    this.appRDB?.batchInsert('USER', valueBuckets).then((insertNum: number) => {
      hilog.info(0x0000, TAG, `Insert is successful, rows number : ${insertNum}`);
    }).catch((error: BusinessError) => {
      hilog.error(0x0000, TAG, `Insert is failed, code is ${error.code},message is ${error.message}`);
    })
  }

如果是插入单挑数据可以用insert,代码如下:

  //插入一条数据
  async insertUser() {
    const user: relationalStore.ValuesBucket = {
      'NAME': 'mutang',
      'AGE': 8,
      'SCORE': 100
    };
    await this.appRDB?.insert('USER', user,
      relationalStore.ConflictResolution.ON_CONFLICT_REPLACE).then((rowId: number) => {
      hilog.info(0x0000, TAG, `Insert is successful, rowId = ${rowId}`);
    }).catch((error: BusinessError) => {
      hilog.error(0x0000, TAG, `Insert is failed, code is ${error.code},message is ${error.message}`);
    })
  }

4.根据谓词指定的实例对象,对数据进行修改或删除

首先使用RdbPredicates确定数据库操作条件,然后根据筛选条件来执行修改或者删除

 async updateUser(id:number,score:number){
    const tempDate:relationalStore.ValuesBucket ={
      'SCORE':score
    }
    //使用RdbPredicates确定数据库操作条件
    let predicates = new relationalStore.RdbPredicates('USER');
    predicates.equalTo('ID',id);
    //4.执行数据库操作
    this.appRDB?.update(tempDate,predicates,relationalStore.ConflictResolution.ON_CONFLICT_REPLACE).then(async (rows:Number)=>{
      hilog.info(0x0000, TAG, `Updated row count: ${rows}`);
    }).catch((error:BusinessError)=>{

    })
  }
  async deletePlan(planID: number) {
    //使用RdbPredicates确定数据库操作条件
    let predicates = new relationalStore.RdbPredicates('USER');
    predicates.equalTo('ID', planID);
    await this.appRDB?.delete(predicates).then((rows: Number) => {
      hilog.info(0x0000, TAG, `Delete rows: ${rows}`);
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, TAG, `Delete failed, code is ${err.code},message is ${err.message}`);
    })
  }

5.使用ResultSet获取执行完操作的查询结果

查询数据库表大概步骤如下:

  1. 在回调函数中会返回一个ResultSet类型的值。
  2. 我们可以通过goToNextRow进行遍历,获取到一行数据后,可以使用getColumnIndex()方法,参数传入数据表中的字段名,即可获取到该字段名对应的索引值,然后通过getValue()方法传入字段索引,即可获取到该字段名对应的值,getValue()的返回值类型是ValueType,我们可以将其强制类型转化为具体的数据类型。
  3. 再将取出的数据保存至实体类中,将所有的实体类对象存储在实体类数组中。
  4. 关闭ResultSet,释放资源并返回实体类数组。
//查询所有用户名数据
  async queryAllUsers(): Promise<Userinfo[]> {
    let userList: Array<Userinfo> = [];
    //查询数据
   await this.appRDB?.querySql(CommonConstants.QUERY_ALL_NAME_SQL).then((resultSet: relationalStore.ResultSet) => {
      //使用ResultSet获取执行完操作的查询结果
      while (resultSet.goToNextRow()) {
        const id = resultSet.getValue(resultSet.getColumnIndex('ID')) as number;
        const name = resultSet.getValue(resultSet.getColumnIndex('NAME')) as string;
        const age = resultSet.getValue(resultSet.getColumnIndex('AGE')) as number;
        const score = resultSet.getValue(resultSet.getColumnIndex('SCORE')) as number;
        //将所有的实体类对象存储在实体类数组中
        userList.push(new Userinfo(id, name, age,score));
      }
      //关闭ResultSet,释放资源
      resultSet.close();
    },(error:BusinessError)=>{
      hilog.error(0x0000, TAG, `Query failed, code is ${error.code},message is ${error.message}`);
    })
    //将该实体类数组作为该方法的返回值返回
    return userList;
  }

项目实践

实践背景:现有一批学员信息,包含学员姓名,年龄,成绩。满足对学员信息的增删改查

首先在UIAbliity中初始化数据库

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import DBUtils from '../utils/DBUtils'
export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
    });
    //创建数据库
    DBUtils.creatRDB(this.context)
  }

  onWindowStageDestroy(): void {
    // Main window is destroyed, release UI related resources
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    // Ability has brought to foreground
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    // Ability has back to background
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
  }
}

 上面的开发步骤中,其实已经将大部分功能完成了,我们接下来要做的就是页面该如何调用,以及效果展示

在开发步骤中,获取到RdbStore对象后进行了创建表,而后初始化了数据,所以我们UI展示只需要调用查询所有数据的方法:

@State plansSet: Array<Userinfo> = [];

  async aboutToAppear(): Promise<void> {
    await DBUtils.queryAllUsers().then((value) => {
      this.plansSet = value;
    });
  }

页面效果如下:

点击新增学员,这里为了方便,我就不做编辑信息的UI了 

 Button('新增学员')
          .width('100%')
          .margin({ bottom: '10vp' }).onClick(async() => {
          await DBUtils.insertUser().then(() => {
            DBUtils.queryAllUsers().then((value) => {
              this.plansSet = value;
            });
          });
        })

执行完插入操作后调用查询所有信息的方法,最新的结果会更新在UI上,效果如下:

修改:修改学员ID为0的成绩为100分:

Button('修改第一名学员成绩为100分')
          .width('100%')
          .margin({ bottom: '10vp' }).onClick(async() => {
          await DBUtils.updateUser(0,100).then(() => {
            DBUtils.queryAllUsers().then((value) => {
              this.plansSet = value;
            });
          });
        })

执行效果如下:

删除:

 await DBUtils.deletePlan(0).then(() => {
            DBUtils.queryAllUsers().then((value) => {
              this.plansSet = value;
            });
          });

效果如下:

总结

鸿蒙关系型数据库的开发步骤和简单使用就到这了,还有更多的API和高阶使用请参考文档


http://www.niftyadmin.cn/n/5865505.html

相关文章

基于TensorFlow.js与Web Worker的智能证件照生成方案

功能简介 本文基于TensorFlow.js与Web Worker实现了常用的“证件照”功能&#xff0c;可以对照片实现抠图并替换背景。值得一提的是&#xff0c;正常抠图的操作应该由后端进行&#xff0c;这里只是主要演示该功能实现步骤&#xff0c;并不建议该功能由前端全权处理。 限于个人技…

星环科技推出DeepSeek全场景解决方案:即开即用、企业级部署、端侧智能三位一体

星环科技&#xff08;688031.SH&#xff09;正式发布DeepSeek全场景解决方案&#xff0c;全面覆盖个人用户、企业客户及行业场景需求&#xff0c;为用户提供从个人到企业、从云端到本地的全方位AI应用支持&#xff0c;为不同需求的用户提供了灵活、高效且安全的AI解决方案。 省…

Java 实现快速排序算法:一条快速通道,分而治之

大家好&#xff0c;今天我们来聊聊快速排序&#xff08;QuickSort&#xff09;算法&#xff0c;这个经典的排序算法被广泛应用于各种需要高效排序的场景。作为一种分治法&#xff08;Divide and Conquer&#xff09;算法&#xff0c;快速排序的效率在平均情况下非常高&#xff…

【qt链接mysql】

首先根据自己qtcreater 下载mysql安装包 将mysql安装目录下的如下目录中的xxx\MySQL\MySQL Server 5.7\lib\libmysql.dll 拷贝到QT目录C:\Qt\5.7\mingw53_32\bin 下&#xff08;当前这个也是我电脑上的Qt路径&#xff0c;请找到你Qt对应的bin路径&#xff09; 直接在文win11上…

【MySQL】表的增删查改(CRUD)(上)

个人主页&#xff1a;♡喜欢做梦 欢迎 &#x1f44d;点赞 ➕关注 ❤️收藏 &#x1f4ac;评论 CRUD&#xff1a;Create&#xff08;新增数据&#xff09;、Retrieve&#xff08;查询数据&#xff09;、Update&#xff08;修改数据&#xff09;、Delete&#xff08;修改数据…

第46天:Web开发-JavaEE应用原生和FastJson反序列化URLDNS链JDBC链Gadget手搓

#知识点 1、安全开发-JavaEE-原生序列化-URLDNS链分析 2、安全开发-JavaEE-FastJson-JdbcRowSetImpl链分析 一、利用链 利用链也叫"gadget chains"&#xff0c;我们通常称为gadget&#xff1a; 1、共同条件&#xff1a;实现Serializable或者Externalizable接口&#…

快速上手 Unstructured:安装、Docker部署及PDF文档解析示例

1. 核心概念 1.1 Unstructured简介 Unstructured 是一个强大的 Python 库,专注于从非结构化数据中提取和预处理文本信息,广泛应用于 PDF、Word 文档、HTML 等多种格式的文件处理。其核心功能包括分区、清理、暂存和分块,能够将复杂的非结构化文档转换为结构化输出,为后续…

不停机数据库迁移方案

首先我们需要知道一个基本的数据迁移的方案: 创建一个目标表使用源表的数据去初始化目标表执行一次校验, 此时使用源表数据去修复目标表数据双写&#xff0c; 业务开启双写, 读写源表, 写目标表开启增量校验和数据修复, 保持一段时间切换双写顺序, 此时读写目标表, 数据以目标…