JPA 1.0 引入 JPQL 大幅簡化持久化查詢,但其字串本質帶來三大痛點:編譯期無法檢查語法/語義、執行期才暴露拼寫錯誤、結果列表無泛型需強制轉型。JPA 2.0 (JSR 317) 推出的 Criteria API 徹底改寫查詢範式:以 泛型表達式樹 取代字串,實現編譯期類型安全與動態可編程構建雙重突破。本文為系列首篇,奠定核心概念與基礎用法。
String jpql = "select p from Person where p.age > 20"; // 缺少別名 p
Query query = em.createQuery(jpql); // 編譯通過,執行拋異常
編譯器將 JPQL 視為普通字串,無法感知實體結構、屬性類型、關聯路徑。任何拼寫錯誤、類型不匹配、關聯斷裂,皆需部署至伺服器執行才能發現,嚴重拖慢開發迴路。
| 介面/類 | 角色 | 關鍵泛型參數 |
|---|---|---|
QueryBuilder | 查詢與表達式工廠 | — |
CriteriaQuery<T> | 查詢定義根節點,封裝 FROM/WHERE/SELECT/OrderBy | T = 結果類型 |
Root<T> | 查詢根(FROM 子句),特殊的 Path | T = 實體類型 |
Path<T> | 從 Root 導航到的持久屬性/關聯路徑 | T = 屬性類型 |
Predicate | 布爾表達式(WHERE 子句),繼承 Expression<Boolean> | — |
TypedQuery<T> | 可執行查詢,擴展 Query,getResultList() 回傳 List<T> | T = 結果類型 |
EntityManager em = ...;
QueryBuilder qb = em.getQueryBuilder();
CriteriaQuery<Person> c = qb.createQuery(Person.class); // 1. 指定結果類型
Root<Person> p = c.from(Person.class); // 2. 定義查詢根
Predicate cond = qb.gt(p.get(Person_.age), 20); // 3. 構建型安全謂詞
c.where(cond); // 4. 設定 WHERE
TypedQuery<Person> q = em.createQuery(c); // 5. 獲得型安全可執行查詢
List<Person> result = q.getResultList(); // 直接得到 List<Person>
關鍵亮點:
qb.gt(Expression<? extends Number>, Number) 簽名強制雙方為數值型,編譯期阻擋字串比較。Person_.age 來自靜態元模型(下篇詳述),提供屬性的編譯期句柄。TypedQuery 保留泛型資訊,消除遍歷結果時的強轉與 ClassCastException 風險。CriteriaQuery 內部為表達式節點樹,對應傳統子句:
from() → Root(FROM)where() → Predicate(WHERE)select() / multiselect() → Selection(SELECT)orderBy() → Order(ORDER BY)groupBy() / having() → 分組與過濾所有節點皆為泛型 Expression<T> 子型別,構成強類型 AST,可序列化、可遍歷、可轉換(如生成 SQL、NoSQL 查詢、GraphQL 解析器)。
下一篇將深入 靜態元模型(Canonical Metamodel) 生成機制、Metamodel API 型別體系、SingularAttribute/PluralAttribute 區分、運行時 Metamodel 容器與持久化單元作用域對應,並示範函數表達式、複合謂詞、連接、參數化與多樣化結果投影實戰。