1. Групування та функції агрегування
Як робити прості запити на 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();
2. 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();
3. 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();
4. Користь від Criteria API
Так у чому ж користь від Criteria API? Запити громіздкі, на HQL точно буде компактніше.
По-перше, запити на HQL не такі короткі, якщо тобі потрібно передавати до них параметри. Порівняй:
Рахуємо кількість співробітників із зарплатою меншою за 10 тисяч |
---|
HQL |
|
Criteria API |
|
По-друге, часто буває ситуація, коли запит потрібно сконструювати динамічно. Наприклад, у тебе на вебсторінці є фільтрація співробітників, квартир та чого завгодно. І якщо користувачеві якийсь параметр не важливий, він його просто не вказує. Відповідно, на сервер замість нього передається 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 можуть виручати тебе. Такі справи.
Докладніше з темою можна ознайомитися в офіційній документації.