概述
开发中难免遇到操作关系型数据库的场景,但是原生的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 = nullswitch (info.type) {case ColumnType.LONG:val = result.getLong(result.getColumnIndex(info.columnName))breakcase ColumnType.DOUBLE:val = result.getDouble(result.getColumnIndex(info.columnName))breakcase ColumnType.STRING:val = result.getString(result.getColumnIndex(info.columnName))breakcase 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 = nullswitch (info.type) {case ColumnType.LONG:val = result.getLong(result.getColumnIndex(info.columnName))breakcase ColumnType.DOUBLE:val = result.getDouble(result.getColumnIndex(info.columnName))breakcase ColumnType.STRING:val = result.getString(result.getColumnIndex(info.columnName))breakcase 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);