《Hibernate快速开始》Query /Criteria

Criteria查询为HQL,JPQL和native SQL 查询提供了一种类型安全的替代方法。

Hibernate 提供了一个遗留下来比较旧的org.hibernate.CriteriaAPI,并且它不被推荐使用。没有功能开发将针对这些API。最终,特定于Hibernate的criteria 功能将被移植到JPA的扩展javax.persistence.criteria.CriteriaQuery。有关org.hibernate.CriteriaAPI的详细信息,请参阅传统Hibernate条件查询

本 章将重点介绍用于声明类型安全的criteria 查询的JPA API。

Criteria 查询是一个以编程形式和类型安全的方式去表达一个查询。

在使用接口和类来表示查询的各种结构部分(如查询本身,select子句或排序等)方面,它们是类型安全的。它们也可以在引用属性方面也是类型安全的正如我们稍后将看到的一样。尽管我们认为JPA API是优越的,但使用较旧的Hibernate org.hibernate.Criteria查询API的用户将会认识到一般的方法,因为它代表了从该API获得的清楚的经验教训。

Criteria查询本质上是一个对象图,其中图形的每个部分表示增加(当我们沿着这个图形向下导航)的更多原子部分的查询。执行条件查询的第一步是构建此图。 在你使用Criteria查询的第一件事是从你熟悉javax.persistence.crite

ria.CriteriaBuilder接口开始。它的作用就是一个工厂的所有单个的Criteria作品进行生产,你可以通过调用javax.per sistence.EntityManagerFactory 或者 javax.persistence.EntityManager. 任意一个的getCriteriaBuilder()方法来获取javax.persistence.criteria.CriteriaBuilder的实例。

下一步是获得一个javax.persistence.criteria.CriteriaQuery。这是javax.persistence.criteria.CriteriaBuilder实现这一目的的三种方法:

[code lang=”java”]
<T> CriteriaQuery<T> createQuery( Class<T> resultClass )
CriteriaQuery<Tuple> createTupleQuery()
CriteriaQuery<Object> createQuery()
[/code]

每一种服务根据预期的查询结果类型提供不同的用途。JPA规范的第6章Criteria API已经包含与Criteria查询的各个部分有关的大量参考资料。所以,我们不是在这里重复所有这些内容,我们来看看API中比预期更广泛的一些用法。

键入条件查询

Criteria 查询的类型(也称为<T>)表示查询结果中预期的类型。这可能是一个实体,一个整数或任何其他对象。

选择一个实体

这可能是最常见的查询形式。应用程序想要选择的实体实例。
示例1.选择根实体

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Person> criteria = builder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class );
criteria.select( root );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );

List<Person> persons = entityManager.createQuery( criteria ).getResultList();
[/code]

该示例使用Person类的引用在createQuery()中传递,查询的结果将返回的是Person对象。
对CriteriaQuery#select在这个例子中的方法的调用是不必要的,因为root将是隐含的选择,因为我们只有一个查询根。在这里只是为了完成一个例子。

        该Person_.name引用的是JPA元模型引用静态形式的示例。我们将在本章中专门使用该结构。有关JPA静态元模型的更多详细信息,请参阅Hibernate JPA元模型生成器的文档。

 选择一个表达式

选择表达式的最简单的形式是从实体中选择一个特定的属性。但是这个表达式也可能会代表聚合,数学运算等。
示例2.选择属性

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<String> criteria = builder.createQuery( String.class );
Root<Person> root = criteria.from( Person.class );
criteria.select( root.get( Person_.nickName ) );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );

List<String> nickNames = entityManager.createQuery( criteria ).getResultList();
[/code]

在这个例子中,查询是键入进来的,因为预期的结果类型是java.lang.String(Person#nickName属性的类型java.lang.String)。因为一个查询可能包含对Person实体的多个引用,所以属性的引用始终需要进行限定。这是通过调用Root#get方法完成的。

选择多个值

实际上有几种不同的方式可以使用criteria 查询来选择多个值。我们将在这里探索两个选项,但另一种推荐方法是使用元组criteria 查询中描述的元组,或者考虑一个包装器查询,请参阅选择包装器以获取详细信息。

示例3.选择数组

 

[code lang=”java”]

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class );
Root<Person> root = criteria.from( Person.class );
Path<Long> idPath = root.get( Person_.id );
Path<String> nickNamePath = root.get( Person_.nickName);
criteria.select( builder.array( idPath, nickNamePath ) );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
List<Object[]> idAndNickNames = entityManager.createQuery( criteria ).getResultList();
[/code]

从技术上讲,这被分类为一个类型化的查询,但是从处理结果可以看出这是一种误导。无论如何,这里的预期结果类型是数组。

