1. 概述
在之前的文章中,我们重点介绍了如何快速上手 Apache Cayenne ORM。本文将深入探讨如何使用该 ORM 框架编写简单到复杂的查询操作。
2. 环境准备
测试环境配置与前一篇文章保持一致。为便于测试,我们在每个测试用例执行前预置三条作者数据,测试结束后清理:
- Paul Xavier
- pAuL Smith
- Vicky Sarra
3. ObjectSelect 查询
3.1 基础查询
先从简单查询开始,获取所有名称包含"Paul"的作者:
@Test
public void whenContainsObjS_thenWeGetOneRecord() {
List<Author> authors = ObjectSelect.query(Author.class)
.where(Author.NAME.contains("Paul"))
.select(context);
assertEquals(authors.size(), 1);
}
3.2 不区分大小写查询
使用 likeIgnoreCase
实现不区分大小写的 LIKE 查询:
@Test
void whenLikeObjS_thenWeGetTwoAuthors() {
List<Author> authors = ObjectSelect.query(Author.class)
.where(Author.NAME.likeIgnoreCase("Paul%"))
.select(context);
assertEquals(authors.size(), 2);
}
3.3 后缀匹配查询
endsWith()
方法返回名称以"Sarra"结尾的作者:
@Test
void whenEndsWithObjS_thenWeGetOrderedAuthors() {
List<Author> authors = ObjectSelect.query(Author.class)
.where(Author.NAME.endsWith("Sarra"))
.select(context);
Author firstAuthor = authors.get(0);
assertEquals(authors.size(), 1);
assertEquals(firstAuthor.getName(), "Vicky Sarra");
}
3.4 IN 条件查询
查询名称在指定列表中的作者:
@Test
void whenInObjS_thenWeGetAuthors() {
List names = Arrays.asList(
"Paul Xavier", "pAuL Smith", "Vicky Sarra");
List<Author> authors = ObjectSelect.query(Author.class)
.where(Author.NAME.in(names))
.select(context);
assertEquals(authors.size(), 3);
}
3.5 NOT IN 条件查询
nin
(NOT IN)操作符排除指定名称:
@Test
void whenNinObjS_thenWeGetAuthors() {
List names = Arrays.asList(
"Paul Xavier", "pAuL Smith");
List<Author> authors = ObjectSelect.query(Author.class)
.where(Author.NAME.nin(names))
.select(context);
Author author = authors.get(0);
assertEquals(authors.size(), 1);
assertEquals(author.getName(), "Vicky Sarra");
}
3.6 表达式工厂对比
以下两种写法效果完全相同:
Expression qualifier = ExpressionFactory
.containsIgnoreCaseExp(Author.NAME.getName(), "Paul");
Author.NAME.containsIgnoreCase("Paul");
3.7 常用表达式列表
Expression
和 ExpressionFactory
类提供的核心表达式:
- ✅
likeExp
: 构建 LIKE 表达式 - ✅
likeIgnoreCaseExp
: 构建不区分大小写的 LIKE 表达式 - ✅
containsExp
: 字符串任意位置匹配的 LIKE 查询 - ✅
containsIgnoreCaseExp
: 不区分大小写的字符串匹配 - ✅
startsWithExp
: 匹配字符串开头 - ✅
startsWithIgnoreCaseExp
: 不区分大小写的开头匹配 - ✅
endsWithExp
: 匹配字符串结尾 - ✅
endsWithIgnoreCaseExp
: 不区分大小写的结尾匹配 - ✅
expTrue
: 布尔 true 表达式 - ✅
expFalse
: 布尔 false 表达式 - ✅
andExp
: AND 操作符连接表达式 - ✅
orExp
: OR 操作符连接表达式
更多测试用例请参考 GitHub 仓库
4. SelectQuery 查询
4.1 基础用法
这是应用中最常用的查询类型,提供类 SQL 语法的 API,通过构建器模式组合复杂表达式。
4.1.1 LIKE 查询示例
@Test
void whenLikeSltQry_thenWeGetOneAuthor() {
Expression qualifier
= ExpressionFactory.likeExp(Author.NAME.getName(), "Paul%");
SelectQuery query
= new SelectQuery(Author.class, qualifier);
List<Author> authorsTwo = context.performQuery(query);
assertEquals(authorsTwo.size(), 1);
}
⚠️ 注意:未提供表达式时将返回全表数据。
4.1.2 不区分大小写包含查询
@Test
void whenCtnsIgnorCaseSltQry_thenWeGetTwoAuthors() {
Expression qualifier = ExpressionFactory
.containsIgnoreCaseExp(Author.NAME.getName(), "Paul");
SelectQuery query
= new SelectQuery(Author.class, qualifier);
List<Author> authors = context.performQuery(query);
assertEquals(authors.size(), 2);
}
4.1.3 复合条件查询
组合包含"Paul"且以"h"结尾的条件:
@Test
void whenCtnsIgnorCaseEndsWSltQry_thenWeGetTwoAuthors() {
Expression qualifier = ExpressionFactory
.containsIgnoreCaseExp(Author.NAME.getName(), "Paul")
.andExp(ExpressionFactory
.endsWithExp(Author.NAME.getName(), "h"));
SelectQuery query = new SelectQuery(
Author.class, qualifier);
List<Author> authors = context.performQuery(query);
Author author = authors.get(0);
assertEquals(authors.size(), 1);
assertEquals(author.getName(), "pAuL Smith");
}
4.2 排序功能
4.2.1 升序排序
@Test
void whenAscOrdering_thenWeGetOrderedAuthors() {
SelectQuery query = new SelectQuery(Author.class);
query.addOrdering(Author.NAME.asc());
List<Author> authors = query.select(context);
Author firstAuthor = authors.get(0);
assertEquals(authors.size(), 3);
assertEquals(firstAuthor.getName(), "Paul Xavier");
}
等价写法:
query.addOrdering(Author.NAME.getName(), SortOrder.ASCENDING);
4.2.2 降序排序
@Test
void whenDescOrderingSltQry_thenWeGetOrderedAuthors() {
SelectQuery query = new SelectQuery(Author.class);
query.addOrdering(Author.NAME.desc());
List<Author> authors = query.select(context);
Author firstAuthor = authors.get(0);
assertEquals(authors.size(), 3);
assertEquals(firstAuthor.getName(), "pAuL Smith");
}
等价写法:
query.addOrdering(Author.NAME.getName(), SortOrder.DESCENDING);
5. SQLTemplate 查询
当需要直接操作原生 SQL 时,SQLTemplate
是最佳选择。
5.1 清理数据示例
@After
void deleteAllAuthors() {
SQLTemplate deleteAuthors = new SQLTemplate(
Author.class, "delete from author");
context.performGenericQuery(deleteAuthors);
}
5.2 全量查询
@Test
void givenAuthors_whenFindAllSQLTmplt_thenWeGetThreeAuthors() {
SQLTemplate select = new SQLTemplate(
Author.class, "select * from Author");
List<Author> authors = context.performQuery(select);
assertEquals(authors.size(), 3);
}
5.3 条件查询
@Test
void givenAuthors_whenFindByNameSQLTmplt_thenWeGetOneAuthor() {
SQLTemplate select = new SQLTemplate(
Author.class, "select * from Author where name = 'Vicky Sarra'");
List<Author> authors = context.performQuery(select);
Author author = authors.get(0);
assertEquals(authors.size(), 1);
assertEquals(author.getName(), "Vicky Sarra");
}
6. EJBQLQuery 查询
这是 Cayenne 对 JPA 查询语言的实验性实现,采用参数化对象风格。
6.1 全量查询
@Test
void givenAuthors_whenFindAllEJBQL_thenWeGetThreeAuthors() {
EJBQLQuery query = new EJBQLQuery("select a FROM Author a");
List<Author> authors = context.performQuery(query);
assertEquals(authors.size(), 3);
}
6.2 条件查询
@Test
void givenAuthors_whenFindByNameEJBQL_thenWeGetOneAuthor() {
EJBQLQuery query = new EJBQLQuery(
"select a FROM Author a WHERE a.name = 'Vicky Sarra'");
List<Author> authors = context.performQuery(query);
Author author = authors.get(0);
assertEquals(authors.size(), 1);
assertEquals(author.getName(), "Vicky Sarra");
}
6.3 更新操作
@Test
void whenUpdadingByNameEJBQL_thenWeGetTheUpdatedAuthor() {
EJBQLQuery query = new EJBQLQuery(
"UPDATE Author AS a SET a.name "
+ "= 'Vicky Edison' WHERE a.name = 'Vicky Sarra'");
QueryResponse queryResponse = context.performGenericQuery(query);
EJBQLQuery queryUpdatedAuthor = new EJBQLQuery(
"select a FROM Author a WHERE a.name = 'Vicky Edison'");
List<Author> authors = context.performQuery(queryUpdatedAuthor);
Author author = authors.get(0);
assertNotNull(author);
}
更多示例见 GitHub 源码
7. SQLExec 查询
Cayenne M4 版本引入的流式查询 API,适合简单粗暴的 SQL 操作。
7.1 插入操作
@Test
void whenInsertingSQLExec_thenWeGetNewAuthor() {
int inserted = SQLExec
.query("INSERT INTO Author (name) VALUES ('Baeldung')")
.update(context);
assertEquals(inserted, 1);
}
7.2 更新操作
@Test
void whenUpdatingSQLExec_thenItsUpdated() {
int updated = SQLExec.query(
"UPDATE Author SET name = 'Baeldung' "
+ "WHERE name = 'Vicky Sarra'")
.update(context);
assertEquals(updated, 1);
}
详细用法参考 官方文档
8. 总结
本文系统介绍了 Cayenne ORM 的多种查询方式,从简单的对象查询到原生 SQL 操作,覆盖了实际开发中的常见场景。选择合适的查询方式能有效提升开发效率,避免踩坑。
完整示例代码请访问 GitHub 仓库。