Перейти к основному содержимому

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

· 7 мин. чтения

1. Обзор

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

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

2. Настройка

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

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

  • Пол Ксавьер
  • Пол Смит
  • Вики Сарра

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

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

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

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

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

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

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

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

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

  • likeExp : для построения выражения LIKE
  • likeIgnoreCaseExp : используется для построения выражения LIKE_IGNORE_CASE.
  • containsExp : выражение для запроса LIKE с шаблоном, совпадающим в любом месте строки.
  • containsIgnoreCaseExp : то же, что и containsExp , но с использованием подхода без учета регистра .
  • startWithExp : шаблон должен соответствовать началу строки
  • startWithIgnoreCaseExp : похоже на startWithExp, но с использованием подхода без учета регистра .
  • endWithExp : выражение, соответствующее концу строки
  • endWithIgnoreCaseExp : выражение, которое соответствует концу строки с использованием подхода без учета регистра.
  • expTrue : для логического истинного выражения
  • expFalse : для логического ложного выражения
  • andExp : используется для объединения двух выражений с помощью оператора and
  • orExp : объединить два выражения в цепочку с помощью оператора или

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

4. Выберите запрос

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

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

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

Давайте применим запрос 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 , чтобы получить всех авторов с именем, содержащим Пол, независимо от регистра букв:

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

Точно так же давайте получим авторов с именами, содержащими «Paul», без учета регистра ( containsIgnoreCaseExp ) и с именами, которые заканчиваются ( endWithExp ) на букву 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");
}

Восходящий порядок может быть выполнен с использованием класса 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. Шаблон SQL

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

Далее найдем Автора с именем «Вики Сарра»:

@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. EJBQL-запрос

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

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

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

@Test
void givenAuthors_whenFindAllEJBQL_thenWeGetThreeAuthors() {
EJBQLQuery query = new EJBQLQuery("select a FROM Author a");
List<Author> 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<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);
}

Если мы просто хотим выбрать столбец, мы должны использовать этот запрос «выбрать a.name FROM Author a» . Больше примеров доступно в исходном коде статьи на Github .

7. SQLExec

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

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

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

assertEquals(inserted, 1);
}

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

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

assertEquals(updated, 1);
}

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

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

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

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