然后,该示例使用javax.persistence.criteria.CriteriaBuilder的数组方法,它将单个选择明确地结合到javax.persistence.criteria.CompoundSelection。

例子4.使用multiselect选择一个数组

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class );
Root<Person> root = criteria.from( Person.class );

Path<Long> idPath = root.get( Person_.id );
Path<String> nickNamePath = root.get( Person_.nickName);

criteria.multiselect( idPath, nickNamePath );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );

List<Object[]> idAndNickNames = entityManager.createQuery( criteria ).getResultList();
[/code]

就像我们在选择一个数组中看到的那样,我们有一个类型化的criteria 查询返回一个Object数组。两个查询在功能上都是等价的。第二个示例使用的multiselect()方法的行为稍有不同,这是基于在首次构建criteria 查询时给出的类型,但在这种情况下,它表示选择并返回一个Object []。

选择一个包装

选择多个值的另一种方法是改为选择一个将“包装”多个值的对象。回到那里的示例查询,而不是返回[Person#id,Person#nickName]的数组,而是声明一个包含这些值的类并将其用作返回对象。
例5.选择一个包装

 

[code lang=”java”]
public class PersonWrapper {

private final Long id;

private final String nickName;

public PersonWrapper(Long id, String nickName) {
this.id = id;
this.nickName = nickName;
}

public Long getId() {
return id;
}

public String getNickName() {
return nickName;
}
}

&nbsp;

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<PersonWrapper> criteria = builder.createQuery( PersonWrapper.class );
Root<Person> root = criteria.from( Person.class );

Path<Long> idPath = root.get( Person_.id );
Path<String> nickNamePath = root.get( Person_.nickName);

criteria.select( builder.construct( PersonWrapper.class, idPath, nickNamePath ) );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );

List<PersonWrapper> wrappers = entityManager.createQuery( criteria ).getResultList();
[/code]

首先,我们看到我们将用来包装结果值的包装对象的简单定义。具体来说,请注意构造函数及其参数类型。由于我们将返回PersonWrapper对象,因此我们将其PersonWrapper用作我们的criteria 查询的类型。
这个例子演示了javax.persistence.criteria.CriteriaBuilder用于构建包装表达式的方法构造的使用。对于结果中的每一行,我们都说我们希望PersonWrapper通过匹配的构造函数实例化剩下的参数。这个包装表达式然后作为选择传递。

元组criteria 查询

选择多个值的更好方法是使用包装器(我们在选择包装器中看到的)或使用javax.persistence.Tuple代理。
例子6.选择一个元组

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Tuple> criteria = builder.createQuery( Tuple.class );
Root<Person> root = criteria.from( Person.class );

Path<Long> idPath = root.get( Person_.id );
Path<String> nickNamePath = root.get( Person_.nickName);

criteria.multiselect( idPath, nickNamePath );
criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );

List<Tuple> tuples = entityManager.createQuery( criteria ).getResultList();

for ( Tuple tuple : tuples ) {
Long id = tuple.get( idPath );
String nickName = tuple.get( nickNamePath );
}

//or using indices
for ( Tuple tuple : tuples ) {
Long id = (Long) tuple.get( 0 );
String nickName = (String) tuple.get( 1 );
}
[/code]

这个例子说明了通过访问javax.persistence.Tuple接口的查询结果。这个例子使用了显式createTupleQuery()的javax.persistence.criteria.CriteriaBuilder方法。另一种方法是使用createQuery( Tuple.class )。
我们再次看到使用该multiselect()方法,就像在Selecting an array using multiselect.。这里的区别在于,我们定义javax.persistence.criteria.CriteriaQuery的类型是javax.persistence.Tuple这样的,在这种情况下,复合选择被解释为元组元素。javax.persistence.Tuple契约提供了三种访问下层元素的形式:
类型
在Selecting a tuple 的例子说明这种形式的访问tuple.get( idPath )和tuple.get( nickNamePath )调用。这允许基于javax.persistence.TupleElement用于构建criteria的表达式对基础元组值进行类型访问。
位置
允许根据位置访问基础元组值。简单的Object get(int position)表单与 Selecting an array和 Selecting an array using multiselect.所示的访问非常相似。所述<X> X得到(INT position),Class<X>型形式允许键入的位置访问, 而是基于显式提供类型的元组值的类型必须是可分配给。
别名
允许基于(可选)分配的别名访问基础元组值。示例查询不适用别名。别名将通过别名方法应用于javax.persistence.criteria.Selection。就像通过positional访问,同时存在一个类型(Object get(String alias))和无类型(<X> X get(String alias)), Class<X> 型形式。

FROM子句

