Hibernate的criteria查询在JpaSpecificationExecutor中的使用

Hibernate的criteria查询

Hibernate的criteria查询:http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#criteria

为什么要写这个呢?在我的小项目里使用了Spring Data + Jap,使用的时候感觉非常的爽,但是也遇到了一些问题,就比如他的分页排序接口PagingAndSortingRepository,他可以用来实现简单的分页排序但是更复杂的带条件的分页排序就做不到了,为此我需要使用他的JpaSpecificationExecutor接口来做待条件的分页排序,这里就需要用到Hibernate的criteria查询相关的知识了。

16.Criteria

Criteria查询提供了HQL,JPQL和本机SQL查询的类型安全替代方法。

Hibernate提供了旧的遗留org.hibernate.CriteriaAPI,应该被视为已弃用。没有功能开发将针对这些API。最终,特定于Hibernate的Criteria功能将被移植为JPA的扩展javax.persistence.criteria.CriteriaQuery。有关org.hibernate.CriteriaAPI的详细信息,请参阅旧版Hibernate Criteria Queries。本章将重点介绍用于声明类型安全Criteria查询的JPA API。

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

Criteria查询本质上是一个对象图,其中图的每个部分表示查询的更多原子部分(当我们向下导航时)。执行条件查询的第一步是构建此图。该javax.persistence.criteria.CriteriaBuilder接口是与你需要开始使用Criteria的查询能够了解的第一件事。它的作用是工厂的所有Criteria的各个部分。您可以javax.persistence.criteria.CriteriaBuilder通过调用其中getCriteriaBuilder()一个javax.persistence.EntityManagerFactory或的方法来获取实例javax.persistence.EntityManager

下一步是获得一个javax.persistence.criteria.CriteriaQueryjavax.persistence.criteria.CriteriaBuilder为此目的,使用三种方法之一完成此操作:

  • <T> CriteriaQuery<T> createQuery( Class<T> resultClass )
  • CriteriaQuery<Tuple> createTupleQuery()
  • CriteriaQuery<Object> createQuery()

每个都有不同的用途,具体取决于查询结果的预期类型。

16.1. 键入的条件查询

条件查询(aka <T>)的类型表示查询结果中的预期类型。这可能是实体Integer,或任何其他对象。

16.2. 选择一个实体

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

例533.选择Roots实体

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();

该示例使用createQuery()传入Person类引用,因为查询的结果将是Person对象。

CriteriaQuery#select此示例中对方法的调用是不必要的,因为root将是隐含的选择,因为我们只有一个查询Roots。这里只是为了完整的例子。该Person_.name引用是JPA Metamodel引用的静态形式的示例。我们将在本章中专门使用该表格。有关JPA静态元模型的更多详细信息,请参阅Hibernate JPA Metamodel Generator的文档。

16.3. 选择一个表达式

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

示例534.选择属性

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();

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

16.4. 选择多个值

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

例535.选择一个数组

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();

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

然后该示例使用数组方法,javax.persistence.criteria.CriteriaBuilder该方法明确地将各个选择组合成a javax.persistence.criteria.CompoundSelection

例536.使用选择数组 multiselect

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();

正如我们在选择数组中看到的那样我们有一个返回Object数组的类型化条件查询。两个查询在功能上都是等效的。第二个示例使用的multiselect()方法Roots据首次构建条件查询时给出的类型略有不同,但在这种情况下,它表示选择并返回Object []

16.5. 选择包装器

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

例537.选择包装器

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;
    }
}


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();

首先,我们将看到我们将用于包装结果值的包装器对象的简单定义。具体来说,请注意构造函数及其参数类型。由于我们将返回PersonWrapper对象,因此我们将其PersonWrapper用作条件查询的类型。

此示例说明了javax.persistence.criteria.CriteriaBuilder用于构建包装表达式的方法构造的用法。对于结果中的每一行,我们都说我们希望PersonWrapper通过匹配的构造函数对剩余的参数进行实例化。然后将此包装表达式作为select传递。

16.6. 元组Criteria查询

选择多个值的更好方法是使用包装器(我们在选择包装器中看到的)或使用javax.persistence.Tuple合同。

例538.选择一个元组

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 );
}

此示例说明通过javax.persistence.Tuple接口访问查询结果。该示例使用明确createTupleQuery()javax.persistence.criteria.CriteriaBuilder。另一种方法是使用createQuery( Tuple.class )

我们再一次看到了该multiselect()方法的使用,就像选择一个数组一样multiselect。这里的区别在于,javax.persistence.criteria.CriteriaQuery定义的类型是javax.persistence.Tuple复合选择,在这种情况下,被解释为元组元素。

javax.persistence.Tuple契约提供了对底层元素的三种访问形式:

  • 类型

    选择一个元组的例子说明这种形式的访问tuple.get( idPath )tuple.get( nickNamePath )调用。这允许基于javax.persistence.TupleElement用于构建条件的表达式对底层元组值进行类型化访问。

  • 位置

    允许Roots据位置访问基础元组值。简单的Object get(int position)形式非常类似于选择数组使用multiselect选择数组中所示的访问。所述 X得到(INT位置,类型形式允许键入的位置访问,而是基于显式提供类型的元组值的类型必须是可分配给。

  • 别名

    允许基于(可选)分配的别名访问基础元组值。示例查询未应用别名。将通过别名方法应用别名javax.persistence.criteria.Selection。就像positional访问一样,有一个类型(Object get(String别名))和一个无类型( X get(String alias,Class type form)。

16.7. FROM子句

CriteriaQuery对象定义在一个或多个实体,嵌入的,或基本的抽象模式类型的查询。查询的Roots对象是实体,通过导航从中获取其他类型。

- JPA规范,第6.5.2节“查询Roots”,第262页

FROM子句的所有单独部分(Roots,连接,路径)实现javax.persistence.criteria.From接口。

16.8. Roots

Roots定义了查询中所有连接,路径和属性可用的基础。Roots始终是实体类型。Roots据以下方法的重载方法定义Roots并将其添加到条件中javax.persistence.criteria.CriteriaQuery

例539.Roots方法

<X> Root<X> from( Class<X> );

<X> Root<X> from( EntityType<X> );

例540.添加Roots示例

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

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

Criteria查询可以定义多个Roots,其效果是在新添加的Roots和其他Roots之间创建笛卡尔积。以下是在实体PersonPartner实体之间定义笛卡尔积的示例:

例541.添加多个Roots示例

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();

16.9. Joins

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

例542.加入例子

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();

16.10. Fetches

就像在HQL和JPQL中一样,条件查询可以指定与所有者一起提取关联数据。Fetches由javax.persistence.criteria.From接口的众多重载提取方法创建。

例543.加入获取示例

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();

从技术上讲,嵌入式属性总是与其所有者一起提取。但是,为了定义Phone#地址的获取,我们需要一个,javax.persistence.criteria.Fetch因为LAZY默认情况下元素集合。

16.11. 路径表达式

Roots,连接和提取本身也是路径。

16.12. 使用参数

例544.参数示例

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();

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

16.13. 使用group by

例545.逐个例子

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 );
}

可以参考的例子:JPA Criteria Query 的 变态例子

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值