Заявки за критерии на JPA

1. Общ преглед

В този урок ще обсъдим една много полезна JPA функция - критерийни заявки.

Той не само ни дава възможност да пишем заявки, без да правим суров SQL, но също така ни дава обектно ориентиран контрол върху заявките, което е една от основните характеристики на Hibernate. API на критериите ни позволява да изградим обект на критерий за заявка програмно, където можем да приложим различни видове правила за филтриране и логически условия.

От Hibernate 5.2, API на критериите за хибернация е остарял и новата разработка е фокусирана върху API на критериите за JPA. Ще проучим как да използваме Hibernate и JPA за изграждане на критерийни заявки.

2. Зависимости на Maven

За да илюстрираме API, ще използваме референтната реализация на JPA - Hibernate.

За да използвате Hibernate, не забравяйте да добавите най-новата му версия към вашия pom.xml файл:

 org.hibernate hibernate-core 5.3.2.Final 

Най-новата версия на Hibernate можете да намерите тук.

3. Прост пример с използване на критерии

Нека започнем, като разгледаме как да извличаме данни, използвайки заявки за критерии. Ще разгледаме как да вземем всички екземпляри на определен клас от базата данни.

Имаме клас Item, който представлява кортежа „ITEM“ в базата данни:

public class Item implements Serializable { private Integer itemId; private String itemName; private String itemDescription; private Integer itemPrice; // standard setters and getters }

Нека разгледаме проста заявка за критерии, която ще извлече всички редове на „ ITEM“ от базата данни:

Session session = HibernateUtil.getHibernateSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery cr = cb.createQuery(Item.class); Root root = cr.from(Item.class); cr.select(root); Query query = session.createQuery(cr); List results = query.getResultList();

Горната заявка е проста демонстрация как да получите всички елементи. Нека да видим какво е направено, стъпка по стъпка:

