【代码案例】HarmonyOS 鸿蒙Next在TaskPool线程中操作关系型数据库实现案例

发布于 1周前 作者 phonegap100 来自 鸿蒙OS

【代码案例】HarmonyOS 鸿蒙Next在TaskPool线程中操作关系型数据库实现案例

HarmonyOS Next应用开发案例(持续更新中……)

本案例完整代码,请访问:https://gitee.com/harmonyos-cases/cases/tree/master/CommonAppDevelopment/feature/perfermance/operaterdbintaskpool

本案例已上架 HarmonyOS NEXT 开源组件市场,如需获取或移植该案例,可安装此插件。开发者可使用插件获取鸿蒙组件,添加到业务代码中直接编译运行。

介绍

本示例通过通讯录场景实例进行讲解,介绍了在 TaskPool 线程中操作关系型数据库的方法,涵盖了单条插入(新增联系人)、批量插入(通讯录同步)、删除(删除联系人)、修改(更新联系人信息)和查询等基本操作。

效果图预览

通讯录场景实例

使用说明

  1. 首次进入页面,出现“点击同步通讯录数据”按钮。点击按钮后,将本地 JSON 数据分批插入数据库中。
  2. 同步完成后,页面显示通讯录列表。点击列表项进入联系人的详情页面,点击加号按钮进入新增联系人页面。
  3. 在详情页面,可以对联系人信息进行修改和删除操作。

