自工做以來,除了之前比較流量的hibernate,就是一直使用ORM 規範 JPA了.而這幾天工做須要,研究了下JPA的標準查詢,名爲:JPA criteria查詢.相比JPQL,其優點是類型安全,更加的面向對象. html
使用標準查詢,開發人員可在編譯的時候就檢查查詢的正確與否.而之前也只是在Hibernate中據說有過.具體不詳,沒用過. java
用的maven插件生成的.具體看這些把.
Hibernate
org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor
http://relation.to/Bloggers/HibernateStaticMetamodelGeneratorAnnotationProcessor
OpenJPA
org.apache.openjpa.persistence.meta.AnnotationProcessor6
http://openjpa.apache.org/builds/latest/docs/manual/manual.html#d0e11094
DataNucleus
org.datanucleus.jpa.JPACriteriaProcessor
http://www.datanucleus.org/products/accessplatform_2_1/jpa/jpql_criteria_metamodel.html 程序員
在JPA中,標準查詢是以元模型的概念爲基礎的.元模型是爲具體持久化單元的受管實體定義的.這些實體能夠是實體類,嵌入類或者映射的父類.提供受管實體元信息的類就是元模型類. 數據庫
描述受管類的狀態和他們之間的關係的靜態元模型類能夠 apache
以下code,一個簡單的實體類package com.demo.entities;下,實體類Employee ,假設該實體有諸如id,name和age的基本屬性,還有與類Address的OneToMany關聯: 數組
@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;下: maven
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規範中爲定義標準元模型類而描述的規則建立。 測試
使用元模型類最大的優點是憑藉其實例化能夠在編譯時訪問實體的持久屬性.該特性使得criteria 查詢更加類型安全. fetch
元模型API與Java中的標準反射API密切相關。主要不一樣在於使用標準反射API編譯器沒法驗證其正確性。例如:下面的代碼會經過編譯測試:
Class myClass = Class.forName("com.demo.Test"); Field myField = myClass.getField("myName");編譯器假定com.demo.Test中定義了屬性myName,一旦該類並無定義屬性myName,編譯器將拋出運行時異常。
元模型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
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);
Root<Employee> employee = criteriaQuery.from(Employee.class);
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");
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()]));
criteriaQuery.where( criteriaBuilder.and( criteriaBuilder.like(employee.get(Employee_.name), "M%"), criteriaBuilder.equal(employee.get(Employee_.age), 25) )); em.createQuery(criteriaQuery).getResultList();
默認狀況下,鏈接操做使用內鏈接,而外鏈接能夠經過在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);
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
CriteriaQuery<String> criteriaQuery = criteriaBuilder.createQuery(String.class); Root<Dept> root = criteriaQuery.from(Dept.class); criteriaQuery.select(root.get(Dept_.name));
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;
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
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%'
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>
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
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
轉的,以爲寫的不錯,整理到一個文章裏了,地址:http://tanlan.iteye.com/blog/1101110