Let's say I have the code, like:
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();
Can some please explain why we are using two createQuery()
methods and what is the difference between them?
CriteriaBuilder#createQuery(Class<T> resultClass)
creates CriteriaQuery<T>
,
and
EntityManager#createQuery(CriteriaQuery<T> criteriaQuery)
creates TypedQuery<T>
.
These two types are not necessarily alternatives of each other, they rather serve a little different purposes:
TypedQuery<T>
is used to avoid casting to the target type and CriteriaQuery<T>
is used to define queries programmatically (instead of writing them manually). You can even use both in conjunction and I will show you - how.
Now, let's see this in a bit more details.
TypedQuery<T>
JPA represents queries with either Query
, TypedQuery<T>
or StoredProcedureQuery
instances (all from javax.persistence
package, and latter two extend Query
).
A simple example of using Query
would look like this:
//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();
Note, that Query
API methods return either Object
or a raw type (without specialized type) List
instances, which you have to cast to your expected/target type.
TypedQuery<T>
, on the other hand, differs from Query
in a way, that you provide the class of your expected/target return value (i.e. generic type argument) when creating the query, and you thereby skip the casting part, like this:
//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();
Important point here is, that in all these cases, you have to write HQL or JPQL query manually in order to construct corresponding Query
instance, on which, you will, afterwards, invoke corresponding method(s).
CriteriaQuery<T>
CriteriaQuery<T>
is also query, so it is conceptually same thing as Query
(you build the Query against database, and you want to use that query in order to get data from/to database), but its main purpose is to provide a programmatic and type-safe way for defining a platform-independent queries.
JPA Specification 2.2 states, that:
The JPA Criteria API is used to define queries through the construction of object-based query definition objects, rather than use of the string-based approach of the Java Persistence query language.
So, instead of manually writing HQL/JPQL queries, you construct the query programmatically, like this:
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();
Answering your final question - which methods hits the database?:
In all above cases, actual query hits the database when you invoke methods of Query
(or its child) objects. In our examples, these are:
query.getSingleResult();
query.getResultList();
typedQuery.getSingleResult();
typedQuery.getResultList();
Remember two steps:
HQL
, JPQL
or CriteriaQuery<T>
);