#java #jpa #criteria-api
#java #jpa #критерии-api
Вопрос:
Допустим, у меня есть код, например:
EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> allPets = q.getResultList();
Не могли бы вы объяснить, почему мы используем два createQuery()
метода и в чем разница между ними?
Ответ №1:
CriteriaBuilder#createQuery(Class<T> resultClass)
создает CriteriaQuery<T>
,
и
EntityManager#createQuery(CriteriaQuery<T> criteriaQuery)
создает TypedQuery<T>
.
TL; DR:
Эти два типа не обязательно являются альтернативами друг друга, они скорее служат немного разным целям:
TypedQuery<T>
используется, чтобы избежать приведения к целевому типу и CriteriaQuery<T>
используется для определения запросов программно (вместо того, чтобы писать их вручную). Вы даже можете использовать оба вместе, и я покажу вам, как.
Теперь давайте рассмотрим это немного подробнее.
TypedQuery<T>
JPA представляет запросы либо с Query
, TypedQuery<T>
либо StoredProcedureQuery
экземплярами (все из javax.persistence
пакета, а последние два расширяются Query
).
Простой пример использования Query
будет выглядеть так:
//you write/create query
Query query = em.createQuery("your select query..");
//you get Object instance, so cast to target type is needed
SomeType result = (SomeType) query.getSingleResult();
//you get raw List instance, again - cast to target type is needed
List<SomeType> resultList = (List<SomeType>) query.getResultList();
Обратите внимание, что Query
методы API возвращают либо Object
экземпляры необработанного типа (без специализированного типа) List
, которые вы должны привести к вашему ожидаемому / целевому типу.
TypedQuery<T>
с другой стороны, отличается от Query
тем, что вы предоставляете класс вашего ожидаемого / целевого возвращаемого значения (т. Е. Аргумент универсального типа) при создании запроса, и вы, таким образом, пропускаете часть приведения, вот так:
//you still write query, but here you create typed-query object
TypedQuery<SomeType> typedQuery = em.createQuery("your select query..");
//no cast needed
SomeType result = typedQuery.getSingleResult();
//no cast needed
List<SomeType> result = typedQuery.getResultList();
Важным моментом здесь является то, что во всех этих случаях вам нужно написать запрос HQL или JPQL вручную, чтобы создать соответствующий Query
экземпляр, для которого вы впоследствии вызовете соответствующий метод (ы).
CriteriaQuery<T>
CriteriaQuery<T>
это тоже запрос, так что концептуально это то же самое, что Query
(вы создаете запрос к базе данных и хотите использовать этот запрос для получения данных из / в базу данных), но его основная цель — обеспечить программный и типобезопасный способ определения запросов, не зависящих от платформы.
Спецификация JPA 2.2 гласит, что:
JPA Criteria API используется для определения запросов посредством построения объектов определения запросов на основе объектов, а не для использования основанного на строках подхода Java Persistence query language.
Итак, вместо того, чтобы вручную писать запросы HQL / JPQL, вы создаете запрос программно, вот так:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SomeType> cq = cb.createQuery(SomeType.class);
Root<SomeType> root = cq.from(SomeType.class);
//programmatically adding criterias and/or some filter clauses to your query
cq.select(root);
cq.orderBy(cb.desc(root.get("id")));
//passing cq to entityManager or session object
TypedQuery<SomeType> typedQuery = entityManager.createQuery(cq);
List<SomeType> list = typedQuery.getResultList();
Отвечая на ваш последний вопрос — какие методы попадают в базу данных?:
Во всех вышеуказанных случаях фактический запрос попадает в базу данных при вызове методов Query
(или его дочерних) объектов. В наших примерах это:
query.getSingleResult();
query.getResultList();
typedQuery.getSingleResult();
typedQuery.getResultList();
Запомните два шага:
- Вы определяете / конструируете объект запроса (либо с помощью
HQL
,JPQL
либоCriteriaQuery<T>
); - Вы вызываете методы API для этого объекта, которые фактически запрашивают базу данных.