Criteria API, частина 2

Відкрита

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
String hqlQuery = "from Employee where salary < :sal";
Query<Employee> query = session. createQuery(hqlQuery);
query.setParameter("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();

По-друге, часто буває ситуація, коли запит потрібно сконструювати динамічно. Наприклад, у тебе на вебсторінці є фільтрація співробітників, квартир та чого завгодно. І якщо користувачеві якийсь параметр не важливий, він його просто не вказує. Відповідно, на сервер замість нього передається 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 можуть виручати тебе. Такі справи.

Докладніше з темою можна ознайомитися в офіційній документації.

1
Задача
Модуль 4. Робота з БД,  16 рівень1 лекція
Недоступна
Видалення через Criteria API
task1603
Коментарі (1)
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Олександр
Рівень 92
23 серпня, 09:25
Задача не валідується, бо при завантаженні задачі в класі Employee відсутня анотація @GeneratedValue(strategy = GenerationType.IDENTITY) За умовами задачі клас Employee не можна змінювати, при тому що валідатор очікує ту анотацію))))