27 RdbPredicates 条件查询详解:EqualTo、OrderBy、组合条件

前言

在这里插入图片描述

图:27 RdbPredicates 条件查询详解:EqualTo、OrderBy、组合条件 运行效果截图(HarmonyOS NEXT)

在鸿蒙 RDB 中,RdbPredicates 是构建数据库查询条件的核心类。它提供了一套链式调用 API,让开发者无需编写原始 SQL 即可完成复杂的条件组合。与直接拼接 SQL 相比,RdbPredicates 更安全、更可读、更易维护。

本文以"鹿鹿·笔迹心理分析"项目中的 UserDao、HandwritingDao、ArchiveDao 等 DAO 代码为例,全面解析 RdbPredicates 的各种条件构建方法。

鸿蒙官方·RdbPredicates 文档:developer.huawei.com
项目源码仓库:harmony-app GitHub

RdbPredicates 条件构建方法分类

图:RdbPredicates 链式 API 分类——等值、范围、模糊、排序、分页

RdbPredicates

等值条件
equalTo / notEqualTo

范围条件
greaterThan / lessThan / between

模糊条件
like / glob

空值条件
isNull / isNotNull

排序分组
orderByAsc / orderByDesc / groupBy

分页限制
limit / offset

组合条件
and / or / beginWrap / endWrap

一、RdbPredicates 基础

1.1 创建和基本用法

RdbPredicates 的构造函数接受一个参数——表名,表示该谓词适用于哪张表:

import relationalStore from '@ohos.data.relationalStore'

// 创建 predicates:指定表名
const predicates = new relationalStore.RdbPredicates('handwriting')

1.2 完整查询模式

const store = DatabaseService.getInstance().getStore()
const predicates = new relationalStore.RdbPredicates(TABLE)
// ... 添加查询条件 ...
const rs = await store.query(predicates, ['col1', 'col2', ...])

二、常用查询条件

2.1 等值查询

// 最简单的等值条件
predicates.equalTo('archive_id', archiveId)
// SQL 等价:WHERE archive_id = ?

predicates.equalTo('open_id', 'local')
// SQL 等价:WHERE open_id = ?

2.2 排序

// 降序排列(最新在前)
predicates.orderByDesc('created_at')
// SQL 等价:ORDER BY created_at DESC

// 升序排列
predicates.orderByAsc('created_at')
// SQL 等价:ORDER BY created_at ASC

// 多字段排序
predicates.orderByDesc('last_record_at')
predicates.orderByDesc('id')
// SQL 等价:ORDER BY last_record_at DESC, id DESC

2.3 区间查询

// 不在 NULL 范围内的值
predicates.isNotNull('archive_id')
// SQL 等价:WHERE archive_id IS NOT NULL

// NULL 值
predicates.isNull('archive_id')
// SQL 等价:WHERE archive_id IS NULL

// 大于
predicates.greaterThan('energy_score', 50)
// SQL 等价:WHERE energy_score > ?

// 小于
predicates.lessThan('word_count', 100)
// SQL 等价:WHERE word_count < ?

// 范围
predicates.between('created_at', startTime, endTime)
// SQL 等价:WHERE created_at BETWEEN ? AND ?

// IN
predicates.in('source', ['camera', 'gallery'])
// SQL 等价:WHERE source IN (?, ?)

提示:所有值都是参数化绑定(? 占位符),不存在 SQL 注入风险。

三、链式调用

3.1 连续的链式调用

// ArchiveDao.listByUser
const predicates = new relationalStore.RdbPredicates(TABLE)
  .equalTo('user_id', userId)
  .orderByDesc('last_record_at')

// SQL 等价:WHERE user_id = ? ORDER BY last_record_at DESC

3.2 分步链式调用

// HandwritingDao.listUnclassified
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.beginWrap()
predicates.equalTo('archive_id', 0)
predicates.or()
predicates.isNull('archive_id')
predicates.endWrap()
predicates.orderByDesc('created_at')

// SQL 等价:WHERE (archive_id = 0 OR archive_id IS NULL) ORDER BY created_at DESC

四、组合条件:beginWrap / or / endWrap

4.1 OR 组合

// 查询未归类笔迹:archive_id = 0 或 archive_id IS NULL
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.beginWrap()
predicates.equalTo('archive_id', 0)      // 条件 A
predicates.or()                            // OR
predicates.isNull('archive_id')            // 条件 B
predicates.endWrap()
predicates.orderByDesc('created_at')

