Группировка и агрегирующие функции

Как делать простые запросы на Criteria API ты уже разобрался. Давай посмотрим, как делать более сложные запросы.

Например, мы хотим написать запрос, чтобы определить количество сотрудников в компании. Вот как он будет выглядеть на HQL:

select count(*) from Employee

А вот так на Criteria API:


CriteriaQuery<Long> critQuery = builder.createQuery(Long.class);
critQuery.select(builder.count(critQuery.from(Employee.class)));

Полностью весь Java-код будет выглядеть так:


CriteriaBuilder builder = session.getCriteriaBuilder();
 
CriteriaQuery<Long> critQuery = builder.createQuery(Long.class);
critQuery.select(builder.count(critQuery.from(Employee.class)));
 
Query<Long> query = session.createQuery(critQuery);
Long count = query.getSingleResult();

И он же с использованием HQL:


String hqlQuery = "select count(*) from Employee";
 
Query<Long> query = session.createQuery(hqlQuery);
Long count = query.getSingleResult();

Теперь давай попробуем посчитать среднюю зарплату в компании. Запрос на HQL будет выглядеть так:

select avg(salary) from Employee

А вот так на Criteria API:


CriteriaQuery<Double> critQuery = builder.createQuery(Double.class);
critQuery.select(builder.avg( critQuery.from(Employee.class).get("salary")));

Полностью весь Java-код будет выглядеть так:


CriteriaBuilder builder = session.getCriteriaBuilder();
 
CriteriaQuery<Double> critQuery = builder.createQuery(Double.class);
critQuery.select(builder.avg( critQuery.from(Employee.class).get("salary")));
 
Query<Double> query = session.createQuery(critQuery);
Double avgSalary = query.getSingleResult();

CriteriaUpdate

Изменять таблицу так же легко, как и получать из нее данные. Для этого у CriteriaBuilder есть специальный метод – createCriteriaUpdate(), который создает объект CriteriaUpdate<T>, обновляющий сущности в базе.

Давай повысим зарплату сотрудникам, которые получают меньше 10 тысяч. Вот как будет выглядеть этот запрос на HQL:

update Employee set salary = salary+20000 where salary<=10000

А вот так он будет выглядеть на Criteria API:


CriteriaUpdate<Employee> criteriaUpdate = builder.createCriteriaUpdate(Employee.class);
Root<Employee> root = criteriaUpdate.from(Employee.class);
criteriaUpdate.set("salary", "salary+20000");
criteriaUpdate.where(builder.lt(root.get("salary"), 10000));
 
Transaction transaction = session.beginTransaction();
session.createQuery(criteriaUpdate).executeUpdate();
transaction.commit();

CriteriaDelete

А удалять записи еще проще, чем изменять. Для этого есть специальный метод createCriteriaDelete(), который создает объект CriteriaDelete<T>.

Давай сократим всех сотрудников, которые не имеют никакой ценности: их зарплата меньше 10 тысяч. Вот как будет выглядеть этот запрос на HQL:

delete from Employee where salary<=10000

А вот так он будет выглядеть на Criteria API:


CriteriaDelete<Employee> criteriaDelete = builder.createCriteriaDelete(Employee.class);
Root<Employee> root = criteriaDelete.from(Employee.class);
criteriaDelete.where(builder.lt(root.get("salary"), 10000));
 
Transaction transaction = session.beginTransaction();
session.createQuery(criteriaDelete).executeUpdate();
transaction.commit();

Польза от Criteria API

Так в чем же польза от Criteria API? Запросы громоздкие, на HQL точно компактнее будет.

Во-первых, запросы на HQL не такие уж и короткие, если тебе нужно передавать в них параметры. Сравни:

Считаем количество сотрудников с зарплатой меньше 10 тысяч
HQL
String hqlQuery = "from Employee where salary < :sal";
Query<Employee> query = session.createQuery(hqlQuery);
query.setParametr("sal", 10000);
List<Employee> results = query.getResultList();
Criteria API

CriteriaBuilder builder = session.getCriteriaBuilder();
critQuery.select(critQuery.from(Employee.class)).where(builder.lt(root.get("salary"), 10000));
Query<Employee> query = session.createQuery(critQuery);
List<Employee> results = query.getResultList();

Во-вторых, очень часто бывает ситуация, когда запрос нужно сконструировать динамически. Например, у тебя на web-странице есть фильтрация сотрудников, квартир, да чего угодно. И если пользователю какой-то параметр не важен, то он его просто не указывает. Соответственно, на сервер вместо него передается null.

Вот тебе задача: отобрать сотрудников с определенной профессией (occupation), зарплатой (salary) и годом найма (YEAR (join_date)). Но если любое значение параметра равно null, то не использовать его в фильтре.

Тогда запрос на HQL будет выглядеть примерно так:


from Employee
where (occupation = :ocp)
   	and (salary = :sal)
   	and ( YEAR(join_date) = :jny)

Но работать правильно он не будет, так как мы хотим, чтобы если параметр "jny" был null, то запрос выглядел бы так:


from Employee
where (occupation = :ocp)
   	and (salary = :sal)

Можно попробовать переписать запрос с проверкой параметра на null, тогда мы получим что-то типа этого:


from Employee
where (occupation = :ocp or :ocp is null)
   	and (salary = :sal or :sal is null)
   	and ( YEAR(join_date)= :jny or :jny is null)

Видишь, как реальность усложняется? Реальность часто такая :)

А ведь фильтр можно усложнить еще сильнее. Как насчет поиска пользователей, у которых есть задачи со словом "купить"? Или пользователей, у которых есть просроченные задачи?


from Employee
where (occupation = :ocp)
   	and (salary = :sal)
   	and (YEAR(join_date) = :jny)
   	and (tasks.name like '%купить%')
   	and (tasks.deadline < curdate())

Если ты в таком запросе где-то запишешь or is null, то это не отменит join между таблицами.

Так что на практике, когда ты делаешь любой сложный фильтр по полям нескольких таблиц, Criteria API могут тебя просто выручать. Такие дела.

Более подробно можно ознакомиться в официальной документации.

undefined
1
Задача
Модуль 4. Работа с БД, 16 уровень, 1 лекция
Недоступна
Удаление через Criteria API
Используя Criteria API, в методе deleteEmployeeById удали объект Employee из базы данных по его id...