JPA2中的Criteria查询
Posted on: 2013-11-05, Last modified: 2015-07-31, View: 9001

使用criteria 查询

为了更好的理解criteria 查询,考虑拥有Employee实例集合的Dept实体,Employee和Dept的元模型类的代码如下:

//All Necessary Imports  
@StaticMetamodel(Dept.class)  
public class Dept_ {      
    public static volatile SingularAttribute<Dept, Integer> id;     
    public static volatile ListAttribute<Dept, Employee> employeeCollection;      
    public static volatile SingularAttribute<Dept, String> name;  
}  
//All Necessary Imports  
@StaticMetamodel(Employee.class)  
public class Employee_ {       
    public static volatile SingularAttribute<Employee, Integer> id;      
    public static volatile SingularAttribute<Employee, Integer> age;      
    public static volatile SingularAttribute<Employee, String> name;      
    public static volatile SingularAttribute<Employee, Dept> deptId;  
}  

下面的代码片段展示了一个criteria 查询,它用于获取所有年龄大于24岁的员工:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();  
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);  
Root<Employee> employee = criteriaQuery.from(Employee.class);  
Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);  
criteriaQuery.where(condition);  
TypedQuery<Employee> typedQuery = em.createQuery(criteriaQuery);  
List<Employee> result = typedQuery.getResultList(); 

对应的SQL: SELECT * FROM employee WHERE age > 24
 

构建CriteriaQuery 实例

Root<Employee> employee = criteriaQuery.from(Employee.class);

CriteriaQuery 对象必须在实体类型或嵌入式类型上的Criteria 查询上起作用。它通过调用 CriteriaBuilder, createQuery 或CriteriaBuilder.createTupleQuery 获得。CriteriaBuilder就像CriteriaQuery 的工厂一样。CriteriaBuilder工厂类是调用EntityManager.getCriteriaBuilder 或 EntityManagerFactory.getCriteriaBuilder而得。 Employee实体的 CriteriaQuery 对象以下面的方式创建:

CriteriaBuilder criteriaBuilder = emf.getCriteriaBuilder();  
CriteriaQuery<Employee> criteriaQuery = cb.createQuery(Employee.class);  

QueryRoot

AbstractQuery是CriteriaQuery 接口的父类。它提供得到查询根的方法。Criteria查询的查询根定义了实体类型,能为将来导航获得想要的结果,它与SQL查询中的FROM子句类似。
Root实例也是类型化的,且定义了查询的FROM子句中能够出现的类型。查询根实例能通过传入一个实体类型给 AbstractQuery.from方法获得。Criteria查询,可以有多个查询根。Employee实体的查询根对象可以用以下的语法获得: 

Root<Employee> employee = criteriaQuery.from(Employee.class);

过滤Queries

过滤条件应用到SQL语句的FROM子句中。在criteria 查询中,查询条件通过Predicate 或Expression 实例应用到CriteriaQuery 对象上。这些条件使用 CriteriaQuery .where 方法应用到CriteriaQuery 对象上。CriteriaBuilder 也是作为Predicate 实例的工厂,Predicate 对象通过调用CriteriaBuilder 的条件方法( equal,notEqual, gt, ge,lt, le,between,like等)创建。Predicate 实例也可以用Expression 实例的 isNull, isNotNull 和 in方法获得,复合的Predicate 语句可以使用CriteriaBuilder的and, or andnot 方法构建。


下面的代码片段展示了Predicate 实例检查年龄大于24岁的员工实例

Predicate condition = criteriaBuilder.gt(employee.get(Employee_.age), 24);  
criteriaQuery.where(condition);  


通过Employee_元模型类age属性,称之为路径表达式。若age属性与String文本比较,编译器会抛出错误,这在JPQL中是不可能的。


执行查询与获取元模型实例

