标准查询是一种以编程方式表示查询的类型安全方法。它们在使用接口和类来表示查询的各个结构部分(如查询本身、select 子句或 order-by 等)方面是类型安全的。正如我们稍后将看到的,它们在引用属性方面也可以是类型安全的。老版 Hibernate 的 org.hibernate.Criteria
查询 API 的使用者将认可通用方法,尽管我们相信在从该 API 中吸取经验教训后 JPA API 更胜一筹。
标准查询基本上是一个对象图,其中图的每个部分表示进行查询时粒度更细微的部分(在我们深入此图时)。执行标准查询的第一步是构建此图。要开始使用标准查询,需要熟悉的第一件事是 javax.persistence.criteria.CriteriaBuilder
接口。它的作用是为所有独立的标准项充当一个工厂。通过调用 javax.persistence.EntityManagerFactory
的 getCriteriaBuilder
方法获取 javax.persistence.criteria.CriteriaBuilder
实例
CriteriaBuilder builder = entityManagerFactory.getCriteriaBuilder();
下一步是获取 javax.persistence.criteria.CriteriaQuery
。为此,通过 javax.persistence.criteria.CriteriaBuilder
上的 3 种方法之一进行。
CriteriaQuery<T> createQuery(Class<T>)
CriteriaQuery<Tuple> createTupleQuery()
CriteriaQuery<Object> createQuery()
根据预期的查询结果类型,每种方法都有不同的用途。
[JPA 2 规范] 中的第 6 章 标准 API 已包含大量参考材料,涉及标准查询的各个部分。因此,与其在此重复所有内容,不如让我们来看看 API 中一些更广泛预期的用法。
CriteriaQuery<T> createQuery(Class<T>)
标准查询(又称 <T>)的类型表示查询结果中预期的类型。这可以是实体、整数或任何其他对象。
这是 Hibernate Query Language (HQL) 和 Hibernate Criteria 查询中使用最频繁的查询形式。您有一个实体,并且您想基于某个条件选择该实体的一个或多个。
示例 9.1 选择根实体
CriteriaQuery<Person> criteria = builder.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.selec
t( personRoot ); criteria.where
( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Person> people = em.createQuery( criteria ).getResultList(); for ( Person p
erson : people ) { ... }
我们使用表单 createQuery( Person.class ),因为预期返回实际上是 Person 实体,如同我们在开始处理结果时看到的那样。 | |
personCriteria.select( personRoot ) 在这种特定情况下完全不需要,原因是 personRoot 将成为隐含选择,因为我们只有一个根。此处仅出于示例的完整性才这样做 | |
Person_.eyeColor 元模型引用的静态形式的示例。我们在本章中将专门使用该形式。有关详细信息,请参见 第 4.1 节,“静态元模型”。 |
选择值的简单形式是从实体中选择特定属性。但这还可能是聚合、数学运算等。
示例 9.3. 选择表达式
CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.selec
t( builder.max( personRoot.get( Person_.age ) ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); Integer maxAge = em.createQuery( criteria ).getSingleResult();
在此我们看到 |
实际上,有几种不同的方法可以使用条件查询选择多个值。我们将在本文中探讨 2 种方法,但推荐的替代方法是使用元组,如第 9.2 节“元组条件查询”中所述
示例 9.5. 选择数组 (2)
CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class ); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multi
select( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Object[]>
valueArray = em.createQuery( criteria ).getResultList(); for ( Object[] values : valueArray ) { final Long id = (Long) values[0]; final Integer age = (Integer) values[1]; ... }
正如我们在示例 9.4“选择数组”中所看到的,我们有一个“类型化”条件查询,返回 Object 数组。 | |
这实际上与我们在示例 9.4“选择数组”中所看到的功能完全相同。 |
用于第 9.1.3 节“选择多个值”的另一种替代方法是选择一个将“包装”多个值的 Object。返回到此处的示例查询,与其返回 [Person#id, Person#age]数组,不如声明一个包含这些值的类,然后返回该类。
对于 第 9.1.3 节,“选择多个值”,更好的方法是使用包装器(我们在 第 9.1.4 节,“选择包装器” 中刚刚看到)或使用 javax.persistence.Tuple
合约。
示例 9.7. 选择元组
CriteriaQuery<Tuple> criteria = builder.createTupleQuery(); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multi
select( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Tuple> tu
ples = em.createQuery( criteria ).getResultList(); for ( Tuple tuple : valueArray ) { assert tup
le.get( 0 ) == tuple.get( idPath ); assert tup
le.get( 1 ) == tuple.get( agePath ); ... }
这里我们看到新的 | |
我们再次看到 | |
这里我们看到 |
javax.persistence.Tuple
合约提供了 3 种基本形式来访问基础元素
<X> X get(TupleElement<X> tupleElement)
这允许对基础元组元素进行类型化访问。我们在 示例 9.7,“选择元组” 中的 tuple.get( idPath ) 和 tuple.get( agePath ) 调用中看到了这一点。几乎所有内容都是 javax.persistence.TupleElement
。
Object get(int i)
<X> X get(int i, Class<X> type)
非常与我们在 示例 9.4,“选择一个数组” 和 示例 9.5,“选择一个数组 (2)” 中看到的位置访问类似。这里只有第二种形式提供类型,因为用户在访问时明确地提供了类型。我们可以在 示例 9.7,“选择一个元组” 中在 tuple.get( 0 ) 和 tuple.get( 1 ) 调用中看到这点。
Object get(String alias)
<X> X get(String alias, Class<X> type)
同样,这里只有第二种形式提供类型,因为用户在访问时明确地提供了类型。我们没有看到使用它的示例,但其很简单。例如,我们只需对任一路径应用别名,如 idPath.alias( "id" ) 和/或 agePath.alias( "age" ),我们即可通过那些指定的别名访问各个元组元素。
CriteriaQuery 对象定义对一个或多个实体、可嵌入或基本抽象架构类型的查询。查询的根对象是实体,可通过导航访问其他类型。 | ||
--[JPA 2 规范,第 6.5.2 节 查询根,第 262 页] |
<X> Root<X> from(Class<X>)
<X> Root<X> from(EntityType<X>)
条件查询可定义多个根,其作用是在新添加的根与其他根之间创建 笛卡尔积。以下是匹配所有单身男性和所有单身女性的示例
CriteriaQuery query = builder.createQuery();
Root<Person> men = query.from( Person.class );
Root<Person> women = query.from( Person.class );
Predicate menRestriction = builder.and(
builder.equal( men.get( Person_.gender ), Gender.MALE ),
builder.equal( men.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE )
);
Predicate womenRestriction = builder.and(
builder.equal( women.get( Person_.gender ), Gender.FEMALE ),
builder.equal( women.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE )
);
query.where( builder.and( menRestriction, womenRestriction ) );
9.10 示例。集合示例
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class );
Root<Person> personRoot = person.from( Person.class );
Join<Person,Order> orders = personRoot.join( Person_.orders );
Join<Order,LineItem> orderLines = orders.join( Order_.lineItems );
...
从技术上讲,嵌入属性始终会与其所有者一起抓取。但为了定义 Address#country 的抓取,我们需要对其父路径使用 javax.persistence.criteria.Fetch
。
示例 9.12. 带集合示例
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class );
Root<Person> personRoot = person.from( Person.class );
Join<Person,Order> orders = personRoot.fetch( Person_.orders );
Join<Order,LineItem> orderLines = orders.fetch( Order_.lineItems );
...
版权所有 © 2005 Red Hat 公司和众多作者