一个CriteriaQuery对象定义了一个或多个实体,可嵌入或基本抽象模式类型的查询。查询的根对象是实体,通过导航可以实现其他类型的实体。
– JPA规范,第6.5.2 Query Roots,第262页
FROM子句(roots, joins, paths)的所有单独部分都实现了javax.persistence.criteria.From接口。

根定义所有连接,路径和属性在查询中可用的基础。根始终是一个实体类型。定义根,并通过重载 javax.persistence.criteria.CriteriaQuery方法将其添加到criteria.
示例7.Root 方法

[code lang=”java”]
<X> Root<X> from( Class<X> );
<X> Root<X> from( EntityType<X> );
[/code]

例子8.添加一个root例子

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Person> criteria = builder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class );
[/code]

Criteria 查询可以定义多个根,其作用是在新添加的根和其他根之间创建笛卡尔积。以下是定义实体Person和Partner实体之间的笛卡尔乘积的示例:
例子9.添加多个根例子

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Tuple> criteria = builder.createQuery( Tuple.class );

Root<Person> personRoot = criteria.from( Person.class );
Root<Partner> partnerRoot = criteria.from( Partner.class );
criteria.multiselect( personRoot, partnerRoot );

Predicate personRestriction = builder.and(
builder.equal( personRoot.get( Person_.address ), address ),
builder.isNotEmpty( personRoot.get( Person_.phones ) )
);
Predicate partnerRestriction = builder.and(
builder.like( partnerRoot.get( Partner_.name ), prefix ),
builder.equal( partnerRoot.get( Partner_.version ), 0 )
);
criteria.where( builder.and( personRestriction, partnerRestriction ) );

List<Tuple> tuples = entityManager.createQuery( criteria ).getResultList();
[/code]

Joins

连接允许从其他导航javax.persistence.criteria.From到关联或嵌入的属性。javax.persistence.criteria.From接口是由接口的许多重载连接方法创建的。
例子10.加入例子

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Phone> criteria = builder.createQuery( Phone.class );
Root<Phone> root = criteria.from( Phone.class );

// Phone.person is a @ManyToOne
Join<Phone, Person> personJoin = root.join( Phone_.person );
// Person.addresses is an @ElementCollection
Join<Person, String> addressesJoin = personJoin.join( Person_.addresses );

criteria.where( builder.isNotEmpty( root.get( Phone_.calls ) ) );

List<Phone> phones = entityManager.createQuery( criteria ).getResultList();
[/code]

Fetches

就像在HQL和JPQL中一样,criteria 查询可以指定与相关所有者一起获取关联数据。Fetches 是通过重载javax.persistence.criteria.From接口的许多方法创建的。
例11.加入fetch 示例

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Phone> criteria = builder.createQuery( Phone.class );
Root<Phone> root = criteria.from( Phone.class );

// Phone.person is a @ManyToOne
Fetch<Phone, Person> personFetch = root.fetch( Phone_.person );
// Person.addresses is an @ElementCollection
Fetch<Person, String> addressesJoin = personFetch.fetch( Person_.addresses );

criteria.where( builder.isNotEmpty( root.get( Phone_.calls ) ) );

List<Phone> phones = entityManager.createQuery( criteria ).getResultList();
[/code]

从技术上讲,嵌入的属性总是与其所有者一起获取。然而,为了定义获取Phone#addresses ,我们需要一个javax.persistence.criteria.Fetch因为元素集合默认的是LAZY(注:LAZY为Hibernate的懒加载)。
Path 表达式
Roots, joins and fetches本身也是路径。
使用参数
示例12.参数示例

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Person> criteria = builder.createQuery( Person.class );
Root<Person> root = criteria.from( Person.class );

ParameterExpression<String> nickNameParameter = builder.parameter( String.class );
criteria.where( builder.equal( root.get( Person_.nickName ), nickNameParameter ) );

TypedQuery<Person> query = entityManager.createQuery( criteria );
query.setParameter( nickNameParameter, "JD" );
List<Person> persons = query.getResultList();
[/code]

使用参数方法javax.persistence.criteria.CriteriaBuilder获取参数引用。然后使用参数引用将参数值绑定到javax.persistence.Query。

使用group by

例13.按例分组

[code lang=”java”]
CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<Tuple> criteria = builder.createQuery( Tuple.class );
Root<Person> root = criteria.from( Person.class );

criteria.groupBy(root.get("address"));
criteria.multiselect(root.get("address"), builder.count(root));

List<Tuple> tuples = entityManager.createQuery( criteria ).getResultList();

for ( Tuple tuple : tuples ) {
String name = (String) tuple.get( 0 );
Long count = (Long) tuple.get( 1 );
}
[/code]

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 《Hibernate快速开始》Query /Criteria

  • Trackback 关闭
  • 评论 (0)
  1. 暂无评论

return top