Java Platform, Enterprise Edition (Java EE) 8
Учебник по Java EE

Назад Вперёд Содержание

Использование Criteria и Metamodel API для создания типобезопасных запросов

Базовая семантика запроса Criteria состоит из предложений SELECT, FROM и необязательного WHERE, аналогичных запросу JPQL. Запросы Criteria устанавливают эти предложения с использованием объектов Java, поэтому запрос может быть создан типобезопасным способом.

Здесь рассматриваются следующие темы:

Создание запроса Criteria

Интерфейс javax.persistence.criteria.CriteriaBuilder используется для создания

  • Запросов Criteria

  • Выбор

  • Выражения

  • Предикаты

  • Упорядочивание

Чтобы получить объект интерфейса CriteriaBuilder, вызовите метод getCriteriaBuilder объекта EntityManager или EntityManagerFactory.

В следующем коде показано, как получить объект CriteriaBuilder с помощью метода EntityManager.getCriteriaBuilder:

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();

Запросы Criteria строятся путём получения объекта следующего интерфейса:

javax.persistence.criteria.CriteriaQuery

Объекты CriteriaQuery определяют конкретный запрос, который будет перемещаться по одному или нескольким объектам. Получите объекты CriteriaQuery, вызвав один из методов CriteriaBuilder.createQuery. Чтобы создать типобезопасные запросы, вызовите метод CriteriaBuilder.createQuery следующим образом:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

Тип объекта CriteriaQuery должен быть равен ожидаемому типу результата запроса. В предыдущем коде тип объекта установлен в CriteriaQuery<Pet> для запроса, который найдёт объекты сущности Pet.

Следующий фрагмент кода создаёт объект CriteriaQuery для запроса, который возвращает String:

CriteriaQuery<String> cq = cb.createQuery(String.class);

Корень запроса

Для конкретного объекта CriteriaQuery корневая сущность запроса, от которой происходит вся навигация, называется корнем запроса. Это похоже на предложение FROM в запросе JPQL.

Создайте корень запроса, вызвав метод from объекта CriteriaQuery. Аргументом метода from является либо класс сущности, либо объект EntityType<T> для сущности.

Следующий код устанавливает сущность Pet корнем запроса:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);

Следующий код устанавливает класс Pet корнем запроса, используя объект EntityType<T>:

EntityManager em = ...;
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet_);

Запросы Criteria могут иметь несколько корней запросов. Обычно это происходит, когда запрос перемещается по нескольким объектам.

Следующий код имеет два объекта Root:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet1 = cq.from(Pet.class);
Root<Pet> pet2 = cq.from(Pet.class);

Запрос отношений с помощью объединений

Для запросов, которые переходят к связанным классам сущностей, запрос должен определить соединение со связанной сущностью, вызвав один из методов From.join у корневого объекта запроса или другого объекта соединения. Методы join аналогичны ключевому слову JOIN в JPQL.

Цель объединения использует класс Metamodel типа EntityType<T>, чтобы указать персистентное поле или свойство присоединяемого объекта.

Методы join возвращают объект типа Join<X, Y>, где X — исходная сущность, а Y является целью объединения. В следующем фрагменте кода Pet является исходной сущностью, Owner — целью, а Pet_ — статически сгенерированным классом метамодели:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);

Вы можете образовать цепочку объединений, чтобы перейти к связанным объектам целевой сущности, не создавая объект Join<X, Y> для каждого объединения:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = pet.join(Pet_.owners).join(Owner_.addresses);

Навигация по пути в запросах Criteria

Объекты Path, которые используются в предложениях SELECT и WHERE запроса Criteria, могут быть запросами корневых объектов, объектами объединения или другими объектами Path. Используйте метод Path.get, чтобы перейти к атрибутам сущностей запроса.

