CriteriaBuilder.createQuery 和 EntityManager.createQuery 有什么区别?

Pra*_*mar 3 java jpa criteria-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();
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下为什么我们使用两种createQuery()方法以及它们之间有什么区别吗?

Gio*_*uri 10

CriteriaBuilder#createQuery(Class<T> resultClass)创造CriteriaQuery<T>

EntityManager#createQuery(CriteriaQuery<T> criteriaQuery)创造TypedQuery<T>

长话短说:

这两种类型不一定是彼此的替代品,它们的用途略有不同:

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();
Run Code Online (Sandbox Code Playgroud)

请注意,QueryAPI 方法返回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();
Run Code Online (Sandbox Code Playgroud)

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

CriteriaQuery<T>

CriteriaQuery<T>也是查询,因此它在概念上与Query您针对数据库构建查询,并且您想要使用该查询以便从数据库获取数据/向数据库获取数据)相同,但其主要目的是提供编程类型安全的方法定义独立于平台的查询的方法。

JPA 规范 2.2指出

JPA Criteria API 用于通过构建基于对象的查询定义对象来定义查询,而不是使用 Java 持久性查询语言的基于字符串的方法。

因此,您无需手动编写 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();
Run Code Online (Sandbox Code Playgroud)

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

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

query.getSingleResult();
query.getResultList();
typedQuery.getSingleResult();
typedQuery.getResultList();
Run Code Online (Sandbox Code Playgroud)

记住两步:

  1. 您定义/构造查询对象(使用HQLJPQLCriteriaQuery<T>);
  2. 您调用该对象上的 API 方法,该方法实际上查询数据库。