// SQL 等价:
// WHERE (archive_id = 0 OR archive_id IS NULL)
// ORDER BY created_at DESC

4.2 AND 组合(默认行为)

如果不显式添加 or(),多个条件默认以 AND 连接:

const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.equalTo('archive_id', archiveId)     // 默认 AND
predicates.orderByDesc('created_at')

// SQL 等价:WHERE archive_id = ? ORDER BY created_at DESC
// (实际上只有 1 个 WHERE 条件 + 1 个 ORDER BY)

4.3 嵌套条件

// 复杂场景示例
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.equalTo('user_id', userId)                    // user_id = ?
predicates.beginWrap()                                    // (
predicates.greaterThan('energy_score', 60)               //   energy_score > ?
predicates.or()                                           //   OR
predicates.in('source', ['camera', 'handwrite'])          //   source IN (?, ?)
predicates.endWrap()                                      // )

// SQL 等价:
// WHERE user_id = ?
//   AND (energy_score > ? OR source IN (?, ?))

五、各 DAO 中的 RdbPredicates 应用汇总

5.1 UserDao

// findByOpenId —— 等值查询 + 单条结果
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.equalTo('open_id', openId)

// updateName —— 等值条件 + update
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.equalTo('id', id)
await store.update({ name }, predicates)

5.2 HandwritingDao

// findById —— 等值查询
predicates.equalTo('id', id)

// listByArchive —— 等值 + 排序
predicates.equalTo('archive_id', archiveId)
predicates.orderByDesc('created_at')

// listUnclassified —— 组合条件(OR)
predicates.beginWrap()
predicates.equalTo('archive_id', 0)
predicates.or()
predicates.isNull('archive_id')
predicates.endWrap()
predicates.orderByDesc('created_at')

// batchUpdateArchive —— 循环更新
for (const id of ids) {
  const predicates = new relationalStore.RdbPredicates(TABLE)
  predicates.equalTo('id', id)
  await store.update({ archive_id: archiveId }, predicates)
}

5.3 ArchiveDao

// listByUser —— 等值 + 排序
predicates.equalTo('user_id', userId)
predicates.orderByDesc('last_record_at')

// findById —— 等值查询
predicates.equalTo('id', id)

// delete —— 等值条件删除
predicates.equalTo('id', id)
await store.delete(predicates)

5.4 各 DAO 的 RdbPredicates 使用统计

DAO equalTo orderByDesc beginWrap/or/endWrap 用途
UserDao 用户查询
HandwritingDao 笔迹多条件查询
ArchiveDao 档案查询
ReportDao 报告查询
RelationDao 关系查询

六、RdbPredicates vs 原始 SQL

6.1 对比

对比维度 RdbPredicates 原始 SQL(querySql)
类型安全 ✅ 方法名明确语义 ❌ 字符串拼接易错
SQL 注入防护 ✅ 自动参数化绑定 ❌ 需手动 ? 占位
可读性 ✅ 链式调用清晰 ⚠️ 字符串较长时可读性差
复杂查询(JOIN) ❌ 不支持 ✅ 完全支持
聚合函数 ❌ 不支持 ✅ COUNT / AVG / GROUP BY
参数绑定 自动 需手动传 bindArgs

6.2 选择建议

// ✅ 用 RdbPredicates:简单条件查询
const predicates = new relationalStore.RdbPredicates(TABLE)
predicates.equalTo('archive_id', archiveId)
predicates.orderByDesc('created_at')
await store.query(predicates, COLS)

// ✅ 用原始 SQL:聚合统计
await store.querySql(
  'SELECT COUNT(*) AS cnt FROM handwriting WHERE archive_id = ?',
  [archiveId]
)

七、RdbPredicates 的完整 API

方法 用途 SQL 等价 项目中是否使用
equalTo(field, value) 等于 WHERE field = ?
notEqualTo(field, value) 不等于 WHERE field != ?
greaterThan(field, value) 大于 WHERE field > ?
lessThan(field, value) 小于 WHERE field < ?
between(field, lo, hi) 范围 WHERE field BETWEEN ? AND ?
in(field, values) IN WHERE field IN (?, ?)
notIn(field, values) NOT IN WHERE field NOT IN (?, ?)
isNull(field) 为空 WHERE field IS NULL
isNotNull(field) 非空 WHERE field IS NOT NULL
like(field, pattern) 模糊 WHERE field LIKE ?
orderByAsc(field) 升序 ORDER BY field ASC
orderByDesc(field) 降序 ORDER BY field DESC
limit(count) 限制行数 LIMIT ? ❌(用 querySql 替代)
beginWrap() 左括号 (
endWrap() 右括号 )
or() OR OR

