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 的用法:
- 基础用法 :
new RdbPredicates(TABLE)+ 条件方法 +store.query(predicates, COLS) - 常用条件 :
equalTo、orderByDesc、isNull覆盖了项目中 90% 的查询场景 - 组合条件 :
beginWrap()/or()/endWrap()构建 OR 嵌套 - 链式调用:方法链清晰表达查询意图
- 与原始 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
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!