本系列最終篇聚焦 Criteria API 的動態可編程特性:弱類型 API 支援純運行時決定實體、function() 呼叫資料庫專屬函數、查詢物件可編輯實現「結果中搜尋」、以及 Query-by-Example(QBE)模板查詢。這些能力讓 Criteria API 超越靜態 JPQL,成為構建靈活查詢引擎的核心基建。
當實體類型僅在運行時已知(如通用報表引擎),可用字串屬性名替代靜態元模型:
Class<Account> cls = Class.forName("domain.Account");
EntityType<Account> entity = em.getMetamodel().entity(cls);
CriteriaQuery<Account> c = cb.createQuery(cls);
Root<Account> account = c.from(entity);
Path<Integer> balance = account.<Integer>get("balance"); // 需顯式泛型轉型
c.where(cb.and(cb.greaterThan(balance, 100), cb.lessThan(balance, 200)));
編譯器會警告 unchecked cast,可用參數化方法調用 account.<Integer>get("balance") 抑制警告。此模式失去編譯期類型安全,換取運行時結構自由度。
function() 突破 JPQL 語法侷限JPQL 僅支援標準函數,Criteria API 透過 cb.function(String name, Class<T> type, Expression<?>... args) 直接映射資料庫原生函數:
Expression<String> currentUser = cb.function("CURRENT_USER", String.class, (Expression<?>[])null);
CriteriaQuery<Tuple> q = cb.createTupleQuery();
Root<Customer> c = q.from(Customer.class);
q.multiselect(currentUser, c.get(Customer_.balanceOwed));
等效 JPQL 無法表達,動態 API 不受預定義語法束縛,支援任意資料庫擴展函數(Oracle DECODE、PostgreSQL jsonb_extract 等)。
CriteriaQuery 的 setter(select()、where()、orderBy())可替換子句,getter(getOrderList() 等)回傳快照列表(非活動視圖)。最佳實踐:複製列表、修改、再 set 回去:
List<Order> orders = c.getOrderList();
List<Order> newOrders = new ArrayList<>(orders);
newOrders.add(cb.desc(p.get(Person_.zipcode)));
c.orderBy(newOrders); // 追加排序條件
List<Person> result2 = em.createQuery(c).getResultList();
此機制天然支援分步篩選、動態排序、分頁參數調整等交互式查詢場景。
QBE 由 IBM Research 1970 年代提出,用非 null/非預設值屬性自動組合聯合謂詞。JPA 2.0 標準未納入,但 OpenJPA 透過擴展介面支援:
CriteriaQuery<Employee> q = cb.createQuery(Employee.class);
Employee example = new Employee();
example.setSalary(10000);
example.setRating(1);
q.where(cb.qbe(q.from(Employee.class), example));
// 自動生成:salary = 10000 AND rating = 1
可進一步指定排除屬性、字串比較模式(精確/模糊/前綴),極適合「高級搜尋表單」後端自動構建。
JPA 2.0 Criteria API 不只是 JPQL 的類型安全替代品,更是「以程式碼構建查詢」的標準化 AST(抽象語法樹)API。掌握其靜態與動態雙重面向,能讓持久層從「寫 SQL 字串」進化為「構建查詢物件」,顯著提升可維護性與擴展性。