Продвинутые запросы в Apache Cayenne

Расширенный запрос в Apache Cayenne

1. обзор

Previously, мы сосредоточились на том, как начать работу с Apache Cayenne.

В этой статье мы расскажем, как писать простые и сложные запросы с помощью ORM.

2. Настроить

Настройка аналогична той, которая использовалась в предыдущей статье.

Кроме того, перед каждым тестом мы сохраняем трех авторов и в конце удаляем их:

  • Пол Ксавье

  • Пол Смит

  • Вики Сарра

3. ObjectSelectс

Давайте начнем с простого и посмотрим, как мы можем получить всех авторов с именами, содержащими слово «Paul»:

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

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

Затем давайте посмотрим, как мы можем применить тип запроса LIKE без учета регистра в столбце имени автора:

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

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

Затем выражениеendsWith() вернет только одну запись, так как только один автор имеет соответствующее имя:

@Test
void whenEndsWithObjS_thenWeGetOrderedAuthors() {
    List 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 authors = ObjectSelect.query(Author.class)
      .where(Author.NAME.in(names))
      .select(context);

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

nin наоборот, здесь в результате будет только «Вики»:

@Test
void whenNinObjS_thenWeGetAuthors() {
    List names = Arrays.asList(
      "Paul Xavier", "pAuL Smith");
    List 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");

Вот список некоторых доступных выражений в классахExpression иExpressionFactory:

  • likeExp: для построения выражения LIKE

  • likeIgnoreCaseExp: используется для построения выражения LIKE_IGNORE_CASE

  • containsExp: выражение для запроса LIKE с сопоставлением с образцом в любом месте строки

  • containsIgnoreCaseExp: то же, что иcontainsExp, но с использованием подхода без учета регистра

  • startsWithExp: шаблон должен соответствовать началу строки

  • startsWithIgnoreCaseExp: аналогичноstartsWithExp, но с использованием подхода без учета регистра

  • endsWithExp: выражение, которое соответствует концу строки

  • endsWithIgnoreCaseExp: выражение, которое соответствует концу строки с использованием подхода без учета регистра

  • expTrue: для логического выраженияtrue

  • expFalse: для логического выраженияfalse

  • andExp: используется для объединения двух выражений в цепочку с операторомand

  • orExp: объединить два выражения в цепочку с помощью оператораor

Дополнительные письменные тесты доступны в исходном коде статьи, пожалуйста, проверьте репозиторийGithub.

4. SelectQueryс

Это наиболее широко используемый тип запроса в пользовательских приложениях. SelectQuery описывает простой и мощный API, который действует как синтаксис SQL, но все же с объектами и методами Java, за которыми следуют шаблоны построителя для построения более сложных выражений.

Здесь мы говорим о языке выражений, в котором мы создаем запросы, используя классыExpression (для построения выражений), также известные как квалификатор, иOrdering (для сортировки результатов), которые затем преобразуются ORM в собственный SQL.

Чтобы увидеть это в действии, мы собрали несколько тестов, которые на практике показывают, как создавать выражения и сортировать данные.

Давайте применим запрос LIKE, чтобы получить авторов с таким именем, как «Paul»:

@Test
void whenLikeSltQry_thenWeGetOneAuthor() {
    Expression qualifier
      = ExpressionFactory.likeExp(Author.NAME.getName(), "Paul%");
    SelectQuery query
      = new SelectQuery(Author.class, qualifier);

    List 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 authors = context.performQuery(query);

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

Аналогичным образом, давайте получим авторов с именами, содержащими «Paul», без учета регистра (containsIgnoreCaseExp) и с именем, которое заканчивается (endsWithExp) буквой 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 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 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 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 authors = context.performQuery(select);

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

Затем давайте возьмем автора с именем «Вики Сарра»:

@Test
void givenAuthors_whenFindByNameSQLTmplt_thenWeGetOneAuthor() {
    SQLTemplate select = new SQLTemplate(
      Author.class, "select * from Author where name = 'Vicky Sarra'");
    List authors = context.performQuery(select);
    Author author = authors.get(0);

    assertEquals(authors.size(), 1);
    assertEquals(author.getName(), "Vicky Sarra");
}

6. EJBQLQueryс

Затем давайте запросим данные черезEJBQLQuery,, которые были созданы в рамках эксперимента по внедрению Java Persistence API в Cayenne.

Здесь запросы применяются с параметризованным стилем объекта; давайте посмотрим на несколько практических примеров.

Во-первых, поиск всех сохраненных авторов будет выглядеть так:

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

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

Давайте снова выполним поиск по автору с именем «Вики Сарра», но теперь сEJBQLQuery:

@Test
void givenAuthors_whenFindByNameEJBQL_thenWeGetOneAuthor() {
    EJBQLQuery query = new EJBQLQuery(
      "select a FROM Author a WHERE a.name = 'Vicky Sarra'");
    List 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 authors = context.performQuery(queryUpdatedAuthor);
    Author author = authors.get(0);

    assertNotNull(author);
}

Если мы просто хотим выбрать столбец, мы должны использовать этот запрос“select a.name FROM Author a”. Дополнительные примеры доступны в исходном коде статьи наGithub.

7. SQLExecс

SQLExec - это также новый свободный API запросов, представленный в версии M4 Cayenne.

Простая вставка выглядит так:

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

    assertEquals(inserted, 1);
}

Далее мы можем обновить автора на основе его имени:

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

    assertEquals(updated, 1);
}

Мы можем получить более подробную информацию изdocumentation.

8. Заключение

В этой статье мы рассмотрели несколько способов написания простых и более сложных запросов с помощью Cayenne.

Как всегда, исходный код этой статьи можно найти вover on GitHub.