【Harmony】鸿蒙数据库工具封装ZDbUtil

介绍

ZDbUtil是一款数据库操作工具库

  • 通过注解类和字段,与数据库的表进行关联
  • 支持状态管理(V1)中被@Observed装饰的类
  • 支持状态管理(V2)中被@ObservedV2装饰的类
  • 支持存放复杂类型的数据,通过 json 格式进行存储
  • 支持一对一/一对多查询
  • 数据库升级

链接

源码

示例

效果图

数据库操作2

下载安装

在每个har/hsp模块中,通过ohpm工具下载安装库:

ohpm install @hzw/zdb

注解

Table

  • 参数
    • name: 表名,默认为类名

Column

  • 参数:
    • name: 列名,默认为字段名称
    • type: 列类型,查看ColumnType
      • Integer: 整型
      • Text: 文本类型
      • Boolean: 布尔类型
      • Real: 浮点类型
    • primaryKey: 是否主键
    • autoIncrement: 是否自增
    • notNull: 是否为空
    • unique: 是否唯一

OneToOne

  • 联表查询,一对一
  • 参数:
    • joinTable: 关联表的类
    • relatedIdProperty: 关联id属性名,该属性是主表关联类的字段

OneToMany

  • 一对多,使用方法参考一对多查询
  • 参数:
    • placeHolderTable: 占位类,查询的时候通过include把占位类替换为联动表对应实体类
    • relatedIdProperty: 关联id属性名,该属性是主表关联类的字段

ObjToJson

  • 以json形式存储复杂对象
  • 参数:
    • cls: 复杂对象的类信息

使用方法

定义实体类

只要添加了@Table注解,@Column注解
在初始化数据库时,会自动创建表,并添加列。

import { Column, ColumnType, ObjToJson, OneToOne, Table } from '@hzw/zdb';

// 作业本
export class StudentHomeworkBook {
  // 书本名
  name?: string
}

// 父亲
export class StudentFather {
  // 名字
  name?: string
  // 父亲
  @ObjToJson({ cls: StudentFather })
  father?: StudentFather | undefined
}

// 分类
@Table()
export class StudentClassify {
  // id
  @Column({ type: ColumnType.Integer, primaryKey: true, autoIncrement: true })
  id?: number
  // 名称
  @Column({ type: ColumnType.Text })
  name?: string
  // 创建时间
  @Column({ type: ColumnType.Integer })
  createTime?: number
  // 更新时间
  @Column({ type: ColumnType.Integer })
  editTime?: number | undefined
}

// 学生
@Table()
export class StudentInfo {
  // id
  @Column({ type: ColumnType.Integer, primaryKey: true, autoIncrement: true })
  id?: number | undefined
  // 标题
  @Column({ type: ColumnType.Text })
  title?: string | undefined
  // 创建时间
  @Column({ type: ColumnType.Integer })
  createTime?: number | undefined
  // 成绩
  @Column({ type: ColumnType.Real })
  score?: number | undefined
  // 是否管理员
  @Column({ type: ColumnType.Boolean })
  isManager?: boolean | undefined
  // 分类id
  @Column({ type: ColumnType.Integer })
  classifyId?: number | undefined
  // 分类
  @OneToOne({ joinTable: StudentClassify, relatedIdProperty: "classifyId" })
  classify?: StudentClassify | undefined
  // 名下作业本
  @ObjToJson({ cls: StudentHomeworkBook })
  homeworkBookList?: StudentHomeworkBook[] | undefined
  // 父亲
  @ObjToJson({ cls: StudentFather })
  father?: StudentFather | undefined
}

初始化数据库并根据被@Table注解的类创建表

ZDbUtil.initDatabase({
  context: this.context
})

定义数据库表操作类

建议用一个数据库管理类来管理所有的表操作类,具体参考示例创建数据库管理实例

studentInfoDao = ZBaseDao.get(StudentInfo)
  • 使用后,可以通过该实例 studentInfoDao 对表进行增删改查操作。
  • 也可以直接使用 ZDbUtil 传入对应的表关联类来进行表的增删改查操作。

查数据

更多条件查询查看请查看SQL查询构建器方法速查表

  • 使用 ZBaseDao 表操作实例
// 查询多条数据
studentInfoDao.querySqlBuilder()
// ...其他条件查询
  .limitAs(this.pageSize)
  .offsetAs(this.page * this.pageSize)
  .query()
  .then((data) => {
  })
// 根据 id 查询单条数据
studentInfoDao.querySqlBuilder()
  .queryOneById(id)
  .then((data) => {
    this.item = data;
  })
  • 直接使用 ZDbUtil
// 查询多条数据
// 传入被@Table注解的类
ZDbUtil.querySqlBuilder(StudentInfoV1)
// ...其他条件查询
  .limitAs(this.pageSize)
  .offsetAs(this.page * this.pageSize)
  .query()
  .then((data) => {
  })
