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 分类——等值、范围、模糊、排序、分页
一、RdbPredicates 基础
1.1 创建和基本用法
RdbPredicates的构造函数接受一个参数——表名,表示该谓词适用于哪张表:
importrelationalStorefrom'@ohos.data.relationalStore'// 创建 predicates:指定表名constpredicates=newrelationalStore.RdbPredicates('handwriting')1.2 完整查询模式
conststore=DatabaseService.getInstance().getStore()constpredicates=newrelationalStore.RdbPredicates(TABLE)// ... 添加查询条件 ...constrs=awaitstore.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 DESC2.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 ?// INpredicates.in('source',['camera','gallery'])// SQL 等价:WHERE source IN (?, ?)提示:所有值都是参数化绑定(
?占位符),不存在 SQL 注入风险。
三、链式调用
3.1 连续的链式调用
// ArchiveDao.listByUserconstpredicates=newrelationalStore.RdbPredicates(TABLE).equalTo('user_id',userId).orderByDesc('last_record_at')// SQL 等价:WHERE user_id = ? ORDER BY last_record_at DESC3.2 分步链式调用
// HandwritingDao.listUnclassifiedconstpredicates=newrelationalStore.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 NULLconstpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.beginWrap()predicates.equalTo('archive_id',0)// 条件 Apredicates.or()// ORpredicates.isNull('archive_id')// 条件 Bpredicates.endWrap()predicates.orderByDesc('created_at')// SQL 等价:// WHERE (archive_id = 0 OR archive_id IS NULL)// ORDER BY created_at DESC4.2 AND 组合(默认行为)
如果不显式添加or(),多个条件默认以AND连接:
constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('archive_id',archiveId)// 默认 ANDpredicates.orderByDesc('created_at')// SQL 等价:WHERE archive_id = ? ORDER BY created_at DESC// (实际上只有 1 个 WHERE 条件 + 1 个 ORDER BY)4.3 嵌套条件
// 复杂场景示例constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('user_id',userId)// user_id = ?predicates.beginWrap()// (predicates.greaterThan('energy_score',60)// energy_score > ?predicates.or()// ORpredicates.in('source',['camera','handwrite'])// source IN (?, ?)predicates.endWrap()// )// SQL 等价:// WHERE user_id = ?// AND (energy_score > ? OR source IN (?, ?))五、各 DAO 中的 RdbPredicates 应用汇总
5.1 UserDao
// findByOpenId —— 等值查询 + 单条结果constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('open_id',openId)// updateName —— 等值条件 + updateconstpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('id',id)awaitstore.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(constidofids){constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('id',id)awaitstore.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)awaitstore.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:简单条件查询constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('archive_id',archiveId)predicates.orderByDesc('created_at')awaitstore.query(predicates,COLS)// ✅ 用原始 SQL:聚合统计awaitstore.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 不能跨表
// ❌ 错误:RdbPredicates 只能作用于一张表constpredicates=newrelationalStore.RdbPredicates('handwriting')// 无法同时查询 report 表的字段// ✅ 正确:分两次查询或使用原始 SQL JOINconsthandwriting=awaitHandwritingDao.findById(id)constreport=awaitReportDao.findByHandwriting(handwriting.id!)8.2 每次查询创建新的 predicates
// ✅ 正确:每个方法内部创建新的 predicatesstaticasynclistByArchive(archiveId:number):Promise<HandwritingEntity[]>{constpredicates=newrelationalStore.RdbPredicates(TABLE)predicates.equalTo('archive_id',archiveId)// ...}// ❌ 错误:复用 predicates 对象(状态污染)constglobalPredicates=newrelationalStore.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 等效示例
相同查询的两种写法对比:
// RdbPredicates 写法constpred=newrelationalStore.RdbPredicates('handwriting')pred.equalTo('user_id',userId).orderByDesc('created_at').limit(10)constrs=awaitstore.query(pred,['id','created_at','image_path'])// 原始 SQL 写法constsql=`SELECT id, created_at, image_path FROM handwriting WHERE user_id = ? ORDER BY created_at DESC LIMIT 10`constrs2=awaitstore.querySql(sql,[userId])两种方式在功能上等效,RdbPredicates 可读性更好,SQL 方式更灵活。
7.2 选择建议
- 简单 CRUD 操作→ 优先使用 RdbPredicates,代码更安全易读
- 聚合统计、JOIN 查询→ 使用
querySql原始 SQL - 动态多条件筛选→ RdbPredicates 链式调用更优雅
- 复杂报表查询→ SQL 表达力更强,直接写 SQL
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!