在要指定查询结果的CriteriaQuery<T>接口上指定投影项(或简单地说选择项)基本上有两种方法:
CriteriaQuery<T> select(Selection<? extends T> selection);
CriteriaQuery<T> multiselect(Selection<?>... selections);
通常使用的投影术语是查询本身的候选类(在您的示例中为Address)。它可能像您的第一个示例一样是隐含的。
第一个示例中来自Address 实体的查询没有明确指定其投影项,在这种情况下与明确选择候选类本身相同。
隐式推断出候选持久实体本身作为唯一的投影项。
但是,当查询的投影结果不是候选持久实体本身时,可以使用其他几种构造来塑造查询结果。这些构造在CriteriaBuilder 接口中可用。
塑造查询结果的方法:
CompoundSelection<Y> construct(Class<Y> result, Selection<?>... terms);
CompoundSelection<Object[]> array(Selection<?>... terms);
CompoundSelection<Tuple> tuple(Selection<?>... terms);
为了完整起见,我将尝试通过一个简单的示例依次演示它们中的每一个。
construct() 将查询结果转化为类(非持久实体)的实例:
construct() 方法创建给定类参数的实例,并使用来自输入选择项的值调用(非持久实体的)构造函数。例如,非持久实体(甚至不是实体,纯 Java 类)的构造函数的这些参数必须在数量、顺序和类型(数据类型)上与对应于输入选择项的值匹配。
CriteriaQuery<EmployeeDetails> q = cb.createQuery(EmployeeDetails.class);
Root<Employee> root = q.from(Employee.class);
q.select(cb.construct(EmployeeDetails.class, root.get(Employee_.empName), root.get(Employee_.salary));
EmployeeDetails 在这种情况下,是一个普通的 Java 类——一个非持久实体,它有一个构造函数,它接受两个类型为 String(对于 empName)和 BigDecimal(对于 salary)的参数)。
查询返回List<EmployeeDetails>——来自选定Employees的非持久实体——持久实体。
根据返回的行数,查询还可能使用getSingleResult() 方法返回EmployeeDetails。
多个选择项也可以组合成一个复合选择项,代表Object[] 或Tuple,如下所示。
将查询结果整形为Object[]:
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<Employee> root = q.from(Employee.class);
q.select(cb.array(root.get(Employee_.empName), root.get(Employee_.salary));
List<Object[]> list = entityManager.createQuery(q).getResultList();
可以看出,查询返回List<Object[]>。此列表的每个元素都包含一个从 0 开始的数组 Objects - Object[]。
将查询结果整形为Tuple:
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Employee> root = q.from(Employee.class);
Selection<String> empName = root.get(Employee_.empName).alias("empName");
q.select(cb.tuple(empName, root.get(Employee_.salary).alias("salary");
List<Tuple> list = entityManager.createQuery(q).getResultList();
String employeeName = list.get(0).get("empName");//Referring to by using its alias (empName)
String employeeSalary = list.get(0).get(1);//Referring to by using its index (salary)
//Iterate over a list of tuples through a foreach loop using alias.
for (Tuple tuple : list) {
System.out.println(tuple.get("empName") + " : " + tuple.get("salary"));
}
查询返回List<Tuple>。 Tuple 保存的值可通过基于 0 的整数索引、使用别名 TupleElement 或直接通过 TupleElement 访问。
CriteriaBuilder#createTupleQuery() 类似于CriteriaBuilder#createQuery(Tuple.class)。
使用multiselect() 根据结果类型解释术语:
multiselect() 方法会根据CriteriaQuery 的结果类型自动解释输入术语,以自动得出结果的形状——相关CriteriaQuery 的返回类型。例如,可以使用multiselect() 重写此答案中的第一个示例,如下所示。
CriteriaQuery<EmployeeDetails> q = cb.createQuery(EmployeeDetails.class);
Root<Employee> root = q.from(Employee.class);
q.multiselect(root.get(Employee_.empName), root.get(Employee_.salary));
由于结果的类型是EmployeeDetails,multiselect() 方法将其参数投影项解释为EmployeeDetails 的构造函数参数。
请注意,在第一个示例中使用的 cb.construct(EmployeeDetails.class, root.get(Employee_.empName), root.get(Employee_.salary) 这样的构造不必在前面使用 multiselect() 的示例中使用,它会根据查询的返回类型自动解释其参数投影术语,并且在结果类中调用适当的构造函数 - EmployeeDetails。
如果要指定查询返回Tuple(或Tuples 的列表),则具有完全相同参数的multiselect() 方法将创建Tuple 实例,如下所示。
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Employee> root = q.from(Employee.class);
q.multiselect(root.get(Employee_.empName), root.get(Employee_.salary));
不用说,同样的事情也可以重写以返回Object[](或Object[]s 的列表)。
CriteriaQuery<Object[]> q = cb.createQuery(Object[].class);
Root<Employee> root = q.from(Employee.class);
q.multiselect(root.get(Employee_.empName), root.get(Employee_.salary));
在这两种情况下,muliselect() 方法会根据查询的返回类型(分别为 Tuple 和 Object[])自动解释其参数投影项。
还应该注意的是,EcliseLink 有一个bug,在使用multiselect() 以这种方式获取单个Boolean 选择项时仍有待修复,如this question 所示 .
根据不同的 JPA 提供者,multiselect() 方法的行为会因为Object 作为结果类型而变得更加有趣,
- 如果
multiselect() 方法与单个选择项一起使用,则返回类型是所选项本身。
- 但是,如果
multiselect() 方法包含多个输入项/选择项/投影项,则结果类型为 Object[]。
我不太确定不同的提供者,但multiselect() 方法指定的选择项可能不是数组或集合(或元组值复合项)。唯一可以允许作为multiselect() 参数的复合术语是那些由construct() 方法创建的术语,它基本上依次表示单个元素。
除了上述结构之外,CriteriaQuery#select(Selection<? extends T> selection) 的使用在使用标量/组/聚合/单值函数(例如count()、max()、min() 等)和子查询时也很常见。为简洁起见,此答案不包括子查询的使用。
Selection 定义查询选择的内容。一个选择可以是
任何对象表达式、属性表达式、函数、子选择、
构造函数或聚合函数。可以为一个别名定义
Selection 使用 alias() API。
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
// Count the total employees
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root employee = criteriaQuery.from(Employee.class);
criteriaQuery.select(criteriaBuilder.count(employee)); //<------
Query query = entityManager.createQuery(criteriaQuery);
Long result = query.getSingleResult();
// Maximum salary
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
Root employee = criteriaQuery.from(Employee.class);
criteriaQuery.select(criteriaBuilder.max(employee.get("salary")); //<------
Query query = entityManager.createQuery(criteriaQuery);
Long result = query.getSingleResult();
http://en.wikibooks.org/wiki/Java_Persistence/Criteria#Selection