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 ✅

提示:项目中主要使用 equalTo、orderByDesc、beginWrap/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. 常用条件 :equalTo、orderByDesc、isNull 覆盖了项目中 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

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

相关推荐
李游Leo18 分钟前
HarmonyOS 7 + ArkUI Accessibility:闪控窗紧凑态的语义树裁剪与读屏焦点收敛【鸿蒙心迹】
华为·harmonyos
李游Leo41 分钟前
HarmonyOS 7 ArkUI Navigation + WindowStage:折叠屏窗口宽度驱动的单双栏切换与草稿状态保持【鸿蒙心迹】
华为·harmonyos
HwJack201 小时前
【共创稿事节】HarmonyOS 7材质与光照:真实感的营造与性能的平衡
pytorch·harmonyos·材质
翼辉cto1 小时前
otlinx-datetime 官方 KMP 日期时间库的 OpenHarmony 鸿蒙化适配实战
华为·harmonyos
m0_738185821 小时前
Flutter 鸿蒙化实战:flutter_iot_wifi 适配 OpenHarmony,IoT 设备 WiFi 配网
物联网·flutter·华为·harmonyos·鸿蒙
TMT星球2 小时前
华为Mate 90系列及全场景新品发布会举行,多款重磅新品亮相
华为
李游Leo2 小时前
HarmonyOS 7 + Hvigor-module.json5:权限增量审计与隐私声明一致性门禁【鸿蒙心迹】
数码相机·华为·harmonyos
李游Leo2 小时前
HarmonyOS 7 ArkTS + Node.js:把权限声明、运行时授权与隐私文案做成提审前一致性扫描【鸿蒙心迹】
华为·node.js·harmonyos
fellow993 小时前
把 DeepSeek Harness 搬进鸿蒙的 8 个坑
华为·harmonyos
垆边人似月.3 小时前
华为机考题(一):质数因子
算法·华为