该文翻译自网络,原文地址:
使用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 实例
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