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

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 链式 API 分类------等值、范围、模糊、排序、分页
#mermaid-svg-Lq7W4kMI1xPvrgrM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Lq7W4kMI1xPvrgrM .error-icon{fill:#552222;}#mermaid-svg-Lq7W4kMI1xPvrgrM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Lq7W4kMI1xPvrgrM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .marker.cross{stroke:#333333;}#mermaid-svg-Lq7W4kMI1xPvrgrM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Lq7W4kMI1xPvrgrM p{margin:0;}#mermaid-svg-Lq7W4kMI1xPvrgrM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster-label text{fill:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster-label span{color:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster-label span p{background-color:transparent;}#mermaid-svg-Lq7W4kMI1xPvrgrM .label text,#mermaid-svg-Lq7W4kMI1xPvrgrM span{fill:#333;color:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .node rect,#mermaid-svg-Lq7W4kMI1xPvrgrM .node circle,#mermaid-svg-Lq7W4kMI1xPvrgrM .node ellipse,#mermaid-svg-Lq7W4kMI1xPvrgrM .node polygon,#mermaid-svg-Lq7W4kMI1xPvrgrM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .rough-node .label text,#mermaid-svg-Lq7W4kMI1xPvrgrM .node .label text,#mermaid-svg-Lq7W4kMI1xPvrgrM .image-shape .label,#mermaid-svg-Lq7W4kMI1xPvrgrM .icon-shape .label{text-anchor:middle;}#mermaid-svg-Lq7W4kMI1xPvrgrM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .rough-node .label,#mermaid-svg-Lq7W4kMI1xPvrgrM .node .label,#mermaid-svg-Lq7W4kMI1xPvrgrM .image-shape .label,#mermaid-svg-Lq7W4kMI1xPvrgrM .icon-shape .label{text-align:center;}#mermaid-svg-Lq7W4kMI1xPvrgrM .node.clickable{cursor:pointer;}#mermaid-svg-Lq7W4kMI1xPvrgrM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .arrowheadPath{fill:#333333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lq7W4kMI1xPvrgrM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Lq7W4kMI1xPvrgrM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lq7W4kMI1xPvrgrM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster text{fill:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM .cluster span{color:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Lq7W4kMI1xPvrgrM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Lq7W4kMI1xPvrgrM rect.text{fill:none;stroke-width:0;}#mermaid-svg-Lq7W4kMI1xPvrgrM .icon-shape,#mermaid-svg-Lq7W4kMI1xPvrgrM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Lq7W4kMI1xPvrgrM .icon-shape p,#mermaid-svg-Lq7W4kMI1xPvrgrM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Lq7W4kMI1xPvrgrM .icon-shape .label rect,#mermaid-svg-Lq7W4kMI1xPvrgrM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Lq7W4kMI1xPvrgrM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Lq7W4kMI1xPvrgrM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Lq7W4kMI1xPvrgrM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RdbPredicates
等值条件

equalTo / notEqualTo
范围条件

greaterThan / lessThan / between
模糊条件

like / glob
空值条件

isNull / isNotNull
排序分组

orderByAsc / orderByDesc / groupBy
分页限制

limit / offset
组合条件

and / or / beginWrap / endWrap

一、RdbPredicates 基础

1.1 创建和基本用法

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

typescript 复制代码
import relationalStore from '@ohos.data.relationalStore'

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

1.2 完整查询模式

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

二、常用查询条件

2.1 等值查询

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

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

2.2 排序

typescript 复制代码
// 降序排列(最新在前)
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 区间查询

typescript 复制代码
// 不在 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 连续的链式调用

typescript 复制代码
// 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 分步链式调用

typescript 复制代码
// 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 组合

typescript 复制代码
// 查询未归类笔迹: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 连接:

typescript 复制代码
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 嵌套条件

typescript 复制代码
// 复杂场景示例
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

typescript 复制代码
// 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

typescript 复制代码
// 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

typescript 复制代码
// 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 选择建议

typescript 复制代码
// ✅ 用 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 不能跨表

typescript 复制代码
// ❌ 错误: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

typescript 复制代码
// ✅ 正确:每个方法内部创建新的 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 等效示例

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

typescript 复制代码
// 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

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

相关推荐
心中有国也有家1 小时前
AtomGit Flutter 鸿蒙客户端: ChangeNotifier 模式
学习·flutter·华为·harmonyos
国服第二切图仔2 小时前
HarmonyOS APP《画伴梦工厂》开发第59篇-碰一碰精准分享——跨设备素材插入
华为·harmonyos
hqzing2 小时前
在鸿蒙 PC 上使用 Claude Code(最新的 Bun 版本)
华为·harmonyos
GitCode官方4 小时前
开源鸿蒙跨平台直播|QRN 跨端鸿蒙一体化实战
人工智能·华为·开源·harmonyos·atomgit
ZZZMMM.zip4 小时前
演示架构师-PPT大纲生成的HarmonyOS开发实践
人工智能·华为·powerpoint·harmonyos·鸿蒙·鸿蒙系统
国服第二切图仔5 小时前
HarmonyOS APP《画伴梦工厂》开发第60篇-分布式软总线2.0——多设备协同新范式
分布式·wpf·harmonyos
2501_918582375 小时前
50 Canvas 动画:@State + @Watch + animateTo 驱动模式
华为·harmonyos·鸿蒙系统
xianjixiance_5 小时前
43 短距通信场景汇总:NFC / 二维码 / 剪贴板 / 超级终端
华为·harmonyos·鸿蒙系统
木木子226 小时前
[特殊字符] 音乐播放器——状态驱动的多媒体控制
android·开发语言·华为·php·harmonyos