Apache Cayenne中的高级查询

2025/04/14

1. 概述

之前,我们重点介绍了如何开始使用Apache Cayenne。

在本文中,我们将介绍如何使用ORM编写简单和高级查询。

2. 设置

该设置与上一篇文章中使用的设置类似。

此外,每次测试之前,我们都会保存三个作者,最后再删除他们:

  • Paul Xavier
  • pAuL Smith
  • Vicky Sarra

3. 对象选择

让我们从简单的开始,看看如何获取名字中包含“Paul”的所有作者:

@Test
public void whenContainsObjS_thenWeGetOneRecord() {
    List<Author> authors = ObjectSelect.query(Author.class)
        .where(Author.NAME.contains("Paul"))
        .select(context);

    assertEquals(authors.size(), 1);
}

接下来,让我们看看如何在作者name列上应用不区分大小写的LIKE类型查询:

@Test
void whenLikeObjS_thenWeGetTwoAuthors() {
    List<Author> authors = ObjectSelect.query(Author.class)
        .where(Author.NAME.likeIgnoreCase("Paul%"))
        .select(context);

    assertEquals(authors.size(), 2);
}

接下来,endsWith()表达式将仅返回一条记录,因为只有一位作者具有匹配的名称:

@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");
}

更复杂的方法是查询名字在列表中的作者:

@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);
}

nin则相反,结果中只会出现“Vicky”:

@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");
}

请注意,以下两段代码是相同的,因为它们都将创建具有相同参数的相同类型的表达式:

Expression qualifier = ExpressionFactory
    .containsIgnoreCaseExp(Author.NAME.getName(), "Paul");
Author.NAME.containsIgnoreCase("Paul");

以下是ExpressionExpressionFactory类中一些可用表达式的列表:

  • likeExp:用于构建LIKE表达式
  • likeIgnoreCaseExp:用于构建LIKE_IGNORE_CASE表达式
  • containsExp:用于LIKE查询的表达式,其模式与字符串中的任意位置匹配
  • containsIgnoreCaseExp:与containsExp相同,但使用不区分大小写的方法
  • startsWithExp:模式应与字符串的开头匹配
  • startsWithIgnoreCaseExp:类似于startsWithExp但使用不区分大小写的方法
  • endsWithExp:匹配字符串结尾的表达式
  • endsWithIgnoreCaseExp:使用不区分大小写的方法匹配字符串结尾的表达式
  • expTrue:布尔真表达式
  • expFalse:用于布尔值假表达式
  • andExp:用于通过and运算符链接两个表达式
  • orExp:使用或运算符链接两个表达式

更多书面测试可在文章的代码源中找到,请查看Github仓库。

4. SelectQuery

它是用户应用程序中使用最广泛的查询类型,SelectQuery描述了一个简单而强大的API,其行为类似于SQL语法,但仍然使用Java对象和方法,并遵循构建器模式来构建更复杂的表达式。

这里我们讨论的是一种表达式语言,我们使用表达式(构建表达式)又名限定符和排序(对结果进行排序)类来构建查询,然后由ORM转换为原生SQL。

为了看到这一点,我们进行了一些测试,在实践中展示了如何构建一些表达式和对数据进行排序。

让我们应用LIKE查询来获取名字类似于“Paul”的作者:

@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);
}

这意味着如果你不向查询(SelectQuery)提供任何表达式,则结果将是Author表的所有记录。

可以使用containsIgnoreCaseExp表达式执行类似的查询,以获取名称中包含Paul的所有作者,无论字母的大小写如何:

@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);
}

类似地,让我们以不区分大小写的方式(containsIgnoreCaseExp)获取名字包含“Paul”的作者,并且名字以字母h结尾(endsWithExp):

@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");
}

可以使用Ordering类执行升序排列:

@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.asc()),也可以直接使用SortOrder类来获取升序:

query.addOrdering(Author.NAME.getName(), SortOrder.ASCENDING);

相对而言,其顺序为降序:

@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

SQLTemplate也是我们可以与Cayenne一起使用的一种替代方案,可以不使用对象样式查询。

使用SQLTemplate构建查询与编写带有参数的原生SQL语句直接相关。让我们实现一些简单的例子。

以下是我们在每次测试后删除所有作者的方法:

@After
void deleteAllAuthors() {
    SQLTemplate deleteAuthors = new SQLTemplate(Author.class, "delete from author");
    context.performGenericQuery(deleteAuthors);
}

要查找所有记录的作者,我们只需要应用SQL查询select * from Author,我们将直接看到结果是正确的,因为我们恰好有三个保存的作者:

@Test
void givenAuthors_whenFindAllSQLTmplt_thenWeGetThreeAuthors() {
    SQLTemplate select = new SQLTemplate(Author.class, "select * from Author");
    List<Author> authors = context.performQuery(select);

    assertEquals(authors.size(), 3);
}

接下来,让我们获取名为“Vicky Sarra”的作者:

@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

接下来,让我们通过EJBQLQuery查询数据,它是作为在Cayenne中采用Java持久化API的实验的一部分而创建的。

这里,查询采用参数化对象样式;让我们看一些实际的例子。

首先,对所有已保存作者的搜索将如下所示:

@Test
void givenAuthors_whenFindAllEJBQL_thenWeGetThreeAuthors() {
    EJBQLQuery query = new EJBQLQuery("select a FROM Author a");
    List<Author> authors = context.performQuery(query);

    assertEquals(authors.size(), 3);
}

让我们再次使用名字“Vicky Sarra”搜索作者,但现在使用EJBQLQuery:

@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");
}

一个更好的例子是更新作者:

@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);
}

如果我们只想选择一列,应该使用这个查询“select a.name FROM Author a”,Github上文章的源代码中提供了更多示例。

7. SQLExec

SQLExec也是从Cayenne M4版本开始引入的新的流式查询API。

简单的插入如下所示:

@Test
void whenInsertingSQLExec_thenWeGetNewAuthor() {
    int inserted = SQLExec
        .query("INSERT INTO Author (name) VALUES ('Tuyucheng')")
        .update(context);

    assertEquals(inserted, 1);
}

接下来,我们可以根据作者的名字更新作者:

@Test
void whenUpdatingSQLExec_thenItsUpdated() {
    int updated = SQLExec.query(
        "UPDATE Author SET name = 'Tuyucheng' "
        + "WHERE name = 'Vicky Sarra'")
        .update(context);

    assertEquals(updated, 1);
}

我们可以从文档中获得更多详细信息。

8. 总结

在本文中,我们研究了使用Cayenne编写简单和更高级查询的多种方法。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章