Criteria API

Модуль 4. Работа с БД
16 уровень , 0 лекция
Открыта

Знакомство с Criteria API

В Hibernate есть два способа написания запросов к базе:

  • Hibernate Query Language
  • Criteria API

С первым ты уже давно познакомился, настало время познакомиться с Criteria API. Это очень мощный инструмент, в какой-то момент он был даже популярнее HQL. Сейчас он уже не такой популярный, но для некоторых задач он определенно будет лучшим решением чем HQL.

В любом случае, нельзя изучать Hibernate и не познакомиться с Criteria API. Давай напишем небольшой пример, а потом его разберем. Например, запросим всех сотрудников (Employee) из базы. Вот что у нас выйдет:


CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> critQuery = builder.createQuery(Employee.class);
 
Root<Employee> root = critQuery.from(Employee.class);
critQuery.select(root);
 
Query<Employee> query = session.createQuery(critQuery);
List<Employee> results = query.getResultList();

Выглядит сложновато. Давай для сравнения запишем тот же запрос на HQL:


String hqlQuery = "from Employee";
 
Query<Employee> query = session.createQuery(hqlQuery);
List<Employee> results = query.getResultList();

Обрати внимание, что две последних строки у обоих примеров практически идентичны: мы создаем объект Query и с его помощью получаем List. Это намекает на то, что и остальные строки делают что-то идентичное.

Посмотри на строки 3 и 4 первого примера:


Root<Employee> root = critQuery.from(Employee.class);
critQuery.select(root);

Давай запишем их в одну строку:


critQuery.select(critQuery.from(Employee.class));

Ничего не напоминает? А если раскрасить немного по-другому:


critQuery.select(critQuery.from(Employee.class));

Да, это такое хитрое конструирование запроса SELECT FROM.

Примеры работы с Criteria API

Для лучшего понимания я просто приведу несколько примеров.

Запрос 1. Получить всех сотрудников с зарплатой выше 10 тысяч:


critQuery.select(critQuery.from(Employee.class)).where(builder.gt(root.get("salary"), 10000));

Запрос 2. Получить всех сотрудников с зарплатой меньше 50 тысяч:


critQuery.select(critQuery.from(Employee.class)).where(builder.lt(root.get("salary"), 50000));

Запрос 3. Получить всех сотрудников, должность которых содержит слово "тест":


critQuery.select(critQuery.from(Employee.class)).where(builder.like(root.get("occupation"), "%тест%"));

Запрос 4. Получить всех сотрудников с зарплатой от 10 до 50 тысяч:


critQuery.select(critQuery.from(Employee.class)).where(builder.between(root.get("salary"), 10000, 50000));

Запрос 5. Получить всех сотрудников, у которых имя равно null:


critQuery.select(critQuery.from(Employee.class)).where(builder.isNull(root.get("name")));

Запрос 6. Получить всех сотрудников, у которых имя не равно null:


critQuery.select(critQuery.from(Employee.class)).where(builder.isNotNull(root.get("name")));

Это просто такой хитрый способ конструировать запрос:

  • Сначала ты получаешь объект CriteriaBuilder.
  • Затем с его помощью создаешь объект CriteriaQuery.
  • Затем начинаешь добавлять к нему части с помощью CriteriaQuery и CriteriaBuilder.

Именно таким образом ты можешь задать параметры для:

  • SELECT
  • FROM
  • WHERE

Также с помощью CriteriaBuilder ты можешь конструировать различные условия для WHERE.

Продвинутая работа с Criteria API

С помощью Criteria API можно сконструировать запрос любой сложности. И это отличная новость. Например, ты хочешь сложное условие для WHERE. Вот как это можно сделать:


Predicate greaterThan = builder.gt(root.get("salary"), 1000);
Predicate testers = builder.like(root.get("occupation"), "тест%");
 
critQuery.select(critQuery.from(Employee.class)).where(builder.or(greaterThan, testers));

Если ты хочешь написать AND вместо OR, то тебе нужно поменять только последнюю строчку:


critQuery.select(critQuery.from(Employee.class)).where(builder.and(greaterThan, testers));

Все на самом деле очень просто. Давай я приведу таблицу с несколькими сравнениями:

SQL Метод Полная запись
a < b lt(a, b) builder.lt(a, b)
a > b gt(a, b) builder.gt(a, b)
a OR b or(a, b) builder.or(a, b)
a AND b and(a, b) builder.and(a, b)
a LIKE b like(a, b) builder.like(a, b)
a BETWEEN (c, d) between(a, c, d) builder.between(a, c, d)
a IS NULL isNull(a) builder.isNull(a)
a IS NOT NULL isNotNull(a) builder.isNotNull(a)

Все просто, неправда ли?

А как нам добавить сортировку в запрос? Очень просто:


critQuery.select( critQuery.from(Employee.class) );
critQuery.where( builder.and(greaterThan, testers) );
critQuery.orderBy( builder.asc(root.get("salary"), builder.desc(root.get("joinDate") )

Ты просто вызываешь у объекта CrteriaQuery метод orderBy() и передаешь в него нужные параметры.

Вот как этот же запрос будет выглядеть на HQL. Сравни:

select * from Employee where (…) and (…) order by 'salary' asc, 'joinDate' desc

Только нужно запомнить 3 вещи:

  • Ключевые операторы типа SELECT, FROM, WHERE вызываются у объекта CriteriaQuery.
  • Вспомогательные операторы типа AND, OR, DESC вызываются у объекта CriteriaBuilder.
  • Имена полей берутся через get() у объекта Root.
1
Задача
Модуль 4. Работа с БД, 16 уровень, 0 лекция
Недоступна
Знакомство с Criteria API
С помощью Criteria API напиши запрос, который выберет все записи из таблицы animal_table...
1
Задача
Модуль 4. Работа с БД, 16 уровень, 0 лекция
Недоступна
Фильтр результата в Criteria API
С помощью Criteria API напиши запрос, который выберет все записи из таблицы animal_table со значением поля family, не равным null.
Комментарии (12)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Уровень 112
21 июля 2025
В task1602 валидатору нужно, чтобы методы select и where были вызваны в одной инструкции.
Олег Уровень 106 Expert
22 октября 2024
Что такое Criteria API и для чего он используется? Начиная с версии 5.2 Hibernate Criteria API объявлен deprecated. Вместо него рекомендуется использовать JPA Criteria API. JPA Criteria API - это актуальный API, используемый только для выборки(select) сущностей из БД в более объектно-ориентированном стиле. Основные преимущества JPA Criteria API: ❖ ошибки могут быть обнаружены во время компиляции; ❖ позволяет динамически формировать запросы на этапе выполнения приложения. Основные недостатки: ❖ нет контроля над запросом, сложно отловить ошибку ❖ влияет на производительность, множество классов Для динамических запросов - фрагменты кода создаются во время выполнения - JPA Criteria API является предпочтительней. Вот некоторые области применения Criteria API: Criteria API поддерживает проекцию, которую мы можем использовать для агрегатных функций вроде sum(), min(), max() и т.д. Criteria API может использовать ProjectionList для извлечения данных только из выбранных колонок. Criteria API может быть использована для join запросов с помощью соединения нескольких таблиц, используя методы createAlias(), setFetchMode() и setProjection(). Criteria API поддерживает выборку результатов согласно условиям (ограничениям). Для этого используется метод add() с помощью которого добавляются ограничения (Restrictions). Criteria API позволяет добавлять порядок (сортировку) к результату с помощью метода addOrder().
Андрей Уровень 109
7 июля 2024
пропускаем..
И. Ж. Уровень 41
15 мая 2024
Катавасия какая то
Надежда Уровень 104 Expert
11 октября 2023
Исправьте ошибку: вместо critQuery.select(critQuery.from(Employee.class)).where(builder.gt(root.get("salary"), 10000)); надо critQuery.select(root).where(builder.gt(root.get("salary"), 10000)); Объяснение: У вас в строке запроса есть ссылка на root. Соответственно, можно предположить, что ранее вы сделали: Root<Employee> root = critQuery.from(Employee.class); что означает, что вы уже получили все объекты Employee (root представляет собой From для сущности Employee). В своем запросе вы используете critQuery.from(Employee.class) внутри select, что создает новый экземпляр From для сущности Employee. Это лишнее и возникнет проблема, запрос будет работать некорректно. Поэтому в запросе надо использовать critQuery.select(root). Аналогичная ошибка и во всех других примерах.
Дмитрий Уровень 117 Expert
2 февраля 2024
Оно, может, и с разными переменными root будет работать. Вот то, что переменная root не определена в этих примерах, вот от этого точно не будет работать.
Илья Илюшечкин Уровень 1 Expert
21 июля 2023
"Посмотри на строки 3 и 4 первого примера:" я один вижу тут не соответствие?
Роман И Уровень 20
25 января 2023
Допустим я хочу произвести поиск в любом поле сущности? один из вариантов сделать сущность toString через пробелы и искать там, можно конечно дергать и искать каждое поле с условие or. Как сие реализовать в коде? кстати, часто вижу, что уже набивают предикаты и строят спецификацию. Хотелось бы это увидеть
Константин Уровень 100 Expert
22 января 2023
https://prnt.sc/4nawCZLgZEMT Данный клас уже считается deprecated
Руслан Уровень 108 Expert
18 декабря 2022
Непонятно что такое Root, как он связан с CriteriaBuilder и CriteriaQuery. Мало что объяснили в лекции. Если кому-то, как и мне было мало материала, можно конкретнее почитать здесь
Алексей Уровень 91 Expert
11 декабря 2022
Вторая задача чувствительна к стилистике кода, проходит валидацию только при определенном формате