// 根据 id 查询单条数据
ZDbUtil.querySqlBuilder(StudentInfoV1)
  .queryOneById(id)
  .then((data) => {
    this.item = data;
  })

以下是查询构建类核心方法的功能表格,包含方法名、功能描述,便于快速查阅和使用:

SQL查询构建器方法速查表
方法名功能描述
equalTo(field, value)添加 字段 = 值 条件
notEqualTo(field, value)添加 字段 <> 值 条件
notEqualToWithOrIsNull(field, value)添加 (字段 <> 值 OR 字段 IS NULL) 条件(值不等或字段为空)
and()添加逻辑与(AND)连接符
or()添加逻辑或(OR)连接符
beginWrap()开始包裹条件(生成左括号 (
endWrap()结束包裹条件(生成右括号 )
contains(field, value)添加 字段包含值 条件(基于 INSTR 函数)
beginsWith(field, value)添加 字段以值开头 条件(LIKE '值%'
endsWith(field, value)添加 字段以值结尾 条件(LIKE '%值'
like(field, value)添加 字段模糊匹配值 条件(LIKE '%值%'
glob(field, value)添加 字段按通配符匹配值 条件(GLOB 操作符,区分大小写)
notContains(field, value)添加 字段不包含值 条件(INSTR 结果为 0)
notLike(field, value)添加 字段不模糊匹配值 条件(NOT LIKE '%值%'
greaterThan(field, value)添加 字段 > 值 条件
lessThan(field, value)添加 字段 < 值 条件
greaterThanOrEqualTo(field, value)添加 字段 >= 值 条件
lessThanOrEqualTo(field, value)添加 字段 <= 值 条件
between(field, low, high)添加 字段 BETWEEN 低值 AND 高值 条件
notBetween(field, low, high)添加 字段 NOT BETWEEN 低值 AND 高值 条件
in(field, values)添加 字段 IN (值列表) 条件
notIn(field, values)添加 字段 NOT IN (值列表) 条件
isNull(field)添加 字段 IS NULL 条件
isNotNull(field)添加 字段 IS NOT NULL 条件
orderByAsc(field)添加 ORDER BY 字段 ASC 排序(升序)
orderByDesc(field)添加 ORDER BY 字段 DESC 排序(降序)
orderByAscCaseStart()ORDER BY 中使用 CASE 自定义排序
orderByAscCaseEnd()添加 ELSE xxx END 条件,用在 orderByAscCaseStart() 后
distinct(values)添加 DISTINCT 去重(指定去重字段)
limitAs(value)添加 LIMIT 值 限制返回行数
offsetAs(rowOffset)添加 OFFSET 值 跳过指定行数
groupBy(fields)添加 GROUP BY 字段 分组
indexedBy(field)添加 INDEXED BY 字段 索引提示(优化查询)

说明

  • 逻辑组合:and()/or() 需配合 beginWrap()/endWrap() 使用,或直接链式调用(如 a.and().b.or().c
    会自动生成 a AND b OR c)。
  • 调用顺序:orderByAsc()/orderByDesc()/limitAs()/offsetAs() 放在其他条件的后面使用

通过此表可快速定位所需方法,结合链式调用实现复杂查询条件的动态拼接。

示例

多个分组排序,使用 orderByAscCaseStart 开始分组排序,然后添加条件进行分组,最后使用 orderByAscCaseEnd 结束分组排序

  • 使用 ZBaseDao 表操作实例
studentInfoDao.querySqlBuilder()
  .orderByAscCaseStart() // 先根据isManager进行分组排序
  .equalTo("isManager", true) // 管理员分为一组
  .orderByAscCaseEnd() // 剩余分为一组
  .orderByAscCaseStart() // 再根据score进行分组排序
  .greaterThan("grade", 4) // 年级4以上分为一组
  .greaterThan("grade", 3) // 年级3以上分为一组
  .orderByAscCaseEnd() // 剩余分为一组
  .orderByAsc("score") // 组内成绩排序
  .query()
  .then((data) => {
    this.list = data;
  })
  • 直接使用 ZDbUtil
ZDbUtil.querySqlBuilder(StudentInfoV1)
  .orderByAscCaseStart() // 先根据isManager进行分组排序
  .equalTo("isManager", true) // 管理员分为一组
  .orderByAscCaseEnd() // 剩余分为一组
  .orderByAscCaseStart() // 再根据score进行分组排序
  .greaterThan("grade", 4) // 年级4以上分为一组
  .greaterThan("grade", 3) // 年级3以上分为一组
  .orderByAscCaseEnd() // 剩余分为一组
  .orderByAsc("score") // 组内成绩排序
  .query()
  .then((data) => {
    this.list = data;
  })
一对多查询
  1. 定义一对多的表关联类的占位类
// 学生占位类
export class StudentInfoPlaceholder {
}
  1. 实体类一对多字段用@OneToMany注解,并传入一个 占位类关联表的相关类字段
// 分类
@Table()
export class StudentClassify {
  // 省略...

  // 该分类下的学员
  @OneToMany({ placeHolderTable: StudentInfoPlaceholder, relatedIdProperty: "classifyId" })
  user?: StudentInfo[];
}
  1. 查询方法调用include,传入 真正的关联类 ,替换 占位类
  • 使用 ZBaseDao 表操作实例
studentClassifyDao.querySqlBuilder()
// 显式加载一对多的表关联类,只有调用该方法,一对多查询才会生效
  .include(StudentInfo, StudentInfoPlaceholder)
  .query()
  .then((data) => {
    this.classifyList = data;
  })
  • 直接使用 ZDbUtil
ZDbUtil.querySqlBuilder(StudentClassify)
// 显式加载一对多的表关联类,只有调用该方法,一对多查询才会生效
  .include(StudentInfo, StudentInfoPlaceholder)
  .query()
  .then((data) => {
    this.classifyList = data;
  })

插入/更新数据

  • 使用 ZBaseDao 表操作实例
studentInfoDao.insertOrReplace(data).then(() => {
  promptAction.showToast({ message: "添加成功" });
})
  • 直接使用 ZDbUtil

// data类型确定,且被@Table注解,直接插入/更新数据

ZDbUtil.insertOrReplace(data).then(() => {
  promptAction.showToast({ message: "添加成功" });
})

// data类型是Object,通过传入被@Table注解的类,插入/更新数据

// 插入/更新数据
// 第一个参数为插入/更新的数据
// 第二个参数为被@Table注解的类,数据将会被插入/更新到与注解类关联的表中
ZDbUtil.insertOrReplaceByCls(data, StudentClassify).then(() => {
  promptAction.showToast({ message: "添加成功" });
})

批量插入/更新数据

  • 使用 ZBaseDao 表操作实例
studentInfoDao.batchInsert(data).then(() => {
  promptAction.showToast({ message: "添加成功" });
})
  • 直接使用 ZDbUtil

// data类型确定,且被@Table注解,直接插入/更新数据

ZDbUtil.batchInsert(data).then(() => {
  promptAction.showToast({ message: "添加成功" });
})

//data类型是Object,通过传入被@Table注解的类,插入/更新数据

// 批量插入/更新数据
// 第一个参数为插入/更新的数据
// 第二个参数为被@Table注解的类,数据将会被插入/更新到与注解类关联的表中
ZDbUtil.batchInsertByCls(data, StudentClassify).then(() => {
  promptAction.showToast({ message: "添加成功" });
})

更新数据

更新符合条件的数据

  • 使用 ZBaseDao 表操作实例
// 找出 title = this.updateTitle 的数据,把 score 更新为 this.updateScoreByTitle
studentInfoDao.updateSqlBuilder()
  .set("score", Number(this.updateScoreByTitle))
  .equalTo("title", this.updateTitle)
  .update()
  .then((num) => {
    promptAction.showToast({ message: `修改${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })
  • 直接使用 ZDbUtil
// 找出 title = this.updateTitle 的数据,把 score 更新为 this.updateScoreByTitle
ZDbUtil.updateSqlBuilder(StudentInfoV1)
  .set("score", Number(this.updateScoreByTitle))
  .equalTo("title", this.updateTitle)
  .update()
  .then((num) => {
    promptAction.showToast({ message: `修改${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })

对主键对应的数据进行更新

  • 使用 ZBaseDao 表操作实例
// 找出主键为 this.updateId 的数据,把 score 更新为 this.updateScoreById
studentInfoDao.updateSqlBuilder()
  .set("score", Number(this.updateScoreById))
  .updateOneById(Number(this.updateId))
  .then((num) => {
    promptAction.showToast({ message: `修改${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })
  • 直接使用 ZDbUtil
// 找出主键为 this.updateId 的数据,把 score 更新为 this.updateScoreById
ZDbUtil.updateSqlBuilder(StudentInfoV1)
  .set("score", Number(this.updateScoreById))
  .updateOneById(Number(this.updateId))
  .then((num) => {
    promptAction.showToast({ message: `修改${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })

删除数据

  • 使用 ZBaseDao 表操作实例
studentInfoDao.delete(item).then(() => {
  promptAction.showToast({ message: "删除成功" });
})
  • 直接使用 ZDbUtil

// data类型确定,且被@Table注解,直接删除数据

ZDbUtil.delete(item).then(() => {
  promptAction.showToast({ message: "删除成功" });
})

// data类型是Object,通过传入被@Table注解的类,删除数据

ZDbUtil.deleteByCls(data, StudentInfo).then(() => {
  promptAction.showToast({ message: "删除成功" });
})

批量删除指定条件数据

删除指定条件的数据

  • 使用 ZBaseDao 表操作实例
// 删除 title = this.deleteTitle 的数据
studentInfoDao.deleteSqlBuilder()
  .equalTo("title", this.deleteTitle)
  .delete()
  .then((num) => {
    promptAction.showToast({ message: `删除${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })
  • 直接使用 ZDbUtil
// 删除 title = this.deleteTitle 的数据
ZDbUtil.deleteSqlBuilder(StudentInfoV1)
  .equalTo("title", this.deleteTitle)
  .delete()
  .then((num) => {
    promptAction.showToast({ message: `删除${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })

删除指定id数据

  • 使用 ZBaseDao 表操作实例
// 删除主键为 this.deleteId 的数据
studentInfoDao.deleteSqlBuilder()
  .deleteOneById(Number(this.deleteId))
  .then((num) => {
    promptAction.showToast({ message: `删除${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })
  • 直接使用 ZDbUtil
// 删除主键为 this.deleteId 的数据
ZDbUtil.deleteSqlBuilder(StudentInfoV1)
  .deleteOneById(Number(this.deleteId))
  .then((num) => {
    promptAction.showToast({ message: `删除${num}条,返回手动刷新数据` });
  })
  .catch((err: BusinessError) => {
    promptAction.showToast({ message: `删除失败:${err}` });
  })

清空数据

清空与之关联的表的所有数据

  • 使用 ZBaseDao 表操作实例
studentInfoDao.clear().then(() => {
  promptAction.showToast({ message: "清空成功" });
})
  • 直接使用 ZDbUtil
// 传入@Table注解的类
ZDbUtil.clear(StudentInfo).then(() => {
  promptAction.showToast({ message: "清空成功" });
})

创建数据库管理实例

使用 ZBaseDao 管理表关联的类,用于操作表,再用一个管理类 DatabaseManager 管理所有表操作类 ZBaseDao ,使用如下:

// 数据库管理类
export class DatabaseManager {
  readonly studentClassifyDao = ZBaseDao.get(StudentClassify)
  readonly studentInfoDao = ZBaseDao.get(StudentInfo)
  // ...

  /**
   * 数据库管理实例
   */
  private static _instance: DatabaseManager | null = null

  static instance(): DatabaseManager {
    if (DatabaseManager._instance == null) {
      DatabaseManager._instance = new DatabaseManager()
    }
    return DatabaseManager._instance
  }
}

// 获取表操作实例

studentInfoDao = DatabaseManager.instance().studentInfoDao

// 操作表(这里不一一展示)
// 查询
studentInfoDao.querySqlBuilder()
// ...其他条件查询
  .limitAs(this.pageSize)
  .offsetAs(this.page * this.pageSize)
  .query()
  .then((data) => {
  })

// ...

开启打印sql相关日志

ZDbUtil.sqlLogEnable(true)

或初始化数据库方法中 sqlLogEnable 参数设为 true

ZDbUtil.initDatabase({
  // ...
  sqlLogEnable: true,
})

升级数据库版本

自动升级,需要配置 changeByUpdate 参数为 true,以及设置数据库版本 dbVersion

ZDbUtil.initDatabase({
  // ...
  // 是否升级数据库版修改数据库结构(开启后,如果类修改了,需要升级数据库版本dbVersion)
  changeByUpdate: true,
  // 数据库版本
  dbVersion: 2,
})

手动升级,需要配置 changeByUpdate 参数为 true,以及设置数据库版本 dbVersion,并配置数据库版本迁移 migrations

ZDbUtil.initDatabase({
  // ..
  // 是否升级数据库版修改数据库结构(开启后,如果类修改了,需要升级数据库版本dbVersion)
  changeByUpdate: true,
  // 数据库版本
  dbVersion: 7,
  // 数据库版本迁移
  migrations: [
    new Migration(5, 6)
      .addColumn(StudentInfo, 'isManager2', ColumnType.Text) // 添加列
      .delColumn(StudentInfo, 'age'), // 删除列
    new Migration(6, 7)
      .alterColumnType(StudentInfo, 'isManager2', ColumnType.Boolean) // 修改列类型
      .alterColumnName(StudentInfo, 'isManager2', 'isManager'), // 修改列名
    new Migration(7, 8)
      .addCustomSql(StudentInfo, 'alter table student_info add column isManager2 text;'),// 自定义sql,
  ],
})

联系我们

欢迎大家提交issue、PR(可以统一收集问题,方便更多人查阅,会第一时间回复处理) ,或进群交流(+v: _hhbin)。

tab
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值