【鸿蒙HarmonyOS开发笔记】通用型工具封装之关系型数据库操作类的封装

news/2024/7/21 8:50:10 标签: harmonyos, 笔记, 数据库

概述

开发中难免遇到操作关系型数据库的场景,但是原生的relationalStore使用起来略显繁琐,此文封装一个通用的关系型数据库增删改查的工具类,只需要少量修改配置即可使用,大幅简化我们的开发成本,提高开发效率

完整代码在文章结尾,拿代码直接划到结尾即可


1.封装初始化数据库函数与建表函数

createTable函数只需要传入建表SQL语句即可,需要自行准备哦

//  操作的数据库名称,
const DB_FILENAME: string = 'OliannaWen.db'

class DbUtil {
  // 使用变量来获取关系型数据库操作对象
  rdbStore: relationalStore.RdbStore


  // 初始化数据库
  initDB(context: common.UIAbilityContext): Promise<void> {
    let config: relationalStore.StoreConfig = {
      // 数据库名称
      name: DB_FILENAME,
      // 数据库操作安全等级
      securityLevel: relationalStore.SecurityLevel.S1
    }
    return new Promise<void>((resolve, reject) => {
      // 获取关系型数据库操作对象
      relationalStore.getRdbStore(context, config)
        .then(rdbStore => {
          this.rdbStore = rdbStore
          // 记录日志
          Logger.debug('rdbStore 初始化完成!')
          resolve()
        })
        .catch(reason => {
          Logger.debug('rdbStore 初始化异常', JSON.stringify(reason))
          reject(reason)
        })
    })
  }
  // 创建表函数,传入创建表语句
  createTable(createSQL: string): Promise<void> {
    return new Promise((resolve, reject) => {
      this.rdbStore.executeSql(createSQL)
        .then(() => {
          Logger.debug('创建表成功', createSQL)
          resolve()
        })
        .catch(err => {
          Logger.error('创建表失败,' + err.message, JSON.stringify(err))
          reject(err)
        })
    })
  }

一般来讲,我们都会在EntryAbility.ets中去初始化数据库,调用DbUtil.initDB传入context即可

import UIAbility from '@ohos.app.ability.UIAbility';
import hilog from '@ohos.hilog';
import window from '@ohos.window';
import DbUtil from '../common/utils/DbUtil';
import RecordModel from '../model/RecordModel'
import PreferenceUtyil from "../common/utils/PreferenceUtil"

export default class EntryAbility extends UIAbility {
  async  onCreate(want, launchParam) {
    // 加载用户首选项
    PreferenceUtyil.loadPreference(this.context)


    // 初始化RDB工具
    await DbUtil.initDB(this.context)
    // 创建表

    DbUtil.createTable(RecordModel.getCreateTableSql())
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  }

新增方法

一般来说,我们实际开发使用的实体字段和数据库并不一定是一致的,所以需要先构建映射关系来保证正确插入

export interface ColumnInfo{
  // 实体字段
  name: string
  // 映射到数据库对应的字段
  columnName: string
  // 数据库字段类型
  type: ColumnType
}

export enum ColumnType{
  LONG,
  DOUBLE,
  STRING,
  BLOB
}

使用前需要自行准备映射关系,例如

const COLUMNS: ColumnInfo[] = [
  {name: 'id', columnName: 'id', type: ColumnType.LONG},
  {name: 'typeId', columnName: 'type_id', type: ColumnType.LONG},
  {name: 'itemId', columnName: 'item_id', type: ColumnType.LONG},
  {name: 'amount', columnName: 'amount', type: ColumnType.DOUBLE},
  {name: 'createTime', columnName: 'create_time', type: ColumnType.LONG}
]

然后我们实现新增的通用方法

  // 建立insert方法的映射关系(实体数据插入到数据库的字段映射)
  buildValueBucket(obj: any, columns: ColumnInfo[]): relationalStore.ValuesBucket {
    let value = {}
    columns.forEach(info => {
      let val = obj[info.name]
      if (typeof val !== 'undefined') {
        value[info.columnName] = val
      }
    })
    return value
  }


