標准的API
JPA 2.0中引入的另一個重要特性是標准的API,利用這個API可以動態地構建基於對象的查詢,本質上,這個標准API等價於面向對象的JPQL,使用它,你可以使用基於對象的方法創建查詢,不再需要JPQL使用的字符串操作。
標准API是基於元模型的,元模型是一個提供了架構級關於持久化單元托管類的元數據的抽象模型, 元模型讓你構建強類型的查詢,它也允許你浏覽持久化單元的邏輯結構。
通常,一個注解處理器使用元模型生成靜態元模型類,這些類提供持久化狀態和持久化單元托管類的關系,但你可以對靜態元模型類編碼。下面是一個實體實例:
- @Entity public class Employee {
- @Id Long Id;
- String firstName;
- String lastName;
- Department dept;
- }
下面是對應的靜態元模型類:
- import Javax.persistence.meta,model.SingularAttribute;
- import Javax.persistence.meta,model.StaticMetamodel;
- @Generated("EclipseLink JPA 2.0 Canonical Model Generation"
- @StaticMetamodel(Employee.class)
- public class Employee_ {
- public static volatile SingularAttribute<Employee, Long> id;
- public static volatile SingularAttribute<Employee, String> firstName;
- public static volatile SingularAttribute<Employee, String> lastName;
- public static volatile SingularAttribute<Employee, Department> dept;
- }
此外,JPA 2.0元模型API允許你動態訪問元模型,因此當你使用標准API時,既可以靜態訪問元模型類,也可以動態訪問元模型類。標准API提供了更好的靈活性,既可以使用基於對象的方法,又可以使用基於字符串的方法導航元模型,這意味著你有四種使用標准API的方法:
1、靜態使用元模型類
2、靜態使用字符串
3、動態使用元模型
4、動態使用字符串
無論你使用哪種方法,通過構造一個CriteriaQuery對象定義一個基於標准API的查詢時,使用一個工廠對象CriteriaBuilder構造CriteriaQuery,可以從EntityManager 或 EntityManagerFactory類中獲得CriteriaBuilder。下面的代碼構造一個CriteriaQuery對象:
- EntityManager em = ... ;
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
注意CriteriaQuery對象是泛型類型,使用CriteriaBuilder 的createQuery方法創建一個CriteriaQuery,並為查詢結果指定類型。在這個例子中,createQuery 方法的Employee.class參數指定查詢結果類型是Employee。CriteriaQuery對象和創建它們的方法是強類型的。
接下來,為CriteriaQuery對象指定一個或多個查詢源,查詢源表示查詢基於的實體。你創建一個查詢源,然後使用AbstractQuery接口的from()方法將其添加到查詢。AbstractQuery接口是眾多接口中的一員,如CriteriaQuery,From和root,它們都定義在標准API中。CriteriaQuery接口繼承AbstractQuery接口的屬性。
from()方法的參數是實體類或EntityType實體的實例,from()方法的結果是一個Root對象,Root接口擴展From接口,它表示某個查詢的from子句中可能出現的對象。
下面的代碼增加一個查詢源到CriteriaQuery對象:
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
- Root<Employee> emp = cq.from(Employee.class);
當你向CriteriaQuery對象添加一個或多個查詢源後,你訪問元模型,然後構造一個查詢表達式,你如何做取決於你是以靜態方式提交查詢還是以動態方式提交查詢,以及是使用元模型還是字符串導航元模型。下面是一個使用元模型類靜態查詢的例子:
- cq.select(emp);
- cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));
- TypedQuery<Employee> query = em.createQuery(cq);
- List<Employee> rows = query.getResultList();
CriteriaQuery接口的select() 和 where()方法指定查詢結果返回的選擇項目。
注意,你使用EntityManager創建查詢時,可以在輸入中指定一個CriteriaQuery對象,它返回一個TypedQuery,它是JPA 2.0引入Javax.persistence.Query接口的一個擴展,TypedQuery接口知道它返回的類型。
在元模型術語中,Employee_是對應於Employee實體類的規范化元模型類,一個規范化元模型類遵循JPA 2.0規范中描述的某些規則。例如,元模型類的名字來源於托管類,一般都是在托管類名字後面追加一個下劃線“_”。一個規范化元模型是一個包含靜態元模型類的元模型,這個靜態元模型對應於實體,映射的超類,以及持久化單元中的嵌入式類。實際上,這個查詢使用了規范化元模型。下面是一個完整的查詢:
- EntityManager em = ... ;
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
- Root<Employee> emp = cq.from(Employee.class);
- cq.select(emp);
- cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));
- TypedQuery<Employee> query = em.createQuery(cq);
- List<Employee> rows = query.getResultList();
下面是使用元模型API查詢的動態版本:
- EntityManager em = ... ;
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
- Root<Employee> emp = cq.from(Employee.class);
- EntityType<Employee> emp_ = emp.getModel();
- cq.select(emp);
- cq.where(cb.equal(emp.get(emp_.getSingularAttribute("lastName", String.class)),"Smith"));
- TypedQuery<Employee> query=em.createQuery(cq);
- List<Employee> rows=query.getResultList();
Root的getModel()方法返回根對應的元模型實體,它也允許運行時訪問在Employee實體中聲明的持久化屬性。
getSingularAttribute()方法是一個元模型API方法,它返回一個持久化的單值屬性或字段,在這個例子中,它返回值為Smith 的lastName屬性。下面是使用字符串的元數據導航查詢的靜態版本:
- EntityManager em = ... ;
- CriteriaBuilder cb = em.getCriteriaBuilder();
- CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
- Root<Employee> emp = cq.from(Employee.class);
- cq.select(emp);
- cq.where(cb.equal(emp.get("lastName"), "Smith"));
- TypedQuery query = em.createQuery(cq);
- List <Employee>rows = query.getResultList();
這個基於字符串的方法要相對容易使用些,但卻失去了元模型具有的類型安全。