实现思路

  1. 首先,构建一个关系型数据库并封装数据库操作方法涉及几个关键步骤。

    • 通过 getRdbStore 方法初始化一个关系型数据库,用户可以根据 STORE_CONFIG 配置 RdbStore 的参数,使用 Promise 异步回调。

      // 初始化数据库 
      public async initRdbStore(context: common.Context): Promise<void> {
        this.rdbStore = await rdb.getRdbStore(context, STORE_CONFIG);
        await this.createTable();
      }
      
    • 使用 executeSql 接口初始化数据库表结构和相关数据。

      // 创建数据库表
      private async createTable(): Promise<void> { 
        await this.rdbStore.executeSql(SQL_CREATE_TABLE);
      }
      
    • 封装数据库操作方法分别为数据插入、数据删除、数据查询和数据查询。

      // 单条数据插入数据库
      public async insertData(context: common.Context, Contact: Contact): Promise<void> {
        let value1 = Contact.name;
        let value2 = Contact.phone;
        let value3 = Contact.email;
        let value4 = Contact.address;
        let value5 = Contact.avatar;
        let value6 = Contact.category;
      
        const valueBucket: ValuesBucket = {
          'name': value1,
          'phone': value2,
          'email': value3,
          'address': value4,
          'avatar': value5,
          'category': value6
        }
        if (this.rdbStore != undefined) {
          let ret = await this.rdbStore.insert(TABLE_NAME, valueBucket, rdb.ConflictResolution.ON_CONFLICT_REPLACE);
        }
      }
      
      // 批量插入数据库
      public async batchInsertData(context: common.Context, array: Array<Contact>): Promise<void> {
        let valueBuckets: Array<ValuesBucket> = [];
        for (let index = 0; index < array.length; index++) {
          let contactItem = array[index] as Contact;
          let value1 = contactItem.name;
          let value2 = contactItem.phone;
          let value3 = contactItem.email;
          let value4 = contactItem.address;
          let value5 = contactItem.avatar;
          let value6 = contactItem.category;
      
          const valueBucket: ValuesBucket = {
            'name': value1,
            'phone': value2,
            'email': value3,
            'address': value4,
            'avatar': value5,
            'category': value6
          }
          valueBuckets.push(valueBucket);
        }
        if (this.rdbStore != undefined) {
          let ret = await this.rdbStore.batchInsert(TABLE_NAME, valueBuckets);
        }
      }
      
      // 删除操作
      public async deleteData(context: common.Context, Contact: Contact): Promise<void> { 
        this.rdbStore = await rdb.getRdbStore(context, STORE_CONFIG);
      
        predicates.or().equalTo('id', Contact.id);
        this.rdbStore.delete(predicates, (err: BusinessError, row: number) => {
          if (err) {
            logger.info(TAG, "delete failed, err: " + err);
            return;
          }
          logger.info(TAG, `delete contact success:${row}`);
          promptAction.showToast({
            message: $r('app.string.operate_rdb_in_taskpool_delete_prompt_text', Contact.name),
            duration: CommonConstants.PROMPT_DURATION_TIME
          });
        });
      }
      
      // 更新数据库
      public async updateData(context: common.Context, Contact: Contact): Promise<void> {
        logger.info(TAG, 'update begin');
        if (!context) {
          logger.info(TAG, 'context is null or undefined');
        }
      
        const predicates = new rdb.RdbPredicates(TABLE_NAME);
        if (predicates === null || predicates === undefined) {
          logger.info(TAG, 'predicates is null or undefined');
        }
      
        if (!this.rdbStore) {
          logger.info(TAG, 'update rdbStore is null');
          await this.initRdbStore(context);
        }
        let value1 = Contact.name;
        let value2 = Contact.phone;
        let value3 = Contact.email;
        let value4 = Contact.address;
        let value5 = Contact.avatar;
        let value6 = Contact.category;
      
        const valueBucket: ValuesBucket = {
          'name': value1,
          'phone': value2,
          'email': value3,
          'address': value4,
          'avatar': value5,
          'category': value6
        }
      
        predicates.equalTo("id", Contact.id);
      
        if (this.rdbStore != undefined) {
          this.rdbStore.update(valueBucket, predicates, rdb.ConflictResolution.ON_CONFLICT_REPLACE,
            (err: BusinessError, row: number) => {
              if (err) {
                logger.info(TAG, "updated failed, err: " + err)
                return
              }
              logger.info(TAG, `update done:${row}`);
              promptAction.showToast({
                message: $r('app.string.operate_rdb_in_taskpool_update_prompt_text', Contact.name),
                duration: CommonConstants.PROMPT_DURATION_TIME
              });
            })
        }
      }
      
      // 查询数据库
      public async query(context: common.Context): Promise<Array<Contact>> {
        if (!context) {
          logger.info(TAG, 'context is null or undefined');
          return [];
        }
      
        let predicates = new rdb.RdbPredicates(TABLE_NAME);
        predicates.orderByAsc("category")
        if (predicates === null || predicates === undefined) {
          logger.info(TAG, 'predicates is null or undefined');
          return [];
        }
      
        try {
          this.rdbStore = await rdb.getRdbStore(context, STORE_CONFIG);
          const resultSet: rdb.ResultSet =
            await this.rdbStore.query(predicates);
          logger.info(TAG, 'result is ' + JSON.stringify(resultSet.rowCount));
          // 处理查询到的结果数组
          return this.getListFromResultSet(resultSet);
        } catch (err) {
          logger.error(TAG, 'query result error:' + JSON.stringify(err));
          return [];
        }
      }
      
  2. 创建任务池(taskpool)为数据库操作提供一个多线程的运行环境,将创建好的任务(新增、删除、修改、查询操作)放入taskpool内部任务队列,在子线程中实现数据库增删改查的任务,以此防止阻塞主线程。执行完成后,将结果回调至主线程,从而在主线程中更新数据源和用户界面。这样做不仅提升了应用的响应速度,还确保了用户交互的流畅性。以下代码以查询为例:(注:任务不会立即执行,而是等待分发到工作线程执行。)

    // queryItem函数调用 需使用装饰器[@Concurrent](/user/Concurrent)
    [@Concurrent](/user/Concurrent)
    async function queryItem(context: common.Context): Promise<Array<Contact>> {
      return await DatabaseConnection.getInstance().query(context);
    }
    
    export async function taskPoolExecuteQuery(context: common.Context): Promise<Array<Contact>> {
      try {
        let task: taskPool.Task = new taskPool.Task(queryItem, context); // queryItem函数调用 需使用装饰器[@Concurrent](/user/Concurrent)
        let result: Array<Contact> = await taskPool.execute(task) as Array<Contact>;
        return result;
      } catch (err) {
        logger.error(TAG, 'query error:' + JSON.stringify(err));
        return [];
      }
    }
    
  3. 在taskpool线程中操作关系型数据库方法的调用,将结果回调至主线程,在回调中来操作数据源。

    // 单条数据插入操作
    taskPoolExecuteInsert(context, this.result).then(() => {
        DynamicsRouter.popAppRouter();
        // 数据库插入成功后 操作列表数据源回调
        this.addCallback(this.result);
    });
    
    // 数据删除操作
    taskPoolExecuteDelete(context, this.contact).then(() => {
        if (this.sourceData) {
            // 数据库删除成功后 操作列表数据源
            DynamicsRouter.popAppRouter();
            this.deleteCallback(this.sourceData);
        }
    
    // 更新数据操作
    taskPoolExecuteUpdate(context, this.result).then(() => {
        DynamicsRouter.popAppRouter();
        // 数据库更新成功后 操作列表数据源回调
        this.editCallback(this.result);
    });
    
    // 数据查询操作
    queryRDB() {
        taskPoolExecuteQuery(context).then((contact: Array<Contact>) => {
            this.dataArray = contact.reduce((accumulator, item) => {
                // 如果类别不存在,则创建一个新的数组
                if (!accumulator[item.category]) {
                    accumulator[item.category] = [];
                }
                // 将当前项添加到相应类别的数组中
                accumulator[item.category].push(item);
                return accumulator;
            }, {} as Record<string, Contact[]>);
    
            // 清空类别数组
            this.categoryArray = [];
    
            // 使用 Object.entries() 遍历键值对
            Object.entries(this.dataArray).forEach(data => {
                let categoryContact: CategoryContact = { category: data[0], itemsContact: data[1] }
                this.categoryArray.push(data[0]);
                this.sourceArray.pushData(categoryContact);
            });
        });
    }
    

高性能知识点

本示例使用了 LazyForEach 进行数据懒加载,LazyForEach懒加载可以通过设置 cachedCount 属性来指定缓存数量,同时搭配 组件复用 能力以达到性能最优效果。

工程结构 & 模块类型

operaterdbintaskpool                             // har类型
|---constant
|   |---CommonConstant.ets                       // 常量
|   |---RdbConstant.ets                          // Rdb常量
|---model
|   |---Contact.ets                              // Contact数据结构
|   |---DataSource.ets                           // 解析JSON数据 
|   |---DataSource.ets                           // 列表数据模型
|---view
|   |---AddressBookDetail.ets                    // 通讯录详情页
|   |---AddressBookEdit.ets                      // 通讯录编辑和新增页
|   |---AddressBookList.ets                      // 通讯录列表页
|   |---DatabaseConnection.ets                   // 数据库相关操作
|   |---OpetateRDBTaskPool.ets                   // 主页面
|   |---TaskPool.ets                             // TaskPool线程

模块依赖

本实例依赖 common 模块来实现 资源 的调用以及路由模块来 注册路由

参考资料


更多关于【代码案例】HarmonyOS 鸿蒙Next在TaskPool线程中操作关系型数据库实现案例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复

怎么解决同步问题和序列化问题呢?

序列化的大小也是有限制的。

我觉得这个demo应该最主要解决这两个问题呀,使用方法的多看文档还是可以解决的;

现在这两个问题不知道怎么解决,或者有没有比较简单的解决方法,否则异步线程感觉就是一个鸡肋。

更多关于【代码案例】HarmonyOS 鸿蒙Next在TaskPool线程中操作关系型数据库实现案例的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


hello,你的序列化问题解决了?

12版本之上提供了Sendable 这样的注解,可以解决在ArkTS里面的线程中的序列化,在加新增了相关的锁机制;
或者是使用仓颉开发未来,这个是线程支持的同步的;
或者是通过文件来同步,使用Sendable注解状态加锁机制;

锁机制,指的是AsyncLock么? 类似java里的同步锁,

在HarmonyOS鸿蒙Next中,若要在TaskPool线程中操作关系型数据库,你通常需要使用鸿蒙系统提供的异步编程模型和数据库访问接口。以下是一个简化的代码示例,展示如何在TaskPool中执行数据库操作,但请注意,具体API可能会根据鸿蒙系统的版本和具体数据库实现有所不同。

#include <task_pool.h>
#include <database_api.h> // 假设这是鸿蒙提供的数据库API头文件

void DatabaseOperationTask(void* param) {
    DatabaseConnection* conn = static_cast<DatabaseConnection*>(param);
    // 执行数据库操作,如查询、插入等
    DatabaseResult result = ExecuteDatabaseQuery(conn, "SELECT * FROM some_table");
    // 处理数据库操作结果
    if (result.isSuccess()) {
        // 处理查询结果
    } else {
        // 处理错误
    }
    // 关闭数据库连接
    CloseDatabaseConnection(conn);
}

int main() {
    DatabaseConnection conn = OpenDatabaseConnection("database_url");
    TaskPoolHandle pool = CreateTaskPool();
    DispatchToTaskPool(pool, DatabaseOperationTask, &conn);
    DestroyTaskPool(pool);
    return 0;
}

注意:

  • 上述代码是一个概念性示例,实际鸿蒙系统中的数据库API和任务池API可能与示例中的不同。
  • 确保在操作完成后正确关闭数据库连接,以避免资源泄漏。
  • 鸿蒙系统的异步编程模型可能要求使用特定的回调机制或Promise/Future模式来处理异步操作的结果。

如果问题依旧没法解决请联系官网客服,官网地址是 https://www.itying.com/category-93-b0.html

回到顶部