Аргументом метода get является соответствующий атрибут класса метамодели сущности. Атрибут может быть либо однозначным атрибутом, указанным @SingularAttribute в классе Metamodel, либо атрибутом со значением коллекции, указанным одним из @CollectionAttribute, @SetAttribute, @ListAttribute или @MapAttribute.

Следующий запрос возвращает имена всех домашних животных в хранилище данных. Метод get вызывается у корня запроса pet с атрибутом name класса метамодели Pet_ сущности Pet в качестве аргумента:

CriteriaQuery<String> cq = cb.createQuery(String.class);

Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get(Pet_.name));

Ограничение результатов запроса Criteria

Условия, которые устанавливаются путём вызова метода CriteriaQuery.where, могут ограничивать результаты запроса к объекту CriteriaQuery. Вызов метода where аналогичен установке условия WHERE в запросе JPQL.

Метод where выполняет объекты интерфейса Expression для ограничения результатов в соответствии с условиями выражений. Для создания объектов Expressionиспользуются методы, определённые в интерфейсах Expression и CriteriaBuilder.

Методы интерфейса Expression

Объект Expression используется в предложениях SELECT, WHERE или HAVING запроса. Таблица 43-1 показывает условные методы, которые могут использоваться с объектами Expression.

Таблица 43-1. Условные методы в интерфейсе Expression

Метод

Описание

isNull

Проверяет, является ли выражение null

isNotNull

Проверяет, не является ли выражение null

in

Проверяет, находится ли выражение в списке значений

В следующем запросе используется метод Expression.isNull, чтобы найти всех домашних животных, у которых атрибут color равен null:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).isNull());

В следующем запросе используется метод Expression.in для поиска всех коричневых и чёрных питомцев:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).in("brown", "black"));

Метод in также может проверять, является ли атрибут членом коллекции.

Методы Expression в интерфейсе CriteriaBuilder

Интерфейс CriteriaBuilder определяет дополнительные методы для создания выражений. Эти методы соответствуют арифметическим, строковым операторам, операторам даты, времени, функции case и функциям JPQL. Таблица 43-2 показывает условные методы, которые могут использоваться с объектами CriteriaBuilder.

Таблица 43-2. ​​Условные методы в интерфейсе CriteriaBuilder

Условный метод

Описание

equal

Проверяет, равны ли два выражения

notEqual

Проверяет, что два выражения не равны

gt

Проверяет, что первое числовое выражение больше второго

ge

Проверяет, что первое числовое выражение больше или равно второму

lt

Проверяет, что первое числовое выражение меньше второго

le

Проверяет, что первое числовое выражение меньше или равно второму

between

Проверяет, находится ли значение первого выражения между вторым и третьим выражениями

like

Проверяет, соответствует ли выражение заданному шаблону

В следующем коде используется метод CriteriaBuilder.equal:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido"));

В следующем коде используется метод CriteriaBuilder.gt:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Date someDate = new Date(...);
cq.where(cb.gt(pet.get(Pet_.birthday), date));

В следующем коде используется метод CriteriaBuilder.between:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Date firstDate = new Date(...);
Date secondDate = new Date(...);
cq.where(cb.between(pet.get(Pet_.birthday), firstDate, secondDate));

В следующем коде используется метод CriteriaBuilder.like:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.like(pet.get(Pet_.name), "*do"));

Чтобы указать несколько условных предикатов, используйте методы составных предикатов интерфейса CriteriaBuilder, как показано в таблице 43-3.

Таблица 43-3. Методы составных предикатов в интерфейсе CriteriaBuilder

Метод

Описание

and

Логическое AND двух булевых выражений

or

Логическое OR двух булевых выражений

not

Логическое отрицание данного булева выражения

В следующем коде показано использование составных предикатов в запросах:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido")
        .and(cb.equal(pet.get(Pet_.color), "brown")));

Управление результатами запроса Criteria

