1. Знайомство з 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 = "від 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.

2. Приклади роботи з 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.

3. Просунута робота з 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") )

Ти просто викликаєш в об'єкта CriteriaQuery метод orderBy() і передаєш у нього потрібні параметри.

>

Ось як цей же запит буде виглядати на HQL. Порівняй:

select * from  Employee
where (…) and (…)
Order by 'salary' asc, 'joinDate' desc

Тільки потрібно запам'ятати 3 речі:

  • Ключові оператори типу SELECT, FROM, WHERE викликаються в об'єкта CriteriaQuery.
  • Допоміжні оператори типу AND, OR, DESC викликаються в об'єкта CriteriaBuilder.
  • Імена полів беруться через get() у об'єкта Root.