1. Обзор
В этом уроке мы обсудим очень полезную функцию JPA — Criteria Queries.
Это позволяет нам писать запросы, не выполняя необработанный SQL, а также дает нам некоторый объектно-ориентированный контроль над запросами, что является одной из основных функций Hibernate. Criteria API позволяет нам создавать объект запроса критериев программно, где мы можем применять различные виды правил фильтрации и логических условий.
Начиная с Hibernate 5.2, API-интерфейс Hibernate Criteria устарел, и новые разработки сосредоточены на API-интерфейсе JPA Criteria. Мы рассмотрим, как использовать Hibernate и JPA для построения запросов Criteria.
2. Зависимости Maven
Чтобы проиллюстрировать API, мы будем использовать эталонную реализацию JPA Hibernate.
Чтобы использовать Hibernate, мы обязательно добавим его последнюю версию в наш файл pom.xml
:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.2.Final</version>
</dependency>
Мы можем найти последнюю версию Hibernate здесь.
3. Простой пример с использованием критериев
Давайте начнем с рассмотрения того, как извлекать данные с помощью запросов Criteria. Мы рассмотрим, как получить все экземпляры определенного класса из базы данных.
У нас есть класс Item
, представляющий кортеж «ITEM»
в базе данных:
public class Item implements Serializable {
private Integer itemId;
private String itemName;
private String itemDescription;
private Integer itemPrice;
// standard setters and getters
}
Давайте рассмотрим простой запрос критериев, который извлечет все строки «ПУНКТ»
из базы данных:
Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Item> cr = cb.createQuery(Item.class);
Root<Item> root = cr.from(Item.class);
cr.select(root);
Query<Item> query = session.createQuery(cr);
List<Item> results = query.getResultList();
Приведенный выше запрос является простой демонстрацией того, как получить все элементы. Давайте посмотрим это шаг за шагом:
- Создайте экземпляр
Session
из объектаSessionFactory.
- Создайте экземпляр C
riteriaBuilder
, вызвав метод getCriteriaBuilder().
- Создайте экземпляр
CriteriaQuery
, вызвав метод createQuery()
CriteriaBuilder .
`` - Создайте экземпляр
Query
, вызвав метод createQuery(
) сеанса.
- Вызов метода
getResultList() объекта
запроса
, который дает нам результаты
Теперь, когда мы рассмотрели основы, давайте перейдем к некоторым особенностям запроса критериев.
3.1. Использование выражений
CriteriaBuilder можно использовать для ограничения результатов запроса на основе определенных условий `` с помощью метода CriteriaQuery
where()
и предоставления выражений
, созданных CriteriaBuilder
.
Давайте посмотрим на некоторые примеры часто используемых выражений
.
Для того, чтобы получить предметы стоимостью более 1000:
cr.select(root).where(cb.gt(root.get("itemPrice"), 1000));
Далее, получение предметов с itemPrice
меньше 1000:
cr.select(root).where(cb.lt(root.get("itemPrice"), 1000));
Элементы, имеющие itemName
, содержат Chair
:
cr.select(root).where(cb.like(root.get("itemName"), "%chair%"));
Записи, имеющие itemPrice
от 100 до 200:
cr.select(root).where(cb.between(root.get("itemPrice"), 100, 200));
Предметы, имеющие itemName
в Skate Board
, Paint
and Glue
:
cr.select(root).where(root.get("itemName").in("Skate Board", "Paint", "Glue"));
Чтобы проверить, является ли данное свойство нулевым:
cr.select(root).where(cb.isNull(root.get("itemDescription")));
Чтобы проверить, не является ли данное свойство нулевым:
cr.select(root).where(cb.isNotNull(root.get("itemDescription")));
Мы также можем использовать методы isEmpty()
и isNotEmpty()
, чтобы проверить, является ли список
внутри класса пустым или нет.
Кроме того, мы можем объединить два или более из приведенных выше сравнений. Criteria API позволяет нам легко связывать выражения :
Predicate[] predicates = new Predicate[2];
predicates[0] = cb.isNull(root.get("itemDescription"));
predicates[1] = cb.like(root.get("itemName"), "chair%");
cr.select(root).where(predicates);
Чтобы добавить два выражения с логическими операциями:
Predicate greaterThanPrice = cb.gt(root.get("itemPrice"), 1000);
Predicate chairItems = cb.like(root.get("itemName"), "Chair%");
Элементы с определенными выше условиями, объединенными логическим ИЛИ
:
cr.select(root).where(cb.or(greaterThanPrice, chairItems));
Чтобы получить элементы, соответствующие определенным выше условиям, объединенным логическим И
:
cr.select(root).where(cb.and(greaterThanPrice, chairItems));
3.2. Сортировка
Теперь, когда мы знаем основы использования Criteria
, давайте посмотрим на функции сортировки Criteria
.
В следующем примере мы упорядочиваем список в порядке возрастания имени, а затем в порядке убывания цены:
cr.orderBy(
cb.asc(root.get("itemName")),
cb.desc(root.get("itemPrice")));
В следующем разделе мы рассмотрим, как выполнять агрегатные функции.
3.3. Проекции, агрегаты и группирующие функции
Теперь давайте посмотрим на различные агрегатные функции.
Получить количество строк:
CriteriaQuery<Long> cr = cb.createQuery(Long.class);
Root<Item> root = cr.from(Item.class);
cr.select(cb.count(root));
Query<Long> query = session.createQuery(cr);
List<Long> itemProjected = query.getResultList();
Ниже приведен пример агрегатных функций — Агрегатная
функция для среднего
:
CriteriaQuery<Double> cr = cb.createQuery(Double.class);
Root<Item> root = cr.from(Item.class);
cr.select(cb.avg(root.get("itemPrice")));
Query<Double> query = session.createQuery(cr);
List avgItemPriceList = query.getResultList();
Другими полезными агрегатными методами являются sum()
, max()
, min()
, count()
и т. д. ** ** ``
3.4. Обновление критериев
Начиная с JPA 2.1, поддерживается выполнение обновлений базы данных с использованием Criteria
API.
CriteriaUpdate
имеет метод set()
, который можно использовать для предоставления новых значений для записей базы данных:
CriteriaUpdate<Item> criteriaUpdate = cb.createCriteriaUpdate(Item.class);
Root<Item> root = criteriaUpdate.from(Item.class);
criteriaUpdate.set("itemPrice", newPrice);
criteriaUpdate.where(cb.equal(root.get("itemPrice"), oldPrice));
Transaction transaction = session.beginTransaction();
session.createQuery(criteriaUpdate).executeUpdate();
transaction.commit();
В приведенном выше фрагменте мы создаем экземпляр CriteriaUpdate<Item>
из CriteriaBuilder
и используем его метод set()
для предоставления новых значений для itemPrice
. Чтобы обновить несколько свойств, нам просто нужно несколько раз вызвать метод set()
.
3.5. КритерииУдалить
CriteriaDelete
включает операцию удаления с помощью Criteria
API.
Нам просто нужно создать экземпляр CriteriaDelete
и использовать метод where()
для применения ограничений:
CriteriaDelete<Item> criteriaDelete = cb.createCriteriaDelete(Item.class);
Root<Item> root = criteriaDelete.from(Item.class);
criteriaDelete.where(cb.greaterThan(root.get("itemPrice"), targetPrice));
Transaction transaction = session.beginTransaction();
session.createQuery(criteriaDelete).executeUpdate();
transaction.commit();
4. Преимущество над HQL
В предыдущих разделах мы рассмотрели, как использовать запросы с критериями.
Очевидно, что главное и наиболее весомое преимущество Criteria Queries по сравнению с HQL — приятный, чистый, объектно-ориентированный API.
Мы можем просто писать более гибкие динамические запросы по сравнению с простым HQL. Логика может быть реорганизована с помощью IDE и обладает всеми преимуществами безопасности типов самого языка Java.
Конечно, есть и некоторые недостатки, особенно в отношении более сложных соединений.
Таким образом, нам обычно приходится использовать лучший инструмент для работы — в большинстве случаев это может быть Criteria API, но определенно есть случаи, когда нам придется перейти на более низкий уровень.
5. Вывод
В этой статье мы сосредоточились на основах запросов Criteria в Hibernate и JPA, а также на некоторых дополнительных функциях API.
Обсуждаемый здесь код доступен в репозитории GitHub .