参考:https://my.oschina.net/zhaoqian/blog/133500css
在JPA中,标准查询是以元模型的概念为基础的.元模型是为具体持久化单元的受管实体定义的.这些实体能够是实体类,嵌入类或者映射的父类.提供受管实体元信息的类就是元模型类.java
描述受管类的状态和他们之间的关系的静态元模型类能够python
以下code,一个简单的实体类package com.demo.entities;下,实体类Employee ,假设该实体有诸如id,name和age的基本属性,还有与类Address的OneToMany关联:nginx
@Entity @Table public class Employee{ private int id; private String name; private int age; @OneToMany private List<Address> addresses; // Other code… }
Employee类(com.demo.entities包中定义)的标准元模型类的名字将是使用 javax.persistence.StaticMetamodel注解的Employee_。元模型类的属性所有是static和public的。Employee的每个属性都会使用在JPA2规范中描述的如下规则在相应的元模型类中映射:程序员
如下是用注解处理器产生的元模型类package com.demo.entities;下:数据库
import javax.annotation.Generated; import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.ListAttribute; import javax.persistence.metamodel.StaticMetamodel; @Generated("org.hibernate.jpamodelgen.JPAMetaModelEntityProcesso") @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 ListAttribute<Employee, Address> addresses; }
就像它的名字代表的,注解处理器处理注解,帮助产生源代码。注解处理在编译时就能激活。元模型类遵循JPA2.0规范中为定义标准元模型类而描述的规则建立。swift
使用元模型类最大的优点是凭借其实例化能够在编译时访问实体的持久属性.该特性使得criteria 查询更加类型安全.数组
元模型API与Java中的标准反射API密切相关。主要不一样在于使用标准反射API编译器没法验证其正确性。例如:下面的代码会经过编译测试:安全
Class myClass = Class.forName("com.demo.Test"); Field myField = myClass.getField("myName");
编译器假定com.demo.Test中定义了属性myName,一旦该类并无定义属性myName,编译器将抛出运行时异常。bash
元模型API会强制编译器检查适当的值是否分配给实体类的持久属性。例如:考虑Employee类的age属性,它是Integer变量。若该属性被赋值为String类型的值,编译器会抛出错误。该实现并不要求支持非标准特性。程序员编写的元模型类一般称为非标准元模型类。当EntityManagerFactory 建立时,持久化提供者会初始化元模型类的属性。
为了更好的理解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
CriteriaBuilder是一个工厂对象,安全查询的开始.用于构建JPA安全查询.能够从EntityManager 或 EntityManagerFactory类中得到CriteriaBuilder.
好比: CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery对象必须在实体类型或嵌入式类型上的Criteria 查询上起做用。
它经过调用 CriteriaBuilder, createQuery 或CriteriaBuilder.createTupleQuery 得到。
CriteriaBuilder就像CriteriaQuery 的工厂同样。
CriteriaBuilder工厂类是调用EntityManager.getCriteriaBuilder 或 EntityManagerFactory.getCriteriaBuilder而得。
Employee实体的 CriteriaQuery 对象如下面的方式建立:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
AbstractQuery是CriteriaQuery 接口的父类。它提供获得查询根的方法。
Criteria查询的查询根定义了实体类型,能为未来导航得到想要的结果,它与SQL查询中的FROM子句相似。
Root实例也是类型化的,且定义了查询的FROM子句中可以出现的类型。
查询根实例能经过传入一个实体类型给 AbstractQuery.from方法得到。
Criteria查询,能够有多个查询根。
Employee实体的查询根对象能够用如下的语法得到 :
Root<Employee> employee = criteriaQuery.from(Employee.class);
过滤条件应用到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中是不可能的。
List<Predicate> predicatesList = new ArrayList<Predicate>();
predicatesList.add(.....Pridicate....)
criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
OR语句
predicatesList.add(criteriaBuilder.or(criteriaBuilder.equal(root.get(RepairOrder_.localRepairStatus), LocalRepairStatus.repairing),criteriaBuilder.equal(root.get(RepairOrder_.localRepairStatus), LocalRepairStatus.diagnos)));
忽略大小写(全大写)
predicatesList.add(criteriaBuilder.like(criteriaBuilder.upper(root.get(RepairShop_.shopName)), StringUtils.upperCase(StringUtils.trim(this.shopName)) + "%"));
经过如上两句添加多个.
注意,你使用EntityManager建立查询时,能够在输入中指定一个CriteriaQuery对象,它返回一个TypedQuery,它是JPA 2.0引入javax.persistence.Query接口的一个扩展,TypedQuery接口知道它返回的类型。
因此使用中,先建立查询获得TypedQuery,而后经过typeQuery获得结果.
当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");
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)
下面也是一个更贴切的例子:
//定义一个Expression Expression<String> exp = root.get(Employee.id); // List<String> strList=new ArrayList<>(); strList.add("20"); strList.add("24"); predicatesList.add(exp.in(strList)); criteriaQuery.where(predicatesList.toArray(new Predicate[predicatesList.size()]));
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中,链接跨多张表以获取查询结果,相似的实体链接经过调用 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);
当涉及到collection属性时,抓取链接对优化数据访问是很是有帮助的。这是经过预抓取关联对象和减小懒加载开销而达到的。
使用 criteria 查询,fetch方法用于指定关联属性
Fetch链接的语义与Join是同样的,由于Fetch操做不返回Path对象,因此它不能未来在查询中引用。
在如下例子中,查询Dept对象时employeeCollection对象被加载,这不会有第二次查询数据库,由于有懒加载。
CriteriaQuery<Dept> d = cb.createQuery(Dept.class); Root<Dept> deptRoot = d.from(Dept.class); deptRoot.fetch("employeeCollection", JoinType.LEFT); d.select(deptRoot); List<Dept> dList = em.createQuery(d).getResultList();
对应SQL: SELECT * FROM dept d, employee e WHERE d.id = e.deptId
Root实例,Join实例或者从另外一个Path对象的get方法得到的对象使用get方法能够获得Path对象,当查询须要导航到实体的属性时,路径表达式是必要的。
Get方法接收的参数是在实体元模型类中指定的属性。
Path对象通常用于Criteria查询对象的select或where方法。例子以下:
CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class); Root<Dept> root = criteriaQuery.from(Dept.class); criteriaQuery.select(root.get(Dept_.name));
在JPQL中,查询参数是在运行时经过使用命名参数语法(冒号加变量,如 :age)传入的。在Criteria查询中,查询参数是在运行时建立ParameterExpression对象并为在查询前调用TypeQuery,setParameter方法设置而传入的。下面代码片断展现了类型为Integer的ParameterExpression age,它被设置为24:
ParameterExpression<Integer> age = criteriaBuilder.parameter(Integer.class);
Predicate condition = criteriaBuilder.gt(testEmp.get(Employee_.age), age); criteriaQuery.where(condition); TypedQuery<Employee> testQuery = em.createQuery(criteriaQuery); List<Employee> result = testQuery.setParameter(age, 24).getResultList(); Corresponding SQL: SELECT * FROM Employee WHERE age = 24;
Criteria查询的结果能调用CriteriaQuery.orderBy方法排序,该方法接收一个Order对象作为参数。经过调用 CriteriaBuilder.asc 或 CriteriaBuilder.Desc,Order对象能被建立。如下代码片断中,Employee实例是基于age的升序排列。
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder .createQuery(Employee.class); Root<Employee> employee = criteriaQuery.from(Employee.class); criteriaQuery.orderBy(criteriaBuilder.asc(employee.get(Employee_.age))); em.createQuery(criteriaQuery).getResultList();
对应 SQL: SELECT * FROM Employee ORDER BY age ASC
CriteriaQuery 实例的groupBy 方法用于基于Expression的结果分组。查询经过设置额外表达式,之后调用having方法。下面代码片断中,查询按照Employee类的name属性分组,且结果以字母N开头:
CriteriaQuery<Tuple> cq = criteriaBuilder.createQuery(Tuple.class);
Root<Employee> employee = cq.from(Employee.class); cq.groupBy(employee.get(Employee_.name)); cq.having(criteriaBuilder.like(employee.get(Employee_.name), "N%")); cq.select(criteriaBuilder.tuple(employee.get(Employee_.name),criteriaBuilder.count(employee))); TypedQuery<Tuple> q = em.createQuery(cq); List<Tuple> result = q.getResultList();
对应 SQL: SELECT name, COUNT(*) FROM employeeGROUP BY name HAVING name like 'N%'
Criteria查询的结果与在Critiria查询建立中指定的同样。结果也能经过把查询根传入 CriteriaQuery.select中显式指定。Criteria查询也给开发者投影各类结果的能力。
使用该方法,查询结果能由非实体类型组成。在下面的代码片断中,为EmployeeDetail类建立了一个Criteria查询对象,而EmployeeDetail类并非实体类型。
CriteriaQuery<EmployeeDetails> criteriaQuery = criteriaBuilder.createQuery(EmployeeDetails.class); Root<Employee> employee = criteriaQuery.from(Employee.class); criteriaQuery.select(criteriaBuilder.construct(EmployeeDetails.class, employee.get(Employee_.name), employee.get(Employee_.age))); em.createQuery(criteriaQuery).getResultList(); Corresponding SQL: SELECT name, age FROM employee<span style="white-space: normal;"> </span>
Criteria查询也能经过设置值给CriteriaBuilder.array方法返回Object[]的结果。下面的代码片断中,数组大小是2(由String和Integer组成)。
CriteriaQuery<Object[]> criteriaQuery = criteriaBuilder.createQuery(Object[].class); Root<Employee> employee = criteriaQuery.from(Employee.class); criteriaQuery.select(criteriaBuilder.array(employee.get(Employee_.name), employee.get(Employee_.age))); em.createQuery(criteriaQuery).getResultList();
对应 SQL: SELECT name, age FROM employee
数据库中的一行数据或单个记录一般称为元组。经过调用CriteriaBuilder.createTupleQuery()方法,查询能够用于元组上。CriteriaQuery.multiselect方法传入参数,它必须在查询中返回。
CriteriaQuery<Tuple> criteriaQuery = criteriaBuilder.createTupleQuery();
Root<Employee> employee = criteriaQuery.from(Employee.class); criteriaQuery.multiselect(employee.get(Employee_.name).alias("name"), employee.get(Employee_.age).alias("age")); em.createQuery(criteriaQuery).getResultList();
对应 SQL: SELECT name, age FROM employee
Criteria查询是一种以更加面向对象的方式查询数据库的方法、在本文中,我讨论了JPA2中类型安全的Criteria查询,以及对于理解Criteria查询很是重要的元模型的概念。也讨论了Criteria查询中的各类API。