首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2EE

深度解析 JPA 2.0 Criteria API (二):靜態元模型與類型安全查詢構建

     編輯:J2EE

承接上篇,本文深入剖析 JPA 2.0 Criteria API 的核心基礎——靜態元模型與 Metamodel API,展示如何透過編譯期強類型檢查消除 JPQL 字串查詢的語法與語義錯誤,並詳細講解路徑導航、謂詞組合、連接、參數化與結果投影等實戰技巧。

持久化領域的靜態元模型:編譯期的類型守門員

JPQL 以字串構建查詢,編譯器無法驗證屬性名、類型匹配,執行期才拋出異常。JPA 2.0 引入 Canonical Metamodel(規範元模型):為每個實體類生成對應的 Entity_ 類(如 Person_),其中以 public static volatile SingularAttribute<Entity, Type> 形式暴露每個持久屬性。例如:

@StaticMetamodel(Person.class)
public class Person_ {
    public static volatile SingularAttribute<Person, Long> ssn;
    public static volatile SingularAttribute<Person, String> name;
    public static volatile SingularAttribute<Person, Integer> age;
}

查詢時改用 p.get(Person_.age),編譯器即可檢查 age 是否存在、類型是否為 Integer,杜絕 qb.gt(p.get(Person_.age), "xyz") 這類低級錯誤。

Metamodel API:描述持久化類型與屬性的標準介面體系

類比 Reflection API 的 Class、Field,JPA 定義專用介面層級:

  • 類型層級:EntityType、EmbeddableType、MappedSuperclassType,語義區分實體、嵌入類、映射超類。
  • 屬性層級:SingularAttribute(單值)、CollectionAttribute、ListAttribute、SetAttribute、MapAttribute,精確表達集合語義。

這些介面不僅供靜態元模型實作,亦支援執行期動態查詢:透過 EntityManagerFactory.getMetamodel() 獲取 Metamodel 容器,再以 metamodel.entity(Person.class) 取得 EntityType<Person>,進而動態導航屬性。

運行時作用域:持久化單元即模組邊界

Metamodel 實例對應一個持久化單元(persistence.xml 定義的 <class> 列表),嚴格隔離不同單元的實體類型,避免跨單元類衝突,類比 ClassLoader 隔離類空間。

規範與非規範元模型

  • 規範元模型:遵循命名慣例(Entity_)、字段簽名固定,由 JPA 提供者(如 OpenJPA、Hibernate)透過 Annotation Processor(Java 6 javac -processor)自動生成,字段值由運行時注入。
  • 非規範元模型:開發者手寫,不保證跨提供者可移植,需自行初始化字段值。

實戰:函數表達式、複合謂詞、連接與參數化

函數表達式

CriteriaQuery<Double> c = cb.createQuery(Double.class);
Root<Account> a = c.from(Account.class);
c.select(cb.avg(a.get(Account_.balance)));

複合謂詞

Path<Integer> balance = account.get(Account_.balance);
c.where(cb.and(cb.greaterThan(balance, 100), cb.lessThan(balance, 200)));

多值 in() 表達式

Path<String> name = account.get(Account_.owner).get(Person_.name);
c.where(cb.in(name).value("X").value("Y").value("Z"));

類型安全連接

SetJoin<Customer, PurchaseOrder> o = c.join(Customer_.orders);
Predicate p = cb.equal(o.get(PurchaseOrder_.status), Status.DELIVERED).negate();

注意:每次 join() 產生新實例,同一關聯需複用變數,否則生成笛卡兒積。

強類型參數

ParameterExpression<Integer> age = qb.parameter(Integer.class);
Predicate cond = qb.gt(p.get(Person_.age), age);
TypedQuery<Person> q = em.createQuery(c);
List<Person> r = q.setParameter(age, 20).getResultList();

參數表達式帶顯式類型,編譯期阻擋類型不匹配的綁定與比較,優於 JPQL 的命名/位置參數。

結果投影:從實體到 Tuple、陣列、自定義 DTO

  • q.select(account) / 隱式預測:回傳實體列表。
  • cb.construct(CustomerDetails.class, c.get(Customer_.name), c.get(Customer_.age)):構造非持久化 DTO。
  • cb.array(...) / cb.tuple(...):回傳 Object[] 或 Tuple,支援索引、別名、TupleElement 多種存取方式。
  • q.multiselect(...):根據 CriteriaQuery 泛型自動推導構造方式(DTO、Tuple、陣列)。

下一篇將探討動態弱類型查詢、資料庫專屬函數擴展、可編輯查詢與 Query-by-Example 等進階應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved