Знакомство с 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.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