  1. Създайте екземпляр на Session от обекта SessionFactory
  2. Създайте екземпляр на C riteriaBuilder, като извикате метода getCriteriaBuilder ()
  3. Създайте екземпляр на CriteriaQuery, като извикате метода CriteriaBuilder createQuery ()
  4. Създайте екземпляр на Query, като извикате метода createQuery () на сесията
  5. Извикайте метода getResultList () на обекта на заявката, който ни дава резултатите

Сега, след като разгледахме основите, нека преминем към някои от характеристиките на заявката за критерии:

3.1. Използване на изрази

В CriteriaBuilder може да бъде използвано за ограничаване на резултатите от заявката на базата на определени условия. Чрез използване на метода CriteriaQuery where () и предоставяне на изрази, създадени от CriteriaBuilder.

Ето някои примери за често използвани изрази :

За да получите артикули с цена над 1000:

cr.select(root).where(cb.gt(root.get("itemPrice"), 1000));

След това получаване на елементи с itemPrice по-малко от 1000:

cr.select(root).where(cb.lt(root.get("itemPrice"), 1000));

Елементите с itemNames съдържат стол :

cr.select(root).where(cb.like(root.get("itemName"), "%chair%"));

Записи с артикул Цена между 100 и 200:

cr.select(root).where(cb.between(root.get("itemPrice"), 100, 200));

За да проверите дали даденото свойство е null:

cr.select(root).where(cb.isNull(root.get("itemDescription")));

За да проверите дали даденото свойство не е null:

cr.select(root).where(cb.isNotNull(root.get("itemDescription")));

Можете също да използвате методите isEmpty () и isNotEmpty (), за да проверите дали Списъкът в даден клас е празен или не.

Сега неизбежно идва въпросът дали можем да комбинираме две или повече от горните сравнения или не. Отговорът е, разбира се, да - API на критериите ни позволява лесно да веригираме изрази :

Predicate[] predicates = new Predicate[2]; predicates[0] = cb.isNull(root.get("itemDescription")); predicates[1] = cb.like(root.get("itemName"), "chair%"); cr.select(root).where(predicates);

За да добавите два израза с логически операции:

Predicate greaterThanPrice = cb.gt(root.get("itemPrice"), 1000); Predicate chairItems = cb.like(root.get("itemName"), "Chair%");

Елементи с дефинираните по-горе условия, обединени с логическо ИЛИ :

cr.select(root).where(cb.or(greaterThanPrice, chairItems));

За да получите елементи, съвпадащи с гореописаните условия, обединени с логическо И :

cr.select(root).where(cb.and(greaterThanPrice, chairItems));

3.2. Сортиране

Сега, когато знаем основното използване на критериите , нека да разгледаме функциите за сортиране на критериите .

В следващия пример ние подреждаме списъка във възходящ ред на името и след това в низходящ ред на цената:

cr.orderBy( cb.asc(root.get("itemName")), cb.desc(root.get("itemPrice")));

В следващия раздел ще разгледаме как да правим обобщени функции.

3.3. Проекции, агрегати и функции за групиране

Досега сме обхванали повечето основни теми. Сега нека да разгледаме различните агрегирани функции:

Вземете брой редове:

CriteriaQuery cr = cb.createQuery(Long.class); Root root = cr.from(Item.class); cr.select(cb.count(root)); Query query = session.createQuery(cr); List itemProjected = query.getResultList();

Следва пример за агрегирани функции:

Обобщена функция за средно :

CriteriaQuery cr = cb.createQuery(Double.class); Root root = cr.from(Item.class); cr.select(cb.avg(root.get("itemPrice"))); Query query = session.createQuery(cr); List avgItemPriceList = query.getResultList();

Налични други полезни агрегирани методи са sum () , max () , min () , count () и др.

3.4. CriteriaUpdate

Започвайки от JPA 2.1, има поддръжка за извършване на актуализации на базата данни с помощта на API за критерии .

CriteriaUpdate има метод set () , който може да се използва за предоставяне на нови стойности за записи в базата данни:

CriteriaUpdate criteriaUpdate = cb.createCriteriaUpdate(Item.class); Root root = criteriaUpdate.from(Item.class); criteriaUpdate.set("itemPrice", newPrice); criteriaUpdate.where(cb.equal(root.get("itemPrice"), oldPrice)); Transaction transaction = session.beginTransaction(); session.createQuery(criteriaUpdate).executeUpdate(); transaction.commit();

В горния фрагмент създаваме екземпляр на CriteriaUpdate от CriteriaBuilder и използваме метода set () , за да предоставим нови стойности за itemPrice. За да актуализираме множество свойства, просто трябва да извикаме метода set () няколко пъти.

3.5. CriteriaDelete

CriteriaDelete, както подсказва името му, позволява операция за изтриване, използвайки Criteria API. Всичко, от което се нуждаем, е да създадем екземпляр на CriteriaDelete и да използваме метода where () , за да приложим ограничения:

CriteriaDelete criteriaDelete = cb.createCriteriaDelete(Item.class); Root root = criteriaDelete.from(Item.class); criteriaDelete.where(cb.greaterThan(root.get("itemPrice"), targetPrice)); Transaction transaction = session.beginTransaction(); session.createQuery(criteriaDelete).executeUpdate(); transaction.commit();

4. Предимство пред HQL

In the previous sections we've covered how to use Criteria Queries.

Clearly, the main and most hard-hitting advantage of Criteria queries over HQL is the nice, clean, Object Oriented API.

We can simply write more flexible, dynamic queries compared to plain HQL. The logic can be refactored with the IDE and has all the type-safety benefits of the Java language itself.

There are of course some disadvantages as well, especially around more complex joins.

So, generally speaking, we'll have to use the best tool for the job – that can be the Criteria API in most cases, but there are definitely cases where we'll have to go lower level.

5. Conclusion

In this article, we focused on the basics of Criteria Queries in Hibernate and JPA, and also on some of the advanced features of the API.

Обсъденият тук код е достъпен в хранилището на Github.