当EntityManager.createQuery(CriteriaQuery)方法调用时,一个可执行的查询实例会创建,该方法返回指定从 criteria 查询返回的实际类型的TypedQuery 对象。TypedQuery 接口是javax.persistence.Queryinterface.的子类型。在该片段中, TypedQuery 中指定的类型信息是Employee,调用getResultList时,查询就会得到执行

TypedQuery<Employee> typedQuery = em.createQuery(criteriaQuery);
List<Employee> result = typedQuery.getResultList();

元模型实例通过调用 EntityManager.getMetamodel 方法获得,EntityType<Employee>的元模型实例通过调用Metamodel.entity(Employee.class)而获得,其被传入 CriteriaQuery.from 获得查询根。

Metamodel metamodel = em.getMetamodel();
EntityType<Employee> Employee_ = metamodel.entity(Employee.class); 
Root<Employee> empRoot = criteriaQuery.from(Employee_); 

也有可能调用Root.getModel方法获得元模型信息。类型 EntityType<Dept>的实例Dept_和name属性可以调用getSingularAttribute 方法获得,它与String文本进行比较:

CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();  
Root<Dept> dept = criteriaQuery.from(Dept.class);  
EntityType<Dept> Dept_ = dept.getModel();  
Predicate testCondition = criteriaBuilder.equal(dept.get(
            Dept_.getSingularAttribute("name", String.class)), "Ecomm");

对应的 SQL: SELECT * FROM dept WHERE name = 'Ecomm'

Expression

Expression对象用在查询语句的select,where和having子句中,该接口有 isNull, isNotNull 和 in方法,下面的代码片段展示了Expression.in的用法,employye的年龄检查在20或24的。

CriteriaQuery<Employee> criteriaQuery = criteriaBuilder .createQuery(Employee.class);  
Root<Employee> employee = criteriaQuery.from(Employee.class);  
criteriaQuery.where(employee.get(Employee_.age).in(20, 24));  
em.createQuery(criteriaQuery).getResultList(); 

对应的SQL: SELECT * FROM employee WHERE age in (20, 24)

复合谓词

Criteria Query也允许开发者编写复合谓词,通过该查询可以为多条件测试下面的查询检查两个条件。首先,name属性是否以M开头,其次,employee的age属性是否是25。逻辑操作符and执行获得结果记录。

criteriaQuery.where(criteriaBuilder.and(criteriaBuilder.like(employee.get(Employee_.name),
"M%"),criteriaBuilder.equal(employee.get(Employee_.age), 25)));  
em.createQuery(criteriaQuery).getResultList(); 

对应 SQL: SELECT * FROM employee  WHERE name LIKE 'M%' AND age = 25

连接查询

在SQL中,连接跨多张表以获取查询结果,类似的实体连接通过调用 From.join 执行,连接帮助从一个实体导航到另一个实体以获得查询结果。
Root的join方法返回一个 Join<Dept, Employee>类型(也可以是SetJoin,,ListJoin,MapJoin 或者 CollectionJoin类型)。默认情况下,连接操作使用内连接,而外连接可以通过在join方法中指定JoinType参数为LEFT或RIGHT来实现。

CriteriaQuery<Dept> cqDept = criteriaBuilder.createQuery(Dept.class);  
Root<Dept> deptRoot = cqDept.from(Dept.class);  
Join<Dept, Employee> employeeJoin = deptRoot.join(Dept_.employeeCollection);  
cqDept.where(criteriaBuilder.equal(employeeJoin.get(Employee_.deptId).get(Dept_.id), 1));  
TypedQuery<Dept> resultDept = em.createQuery(cqDept);  

对应的SQL: SELECT * FROM employee e, dept d  WHERE e.deptId = d.id and d.id = 1

附录

英文原文地址:http://www.developer.com/java/ent/article.php/3902911/Querying-in-JPA-2-Typesafe-and-Object-Oriented.htm

From: http://tanlan.iteye.com/blog/1118750
Go
Friend Links:
Sonft