Criteria类涉及Hibernate中QBC查询语句的使用。
以下内容是官方API文档关于Criteria方面的中文翻译。
详细参考文章:Hibernate5.2之后QBC查询——createCriteria()等方法过时的解决方法
官方Hibernate5.2.18 API说明文档:Hibernate ORM 5.2.18.Final User Guide
以下翻译内容纯靠翻译软件,英文水平不行,见谅。
条件查询的类型(也称为
这可能是最常见的查询形式。应用程序希望选择实体实例。
例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对象。
选择表达式的最简单形式是从实体中选择特定属性。但这个表达式也可能表示一个聚合、一个数学运算等。
例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方法调用实现的。
实际上有几种不同的方法可以使用条件查询选择多个值。我们将在这里探讨两个选项,但推荐的另一种方法是使用元组条件查询中描述的元组,或者考虑包装器查询,有关详细信息,请参阅选择包装器。
例534。选择数组
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery
从技术上讲,这被归类为类型化查询,但从处理结果中可以看出,这有点误导人。无论如何,这里的预期结果类型是一个数组。
然后,该示例使用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()方法,该方法的行为与第一次生成条件查询时给定的类型略有不同,但在本例中,它要求选择并返回一个对象[]。
选择多个值的另一种方法是选择将“包装”多个值的对象。回到这里的示例查询,而不是返回一个[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传递。
选择多个值的一个更好的方法是使用包装器(我们在选择包装器时刚刚看到)或使用javax.persistence.Tuple契约。
例537。选择元组
选择多个值的一个更好的方法是使用包装器(我们在选择包装器时刚刚看到)或使用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选择数组中所示的访问非常相似。
化名
允许基于(可选)分配的别名访问基础元组值。示例查询未应用别名。别名将通过javax.persistence.criteria.Selection上的alias方法应用。与位置访问一样,有一个类型化的(Object get(String alias))和一个非类型化的(
CriteriaQuery对象定义一个或多个实体、可嵌入或基本抽象模式类型上的查询。查询的根对象是实体,通过导航可以从中访问其他类型。
-JPA规范,第6.5.2节查询根,第262页
FROM子句的所有单独部分(根、连接、路径)实现javax.persistence.criteria.FROM接口。
根定义查询中所有连接、路径和属性可用的基础。根始终是实体类型。根由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();
连接允许从其他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();
就像在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。
例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