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

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() 方法,它们之间有什么区别?

CriteriaBuilder#createQuery(Class<T> resultClass) creates CriteriaQuery<T>

EntityManager#createQuery(CriteriaQuery<T> criteriaQuery) creates TypedQuery<T>.

TL;DR:

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

CriteriaQuery<T> 用于以编程方式定义查询,而不是手动编写,TypedQuery<T> 用于避免在使用 Query 时必须执行的强制转换。您甚至可以将两者结合使用,我会告诉您如何使用。


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

TypedQuery<T>

JPA 用 QueryTypedQuery<T>StoredProcedureQuery 实例表示 query(全部来自 javax.persistence 包,后两个扩展 Query).

使用 Query 的简单示例如下所示:

Query query = em.createQuery("your select query.."); //you write query
SomeType result = (SomeType) query.getSingleResult(); //cast needed
List<SomeType> resultList = (List<SomeType>) query.getResultList(); //cast needed

请注意,Query API 方法 return Object 或原始类型(没有专用类型)List 实例,您必须对其进行转换到您期望的类型。

另一方面,

TypedQuery<T> 在某种程度上不同于 Query,您在创建查询时提供预期 return 值的 class,然后你跳过铸造部分,像这样:

TypedQuery<SomeType> typedQuery = em.createQuery("your select query..");
SomeType result = typedQuery.getSingleResult(); //<-- no cast needed.
List<SomeType> result = typedQuery.getResultList(); //<-- no cast needed.

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

CriteriaQuery<T>

JPA Specification 2.2:

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.

CriteriaQuery 在概念上与 Query 相同( 你构建用于获取数据的查询 from/to 数据库 )并且它是定义 JPQL/HQL 查询的另一种方法。

CriteriaQuery<T>的主要目的是提供一种程序化type-safe方式来定义platform-independent查询。因此,您无需手动编写 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();

记住两步:

  1. 您 define/construct 查询对象(使用 HQLJPQLCriteriaQuery<T>);
  2. 您对该对象调用了 API 方法,这实际上查询了数据库。