Criteria API

Открыта

Знакомство с 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, 12:03
В task1602 валидатору нужно, чтобы методы select и where были вызваны в одной инструкции.
Олег
Уровень 106
Expert
22 октября 2024, 18:28
Что такое 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, 01:13
пропускаем..
И. Ж.
Уровень 41
15 мая 2024, 11:16
Катавасия какая то
Надежда
Уровень 104
Expert
11 октября 2023, 20:07
Исправьте ошибку: вместо 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, 06:51
Оно, может, и с разными переменными root будет работать. Вот то, что переменная root не определена в этих примерах, вот от этого точно не будет работать.
Илья ИлюшечкинРаботает в В поискеExpert
21 июля 2023, 18:40
"Посмотри на строки 3 и 4 первого примера:" я один вижу тут не соответствие?
Роман ИBackend Developer в СберТех
25 января 2023, 12:51
Допустим я хочу произвести поиск в любом поле сущности? один из вариантов сделать сущность toString через пробелы и искать там, можно конечно дергать и искать каждое поле с условие or. Как сие реализовать в коде? кстати, часто вижу, что уже набивают предикаты и строят спецификацию. Хотелось бы это увидеть
КонстантинQA Engineer в TGI - ITExpert
22 января 2023, 12:46
https://prnt.sc/4nawCZLgZEMT Данный клас уже считается deprecated
w4t3rcs Java Developer
16 сентября 2023, 10:33
Руслан
Уровень 108
Expert
18 декабря 2022, 18:15
Непонятно что такое Root, как он связан с CriteriaBuilder и CriteriaQuery. Мало что объяснили в лекции. Если кому-то, как и мне было мало материала, можно конкретнее почитать здесь
Алексей
Уровень 91
Expert
11 декабря 2022, 15:30
Вторая задача чувствительна к стилистике кода, проходит валидацию только при определенном формате