Для запросов, которые возвращают более одного результата, часто полезно организовать эти результаты. Интерфейс CriteriaQuery определяет следующие методы упорядочения и группировки:

  • Метод orderBy упорядочивает результаты запроса в соответствии с атрибутами объекта

  • Метод groupBy группирует результаты запроса вместе в соответствии с атрибутами объекта, а метод having ограничивает эти группы в соответствии с условием

Здесь рассматриваются следующие темы:

Упорядочение результатов

Чтобы упорядочить результаты запроса, вызовите метод CriteriaQuery.orderBy, передав объект Order. Чтобы создать объект Order, вызовите метод CriteriaBuilder.asc или CriteriaBuilder.desc. Метод asc используется для упорядочивания результатов по возрастанию значения переданного параметра выражения. Метод desc используется для упорядочивания результатов по убыванию значения переданного параметра выражения. Следующий запрос показывает использование метода desc:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
cq.orderBy(cb.desc(pet.get(Pet_.birthday)));

В этом запросе результаты будут упорядочены по дню рождения питомца от наивысшего к низшему. То есть домашние животные, рождённые в декабре, появятся в результатах выборки раньше домашних животных, рождённых в мае.

Следующий запрос показывает использование метода asc:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = pet.join(Pet_.owners).join(Owner_.address);
cq.select(pet);
cq.orderBy(cb.asc(address.get(Address_.postalCode)));

В этом запросе результаты будут упорядочены по почтовому индексу владельца домашнего животного от самого низкого до самого высокого. То есть домашние животные, владелец которых живёт по почтовому индексу 10001, появятся в результатах выборки раньше домашних животных, владелец которых живёт по почтовому индексу 91000.

Если в orderBy передаётся более одного объекта Order, приоритет определяется порядком их появления в списке аргументов orderBy. Первый объект Order имеет приоритет.

Следующий код упорядочивает результаты по нескольким критериям:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);
cq.select(pet);
cq.orderBy(cb.asc(owner.get(Owner_.lastName)), owner.get(Owner_.firstName)));

Результаты этого запроса будут упорядочены в алфавитном порядке по фамилии владельца домашнего животного, а затем по имени.

Группировка результатов

Метод CriteriaQuery.groupBy разделяет результаты запроса на группы. Чтобы установить эти группы, передайте выражение для groupBy:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.groupBy(pet.get(Pet_.color));

Этот запрос возвращает все сущности Pet и группирует результаты по цвету питомца.

Используйте метод CriteriaQuery.having в сочетании с groupBy для фильтрации групп. Метод having принимает условное выражение в качестве параметра, ограничивает результат запроса в соответствии с условным выражением:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.groupBy(pet.get(Pet_.color));
cq.having(cb.in(pet.get(Pet_.color)).value("brown").value("blonde"));

В этом примере запрос группирует возвращённые сущности Pet по цвету, как в предыдущем примере. Однако единственными возвращаемыми группами будут объекты Pet, для атрибута color которых установлено значение brown или blonde. То есть домашние животные серого цвета не попадут в результаты запроса.

Выполнение запросов

Чтобы подготовить запрос к выполнению, создайте объект TypedQuery<T> с типом результата запроса, передав объект CriteriaQuery в EntityManager.createQuery.

Чтобы выполнить запрос, вызовите getSingleResult или getResultList для объекта TypedQuery<T>.

Однозначные результаты запроса

Используйте метод TypedQuery<T>.getSingleResult для выполнения запросов, которые возвращают один результат:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
...
TypedQuery<Pet> q = em.createQuery(cq);
Pet result = q.getSingleResult();

Результаты запроса с коллекцией

Используйте метод TypedQuery<T>.getResultList для выполнения запросов, которые возвращают коллекцию объектов:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
...
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> results = q.getResultList();

Назад Вперёд Содержание
Логотип Oracle  Copyright © 2017, Oracle и/или её дочерних компаний. Все права защищены. Версия перевода 1.0.5 (Java EE Tutorial — русскоязычная версия)