CriteriaBuilder.createQuery和EntityManager.createQuery之间的区别是什么?

huangapple go评论73阅读模式
英文:

What is the difference between CriteriaBuilder.createQuery and EntityManager.createQuery?

问题

让我们假设我有如下代码:

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()方法,以及它们之间的区别是什么吗?

英文:

Let's say I have the code, like:

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery&lt;Pet&gt; cq = cb.createQuery(Pet.class);
Root&lt;Pet&gt; pet = cq.from(Pet.class);
cq.select(pet);
TypedQuery&lt;Pet&gt; q = em.createQuery(cq);
List&lt;Pet&gt; allPets = q.getResultList();

Can some please explain why we are using two createQuery() methods and what is the difference between them?

答案1

得分: 10

CriteriaBuilder#createQuery(Class<T> resultClass) 创建 CriteriaQuery<T>
<br>和<br>
EntityManager#createQuery(CriteriaQuery<T> criteriaQuery) 创建 TypedQuery<T>

TL;DR:

这两种类型并不一定是彼此的替代品,它们更多地是为了不同的目的而存在:

TypedQuery<T> 用于避免将结果强制转换为目标类型,而 CriteriaQuery<T> 则用于以编程方式定义查询(而不是手动编写查询)。你甚至可以将它们结合使用,我会向你展示 - 如何结合使用它们。


现在,让我们更详细地了解一下。

TypedQuery<T>

JPA 使用 QueryTypedQuery<T>StoredProcedureQuery 实例来表示查询(均来自 javax.persistence 包,后两者扩展自 Query)。

使用 Query 的简单示例如下:

//编写/创建查询
Query query = em.createQuery("你的选择查询...");

//获取 Object 实例,因此需要强制转换为目标类型
SomeType result = (SomeType) query.getSingleResult();

//获取原始 List 实例,同样需要将其强制转换为目标类型
List<SomeType> resultList = (List<SomeType>) query.getResultList();

注意,Query API 方法返回的是 Object 或原始类型(没有特定类型的)List 实例,你必须将其转换为预期/目标类型。

另一方面,TypedQuery<T>Query 不同之处在于,在创建查询时,你提供了预期/目标返回值的类(即泛型类型参数),因此你可以跳过强制转换部分,如下所示:

//仍然编写查询,但在这里创建了 TypedQuery 对象
TypedQuery<SomeType> typedQuery = em.createQuery("你的选择查询...");

//不需要转换
SomeType result = typedQuery.getSingleResult();

//不需要转换
List<SomeType> result = typedQuery.getResultList();

重要 的一点是,在所有这些情况下,你必须手动编写 HQL 或 JPQL 查询,以便构造相应的 Query 实例,然后在该实例上调用相应的方法。

CriteriaQuery<T>

CriteriaQuery<T> 也是查询,因此在 概念上 它与 Query 相同(你构建数据库的查询,希望使用该查询从/向数据库获取数据),但其主要目的是提供一种以编程方式定义与平台无关的查询的 类型安全 方式。

JPA 规范 2.2 指出
> JPA Criteria API 用于通过构造基于对象的查询定义对象来定义查询,而不是使用 Java Persistence 查询语言的基于字符串的方法。

因此,不必手动编写 HQL/JPQL 查询,而是以编程方式构建查询,如下所示:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SomeType> cq = cb.createQuery(SomeType.class);
Root<SomeType> root = cq.from(SomeType.class);

//以编程方式向查询添加条件和/或一些过滤子句
cq.select(root);
cq.orderBy(cb.desc(root.get("id")));

//将 cq 传递给 entityManager 或 session 对象
TypedQuery<SomeType> typedQuery = entityManager.createQuery(cq);
List<SomeType> list =  typedQuery.getResultList();

回答你最后的问题 - 哪些方法会访问数据库?

在上述所有情况下,当你在 Query(或其子类)对象上调用方法时,实际查询会访问数据库。在我们的示例中,这些方法是:

query.getSingleResult();
query.getResultList();
typedQuery.getSingleResult();
typedQuery.getResultList();

记住两个步骤:

  1. 你定义/构建查询对象(使用 HQLJPQLCriteriaQuery<T>);
  2. 你在该对象上调用 API 方法,这些方法实际上会查询数据库。
英文:

CriteriaBuilder#createQuery(Class&lt;T&gt; resultClass) creates CriteriaQuery&lt;T&gt;,
<br>and<br>
EntityManager#createQuery(CriteriaQuery&lt;T&gt; criteriaQuery) creates TypedQuery&lt;T&gt;.

TL;DR:

These two types are not necessarily alternatives of each other, they rather serve a little different purposes:

TypedQuery&lt;T&gt; is used to avoid casting to the target type and CriteriaQuery&lt;T&gt; 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&lt;T&gt;

JPA represents queries with either Query, TypedQuery&lt;T&gt; 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(&quot;your select query..&quot;);

//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&lt;SomeType&gt; resultList = (List&lt;SomeType&gt;) 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&lt;T&gt;, 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&lt;SomeType&gt; typedQuery = em.createQuery(&quot;your select query..&quot;);

//no cast needed
SomeType result = typedQuery.getSingleResult();

//no cast needed
List&lt;SomeType&gt; 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&lt;T&gt;

CriteriaQuery&lt;T&gt; 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&lt;SomeType&gt; cq = cb.createQuery(SomeType.class);
Root&lt;SomeType&gt; 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(&quot;id&quot;)));

//passing cq to entityManager or session object
TypedQuery&lt;SomeType&gt; typedQuery = entityManager.createQuery(cq);
List&lt;SomeType&gt; 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:

  1. You define/construct the query object (either with HQL, JPQL or CriteriaQuery&lt;T&gt;);
  2. You invoke the API methods on that object, which actually query the database.

huangapple
  • 本文由 发表于 2020年9月4日 23:10:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63743774.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定