【Java中级】8.5 SSH之Hibernate框架(五)——关于Criteria(QBC)过时的补充

1.0 5.2版本之后过时

Criteria类涉及Hibernate中QBC查询语句的使用。
以下内容是官方API文档关于Criteria方面的中文翻译。
详细参考文章:Hibernate5.2之后QBC查询——createCriteria()等方法过时的解决方法
官方Hibernate5.2.18 API说明文档:Hibernate ORM 5.2.18.Final User Guide
以下翻译内容纯靠翻译软件,英文水平不行,见谅。

image.png

16.1条。类型化条件查询

条件查询的类型(也称为)指示查询结果中的预期类型。这可能是实体、整数或任何其他对象。

16.2条。选择实体

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

例532。选择根实体

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Person.class );
    Root root = criteria.from( Person.class );
    criteria.select( root );
    criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
    
    List persons = entityManager.createQuery( criteria ).getResultList();

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

16.3条。选择表达式

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

例533。选择属性

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( String.class );
    Root root = criteria.from( Person.class );
    criteria.select( root.get( Person_.nickName ) );
    criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
    
    List nickNames = entityManager.createQuery( criteria ).getResultList();

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

16.4条。选择多个值

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

例534。选择数组

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

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

然后,该示例使用javax.persistence.CriteriaBuilder的数组方法,该方法将各个选择显式地组合到javax.persistence.criteria.CompoundSelection中。

例535。使用multiselect选择阵列

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

正如我们在选择数组中看到的,我们有一个返回对象数组的类型化条件查询。这两个查询在功能上是等价的。第二个示例使用multiselect()方法,该方法的行为与第一次生成条件查询时给定的类型略有不同,但在本例中,它要求选择并返回一个对象[]。

16.5条。选择包装器

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

例536。选择包装器

    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 criteria = builder.createQuery( PersonWrapper.class );
    Root root = criteria.from( Person.class );
    
    Path idPath = root.get( Person_.id );
    Path nickNamePath = root.get( Person_.nickName);
    
    criteria.select( builder.construct( PersonWrapper.class, idPath, nickNamePath ) );
    criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
    
    List wrappers = entityManager.createQuery( criteria ).getResultList();

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

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

16.6条。元组条件查询

选择多个值的一个更好的方法是使用包装器(我们在选择包装器时刚刚看到)或使用javax.persistence.Tuple契约。

例537。选择元组

16.6条。元组条件查询

选择多个值的一个更好的方法是使用包装器(我们在选择包装器时刚刚看到)或使用javax.persistence.Tuple契约。

例537。选择元组

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Tuple.class );
    Root root = criteria.from( Person.class );
    
    Path idPath = root.get( Person_.id );
    Path nickNamePath = root.get( Person_.nickName);
    
    criteria.multiselect( idPath, nickNamePath );
    criteria.where( builder.equal( root.get( Person_.name ), "John Doe" ) );
    
    List 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接口访问查询结果。该示例使用javax.persistence.CriteriaBuilder的显式createTupleQuery()。另一种方法是使用createQuery(Tuple.class)。

我们再次看到multiselect()方法的使用,就像使用multiselect选择数组一样。这里的区别是javax.persistence.CriteriaQuery的类型被定义为javax.persistence.Tuple,因此在本例中,复合选择被解释为Tuple元素。

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

打字的

选择元组的示例演示了在tuple.get(idPath)和tuple.get(昵称路径)调用中的这种访问形式。这允许基于用于构建条件的javax.persistence.TupleElement表达式对底层元组值进行类型化访问。

位置

允许基于位置访问基础元组值。simple Object get(int position)表单与选择数组和使用multiselect选择数组中所示的访问非常相似。X get(int position,Class类型表单允许类型化位置访问,但基于显式提供的类型,元组值必须是可分配给的类型。

化名

允许基于(可选)分配的别名访问基础元组值。示例查询未应用别名。别名将通过javax.persistence.criteria.Selection上的alias方法应用。与位置访问一样,有一个类型化的(Object get(String alias))和一个非类型化的(X get(String alias,Classtype form)。

16.7条。从句

CriteriaQuery对象定义一个或多个实体、可嵌入或基本抽象模式类型上的查询。查询的根对象是实体,通过导航可以从中访问其他类型。

-JPA规范,第6.5.2节查询根,第262页

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

16.8条。根

根定义查询中所有连接、路径和属性可用的基础。根始终是实体类型。根由javax.persistence.CriteriaQuery上的重载from方法定义并添加到条件中:

例538。根方法

     Root from( Class );
    
     Root from( EntityType );

例539。添加根示例

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Person.class );
    Root root = criteria.from( Person.class );

条件查询可以定义多个根,其效果是在新添加的根和其他根之间创建笛卡尔积。下面是一个在个人和合作伙伴实体之间定义笛卡尔积的示例:

例540。添加多个根示例

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Tuple.class );
    
    Root personRoot = criteria.from( Person.class );
    Root 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 tuples = entityManager.createQuery( criteria ).getResultList();
16.9条。连接

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

例541。连接示例

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Phone.class );
    Root root = criteria.from( Phone.class );
    
    // Phone.person is a @ManyToOne
    Join personJoin = root.join( Phone_.person );
    // Person.addresses is an @ElementCollection
    Join addressesJoin = personJoin.join( Person_.addresses );
    
    criteria.where( builder.isNotEmpty( root.get( Phone_.calls ) ) );
    
    List phones = entityManager.createQuery( criteria ).getResultList();
16.10条。获取

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

例542。连接获取示例

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Phone.class );
    Root root = criteria.from( Phone.class );
    
    // Phone.person is a @ManyToOne
    Fetch personFetch = root.fetch( Phone_.person );
    // Person.addresses is an @ElementCollection
    Fetch addressesJoin = personFetch.fetch( Person_.addresses );
    
    criteria.where( builder.isNotEmpty( root.get( Phone_.calls ) ) );
    
    List phones = entityManager.createQuery( criteria ).getResultList();

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

使用参数

例543。参数示例

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Person.class );
    Root root = criteria.from( Person.class );
    
    ParameterExpression nickNameParameter = builder.parameter( String.class );
    criteria.where( builder.equal( root.get( Person_.nickName ), nickNameParameter ) );
    
    TypedQuery query = entityManager.createQuery( criteria );
    query.setParameter( nickNameParameter, "JD" );
    List persons = query.getResultList();

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

16.13条。使用分组方式

例544。按示例分组

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery criteria = builder.createQuery( Tuple.class );
    Root root = criteria.from( Person.class );
    
    criteria.groupBy(root.get("address"));
    criteria.multiselect(root.get("address"), builder.count(root));
    
    List tuples = entityManager.createQuery( criteria ).getResultList();
    
    for ( Tuple tuple : tuples ) {
        String name = (String) tuple.get( 0 );
        Long count = (Long) tuple.get( 1 );
    }

END

你可能感兴趣的:(【Java中级】8.5 SSH之Hibernate框架(五)——关于Criteria(QBC)过时的补充)