  // 新增方法,参数为表名称和新增对象
  insert(tableName: string, obj: any, columns: ColumnInfo[]): Promise<number> {
    return new Promise((resolve, reject) => {
      // 1.构建新增数据
      let value = this.buildValueBucket(obj, columns)
      // 2.新增
      this.rdbStore.insert(tableName, value, (err, id) => {
        if (err) {
          Logger.error('新增失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('新增成功!新增id:', id.toString())
          resolve(id)
        }
      })
    })
  }

删除方法比较简单,不做展开

  // 删除方法,传入删除条件
  delete(predicates: relationalStore.RdbPredicates): Promise<number> {
    return new Promise((resolve, reject) => {
      this.rdbStore.delete(predicates, (err, rows) => {
        if (err) {
          Logger.error('删除失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('删除成功!删除行数:', rows.toString())
          resolve(rows)
        }
      })
    })
  }

查询方法

查询方法略微麻烦,我们先按照条件查询出目标,因为relationalStore返回的是结果集,所以我们需要自己再解析一边,代码如下

  
  // 查询方法,传入查询条件,字段,返回解析后的结果
  queryForList<T>(predicates: relationalStore.RdbPredicates, columns: ColumnInfo[]): Promise<T[]> {
    return new Promise((resolve, reject) => {
      this.rdbStore.query(predicates, columns.map(info => info.columnName), (err, result) => {
        if (err) {
          Logger.error('查询失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('查询成功!查询行数:', result.rowCount.toString())
          resolve(this.parseResultSet(result, columns))
        }
      })
    })
  }

  // 解析结果集
  parseResultSet<T> (result: relationalStore.ResultSet, columns: ColumnInfo[]): T[] {
    // 1.声明最终返回的结果
    let arr = []
    // 2.判断是否有结果
    if (result.rowCount <= 0) {
      return arr
    }
    // 3.处理结果
    while (!result.isAtLastRow) {
      // 3.1.去下一行
      result.goToNextRow()
      // 3.2.解析这行数据,转为对象
      let obj = {}
      columns.forEach(info => {
        let val = null
        switch (info.type) {
          case ColumnType.LONG:
            val = result.getLong(result.getColumnIndex(info.columnName))
            break
          case ColumnType.DOUBLE:
            val = result.getDouble(result.getColumnIndex(info.columnName))
            break
          case ColumnType.STRING:
            val = result.getString(result.getColumnIndex(info.columnName))
            break
          case ColumnType.BLOB:
            val = result.getBlob(result.getColumnIndex(info.columnName))
            break
        }
        obj[info.name] = val
      })
      // 3.3.将对象填入结果数组
      arr.push(obj)
      Logger.debug('查询到数据:', JSON.stringify(obj))
    }
    return arr
  }


完整代码

使用时修改 DB_FILENAME ,并且提供实体数据字段与数据库字段的映射关系数组

DbUtil.ts

import common from '@ohos.app.ability.common';
import relationalStore from '@ohos.data.relationalStore';
import { ColumnInfo, ColumnType } from '../bean/ColumnInfo';
import Logger from './Logger';


//  操作的数据库名称
const DB_FILENAME: string = 'OliannaWen.db'

class DbUtil {
  // 使用变量来获取关系型数据库操作对象
  rdbStore: relationalStore.RdbStore


  // 初始化数据库
  initDB(context: common.UIAbilityContext): Promise<void> {
    let config: relationalStore.StoreConfig = {
      // 数据库名称
      name: DB_FILENAME,
      // 数据库操作安全等级
      securityLevel: relationalStore.SecurityLevel.S1
    }
    return new Promise<void>((resolve, reject) => {
      // 获取关系型数据库操作对象
      relationalStore.getRdbStore(context, config)
        .then(rdbStore => {
          this.rdbStore = rdbStore
          // 记录日志
          Logger.debug('rdbStore 初始化完成!')
          resolve()
        })
        .catch(reason => {
          Logger.debug('rdbStore 初始化异常', JSON.stringify(reason))
          reject(reason)
        })
    })
  }
  // 创建表函数,传入创建表语句
  createTable(createSQL: string): Promise<void> {
    return new Promise((resolve, reject) => {
      this.rdbStore.executeSql(createSQL)
        .then(() => {
          Logger.debug('创建表成功', createSQL)
          resolve()
        })
        .catch(err => {
          Logger.error('创建表失败,' + err.message, JSON.stringify(err))
          reject(err)
        })
    })
  }
  // 建立insert方法的映射关系(实体数据插入到数据库的字段映射)
  buildValueBucket(obj: any, columns: ColumnInfo[]): relationalStore.ValuesBucket {
    let value = {}
    columns.forEach(info => {
      let val = obj[info.name]
      if (typeof val !== 'undefined') {
        value[info.columnName] = val
      }
    })
    return value
  }


  // 新增方法,参数为表名称和新增对象
  insert(tableName: string, obj: any, columns: ColumnInfo[]): Promise<number> {
    return new Promise((resolve, reject) => {
      // 1.构建新增数据
      let value = this.buildValueBucket(obj, columns)
      // 2.新增
      this.rdbStore.insert(tableName, value, (err, id) => {
        if (err) {
          Logger.error('新增失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('新增成功!新增id:', id.toString())
          resolve(id)
        }
      })
    })
  }

  // 删除方法,传入删除条件
  delete(predicates: relationalStore.RdbPredicates): Promise<number> {
    return new Promise((resolve, reject) => {
      this.rdbStore.delete(predicates, (err, rows) => {
        if (err) {
          Logger.error('删除失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('删除成功!删除行数:', rows.toString())
          resolve(rows)
        }
      })
    })
  }

  // 查询方法,传入查询条件,字段,返回结果
  queryForList<T>(predicates: relationalStore.RdbPredicates, columns: ColumnInfo[]): Promise<T[]> {
    return new Promise((resolve, reject) => {
      this.rdbStore.query(predicates, columns.map(info => info.columnName), (err, result) => {
        if (err) {
          Logger.error('查询失败!', JSON.stringify(err))
          reject(err)
        } else {
          Logger.debug('查询成功!查询行数:', result.rowCount.toString())
          resolve(this.parseResultSet(result, columns))
        }
      })
    })
  }

  // 解析结果集
  parseResultSet<T> (result: relationalStore.ResultSet, columns: ColumnInfo[]): T[] {
    // 1.声明最终返回的结果
    let arr = []
    // 2.判断是否有结果
    if (result.rowCount <= 0) {
      return arr
    }
    // 3.处理结果
    while (!result.isAtLastRow) {
      // 3.1.去下一行
      result.goToNextRow()
      // 3.2.解析这行数据,转为对象
      let obj = {}
      columns.forEach(info => {
        let val = null
        switch (info.type) {
          case ColumnType.LONG:
            val = result.getLong(result.getColumnIndex(info.columnName))
            break
          case ColumnType.DOUBLE:
            val = result.getDouble(result.getColumnIndex(info.columnName))
            break
          case ColumnType.STRING:
            val = result.getString(result.getColumnIndex(info.columnName))
            break
          case ColumnType.BLOB:
            val = result.getBlob(result.getColumnIndex(info.columnName))
            break
        }
        obj[info.name] = val
      })
      // 3.3.将对象填入结果数组
      arr.push(obj)
      Logger.debug('查询到数据:', JSON.stringify(obj))
    }
    return arr
  }


}


let dbUtil: DbUtil = new DbUtil();

export default dbUtil as DbUtil

ColumnInfo.ts

export interface ColumnInfo{
  // 实体字段
  name: string
  // 映射到数据库对应的字段
  columnName: string
  // 数据库字段类型
  type: ColumnType
}

export enum ColumnType{
  LONG,
  DOUBLE,
  STRING,
  BLOB
}

Logger.ts

import hilog from '@ohos.hilog';

const LOGGER_PREFIX: string = 'OliannaWen.db';

class Logger {
  private domain: number;
  private prefix: string;

  // format Indicates the log format string.
  private format: string = '%{public}s, %{public}s';

  /**
   * constructor.
   *
   * @param prefix Identifies the log tag.
   * @param domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF
   * @param args Indicates the log parameters.
   */
  constructor(prefix: string = '', domain: number = 0xFF00) {
    this.prefix = prefix;
    this.domain = domain;
  }

  debug(...args: string[]): void {
    hilog.debug(this.domain, this.prefix, this.format, args);
  }

  info(...args: string[]): void {
    hilog.info(this.domain, this.prefix, this.format, args);
  }

  warn(...args: string[]): void {
    hilog.warn(this.domain, this.prefix, this.format, args);
  }

  error(...args: string[]): void {
    hilog.error(this.domain, this.prefix, this.format, args);
  }
}

export default new Logger(LOGGER_PREFIX, 0xFF02);

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

相关文章

Predict the Next “X” ,第四范式发布先知AIOS 5.0

今天&#xff0c;第四范式发布了先知AIOS 5.0&#xff0c;一款全新的行业大模型平台。 大语言模型的原理是根据历史单词去不断预测下一个单词&#xff0c;换一句常见的话&#xff1a;Predict the Next “Word”。 当前对于行业大模型的普遍认知就是沿用这种逻辑&#xff0c;用大…

并发编程之读写锁ReadWriteLock的详细解析(带小案例)

读写锁ReadWriteLock lock锁&#xff0c;只允许一个进程进行读/写 使用ReadWriteLock读写锁&#xff0c;可以实现控制&#xff1a; 多个线程同时读 同一时间只允许一个线程写 下面的demo展示的是&#xff0c;当多个线程要进行同时写操作时&#xff0c;没有加锁的情况&#xf…

git 查看文件夹结构树

在Git中&#xff0c;没有直接的命令可以像文件系统那样展示一个可视化的文件结构树。但是&#xff0c;你可以使用一些外部工具或命令来达到这个目的。 以下是一些方法&#xff0c;你可以使用它们来查看Git仓库的文件结构树&#xff1a; 使用tree命令&#xff08;如果你的系统已…

qtcreator配置msvc编译器 visual studio配置qt开发 以及使用对比

qtcreator配置msvc编译器开发 qtcreator在线安装&#xff08;qt5.12之后&#xff09;时候&#xff0c;默认选择的是mingw&#xff08;gcc编译器的windows版本&#xff09;的qt库以及migw编译器&#xff0c;我们可以额外勾选msvc&#xff08;visual studio的编译器&#xff0c;…

【Redis】Redis 生产问题。如何确保缓存和数据库数据的一致性? 常见的缓存更新策略?

目录 缓存穿透 缓存穿透解决办法 缓存击穿 击穿解决办法&#xff1f; 缓存穿透和缓存击穿的区别&#xff1f; 缓存雪崩 雪崩解决办法&#xff1f; 如何确保缓存和数据库数据的一致性&#xff1f; 常见的缓存更新策略&#xff1f; 缓存穿透 定义&#xff1a;缓存穿透说…

elementui 导航菜单栏和Breadcrumb 面包屑关联

系列文章目录 一、elementui 导航菜单栏和Breadcrumb 面包屑关联 文章目录 系列文章目录前言一、elementui 导航菜单栏和Breadcrumb 面包屑怎么关联&#xff1f;二、实现效果三、实现步骤1.本项目演示布局2.添加面包屑2.实现breadcrumbName方法3.监听方法4.路由指配5.路由配置…

Nomad Web更新没有最快只有更快

大家好&#xff0c;才是真的好。 很长时间没介绍运行在浏览器中的Notes客户端即Nomad Web更新情况。 不用安装&#xff0c;直接使用&#xff0c;还可以完美地兼容适应各种操作系统&#xff0c;Nomad Web一定是Notes/Domino产品现在和将来重点发展的用户访问模式。 不过&…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记15:PWM输出

系列文章目录 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记01&#xff1a;赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记02&#xff1a;开发环境安装 嵌入式|蓝桥杯STM32G431&#xff08;…