提示:项目中主要使用 equalToorderByDescbeginWrap/or/endWrap 这几种就覆盖了 90% 的查询场景。更复杂的统计查询直接使用原始 SQL。

八、常见问题

8.1 predicates 不能跨表

// ❌ 错误:RdbPredicates 只能作用于一张表
const predicates = new relationalStore.RdbPredicates('handwriting')
// 无法同时查询 report 表的字段

// ✅ 正确:分两次查询或使用原始 SQL JOIN
const handwriting = await HandwritingDao.findById(id)
const report = await ReportDao.findByHandwriting(handwriting.id!)

8.2 每次查询创建新的 predicates

// ✅ 正确:每个方法内部创建新的 predicates
static async listByArchive(archiveId: number): Promise<HandwritingEntity[]> {
  const predicates = new relationalStore.RdbPredicates(TABLE)
  predicates.equalTo('archive_id', archiveId)
  // ...
}

// ❌ 错误:复用 predicates 对象(状态污染)
const globalPredicates = new relationalStore.RdbPredicates(TABLE)

总结

本文全面解析了鸿蒙 RDB 中 RdbPredicates 的用法:

  1. 基础用法new RdbPredicates(TABLE) + 条件方法 + store.query(predicates, COLS)
  2. 常用条件equalToorderByDescisNull 覆盖了项目中 90% 的查询场景
  3. 组合条件beginWrap() / or() / endWrap() 构建 OR 嵌套
  4. 链式调用:方法链清晰表达查询意图
  5. 与原始 SQL 互补:简单查询用 RdbPredicates,复杂聚合用 querySql

下一篇文章将介绍 ValuesBucket 数据插入——create 方法的 ValuesBucket 构建。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


参考资源:

  • RdbPredicates API 参考
  • RdbStore.query 方法
  • [UserDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/UserDao.ets)
  • [HandwritingDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/HandwritingDao.ets)
  • [ArchiveDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/ArchiveDao.ets)
  • [ReportDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/ReportDao.ets)
  • [RelationDao 项目源码](file:///Users/fiona/Downloads/bijixinli/harmony-app/entry/src/main/ets/features/data/RelationDao.ets)
  • 鸿蒙数据持久化指南
  • HarmonyOS 开发文档

七、RdbPredicates 与原始 SQL 对比

对于项目中的查询需求,RdbPredicates 和原始 SQL 各有适用场景:

场景 RdbPredicates 原始 SQL
单表简单查询 ✅ 推荐,链式可读 可用但过于繁琐
多条件组合 ✅ and/or 组合 可用
聚合统计(COUNT/SUM) ❌ 不支持 ✅ 必须用 SQL
多表 JOIN ❌ 不支持 ✅ 必须用 SQL
动态条件构建 ✅ 链式追加条件 需要字符串拼接
SQL 注入安全 ✅ 自动参数化 需要手动参数绑定

7.1 等效示例

相同查询的两种写法对比:

// RdbPredicates 写法
const pred = new relationalStore.RdbPredicates('handwriting')
pred.equalTo('user_id', userId)
    .orderByDesc('created_at')
    .limit(10)
const rs = await store.query(pred, ['id', 'created_at', 'image_path'])

// 原始 SQL 写法
const sql = `SELECT id, created_at, image_path FROM handwriting WHERE user_id = ? ORDER BY created_at DESC LIMIT 10`
const rs2 = await store.querySql(sql, [userId])

两种方式在功能上等效,RdbPredicates 可读性更好,SQL 方式更灵活。

7.2 选择建议

  • 简单 CRUD 操作 → 优先使用 RdbPredicates,代码更安全易读
  • 聚合统计、JOIN 查询 → 使用 querySql 原始 SQL
  • 动态多条件筛选 → RdbPredicates 链式调用更优雅
  • 复杂报表查询 → SQL 表达力更强,直接写 SQL

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