真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JPA2.0動(dòng)態(tài)查詢機(jī)制CriteriaAPI怎么用

小編給大家分享一下JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)主營(yíng)政和網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,手機(jī)APP定制開發(fā),政和h5小程序開發(fā)搭建,政和網(wǎng)站營(yíng)銷推廣歡迎政和等地區(qū)企業(yè)咨詢

自從 JPA 于 2006 年首次被引入之后,它就得到了 Java 開發(fā)社區(qū)的廣泛支持。該規(guī)范的下一個(gè)主要更新 —— 2.0 版本 (JSR 317) —— 將在 2009 年年底完成。JPA 2.0 引入的關(guān)鍵特性之一就是 Criteria API,它為 Java 語言帶來了一種獨(dú)特的能力:開發(fā)一種 Java 編譯器可以在運(yùn)行時(shí)驗(yàn)證其正確性的查詢。Criteria API 還提供一個(gè)能夠在運(yùn)行時(shí)動(dòng)態(tài)地構(gòu)建查詢的機(jī)制。

本文將介紹 Criteria API 和與之密切相關(guān)的 元模型(metamodel)概念。您將學(xué)習(xí)如何使用 Criteria API 開發(fā) Java 編譯器能夠檢查其正確性的查詢,從而減少運(yùn)行時(shí)錯(cuò)誤,這種查詢優(yōu)于傳統(tǒng)的基于字符串的 Java Persistence Query Language (JPQL) 查詢。借助使用數(shù)據(jù)庫函數(shù)或匹配模板實(shí)例的樣例查詢,我將演示編程式查詢構(gòu)造機(jī)制的強(qiáng)大威力,并將其與使用預(yù)定義語法的 JPQL 查詢進(jìn)行對(duì)比。本文假設(shè)您具備基礎(chǔ)的 Java 語言編程知識(shí),并了解常見的 JPA 使用,比如 EntityManagerFactoryEntityManager

JPQL 查詢有什么缺陷?

JPA 1.0 引進(jìn)了 JPQL,這是一種強(qiáng)大的查詢語言,它在很大程度上導(dǎo)致了 JPA 的流行。不過,基于字符串并使用有限語法的 JPQL 存在一些限制。要理解 JPQL 的主要限制之一,請(qǐng)查看清單 1 中的簡(jiǎn)單代碼片段,它通過執(zhí)行 JPQL 查詢選擇年齡大于 20 歲的 Person 列表:


清單 1. 一個(gè)簡(jiǎn)單(并且錯(cuò)誤)的 JPQL 查詢

EntityManager em = ...;
String jpql = "select p from Person where p.age > 20";Query query = em.createQuery(jpql);
List result = query.getResultList();

這個(gè)基礎(chǔ)的例子顯示了 JPA 1.0 中的查詢執(zhí)行模型的以下關(guān)鍵方面:

  • JPQL 查詢被指定為一個(gè) String(第 2 行)。

  • EntityManager 是構(gòu)造一個(gè)包含給定 JPQL 字符串的可執(zhí)行 查詢實(shí)例的工廠(第 3 行)。

  • 查詢執(zhí)行的結(jié)果包含無類型的 java.util.List 的元素。

但是這個(gè)簡(jiǎn)單的例子有一個(gè)驗(yàn)證的錯(cuò)誤。該代碼能夠順利通過編譯,但將在運(yùn)行時(shí)失敗,因?yàn)樵?JPQL 查詢字符串的語法有誤。清單 1 的第 2 行的正確語法為:

String jpql = "select p from Person p where p.age > 20";


不幸的是,Java 編譯器不能發(fā)現(xiàn)此類錯(cuò)誤。在運(yùn)行時(shí),該錯(cuò)誤將出現(xiàn)在第 3 或第 4 行(具體行數(shù)取決于 JPA 提供者是否在查詢構(gòu)造或執(zhí)行期間根據(jù) JPQL 語法解析 JPQL 字符串)。

類型安全查詢?nèi)绾翁峁椭?/strong>

Criteria API 的最大優(yōu)勢(shì)之一就是禁止構(gòu)造語法錯(cuò)誤的查詢。清單 2 使用 CriteriaQuery 接口重新編寫了 清單 1 中的 JPQL 查詢:


清單 2. 編寫 CriteriaQuery 的基本步驟

EntityManager em = ...QueryBuilder qb = em.getQueryBuilder();CriteriaQuery< Person> c = qb.createQuery(Person.class);Root< Person> p = c.from(Person.class);Predicate condition = qb.gt(p.get(Person_.age), 20);
c.where(condition);TypedQuery< Person> q = em.createQuery(c); 
List< Person> result = q.getResultList();

清單 2 展示了 Criteria API 的核心構(gòu)造及其基本使用:

  • 第 1 行通過幾種可用方法之一獲取一個(gè) EntityManager 實(shí)例。

  • 在第 2 行,EntityManager 創(chuàng)建 QueryBuilder 的一個(gè)實(shí)例。QueryBuilderCriteriaQuery 的工廠。

  • 在第 3 行,QueryBuilder 工廠構(gòu)造一個(gè) CriteriaQuery 實(shí)例。CriteriaQuery 被賦予泛型類型。泛型參數(shù)聲明 CriteriaQuery 在執(zhí)行時(shí)返回的結(jié)果的類型。在構(gòu)造 CriteriaQuery 時(shí),您可以提供各種結(jié)果類型參數(shù) —— 從持久化實(shí)體(比如 Person.class)到形式更加靈活的 Object[]。

  • 第 4 行在 CriteriaQuery 實(shí)例上設(shè)置了查詢表達(dá)式。查詢表達(dá)式是在一個(gè)樹中組裝的核心單元或節(jié)點(diǎn),用于指定 CriteriaQuery。圖 1 顯示了在 Criteria API 中定義的查詢表達(dá)式的層次結(jié)構(gòu):
    圖 1. 查詢表達(dá)式中的接口層次結(jié)構(gòu)
    JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用

    首先,將 CriteriaQuery 設(shè)置為 Person.class 查詢。結(jié)果返回 Root< Person> 實(shí)例 p。Root 是一個(gè)查詢表達(dá)式,它表示持久化實(shí)體的范圍。Root< T> 實(shí)際上表示:“對(duì)所有類型為 T 的實(shí)例計(jì)算這個(gè)查詢。” 這類似于 JPQL 或 SQL 查詢的 FROM 子句。另外還需要注意,Root< Person> 是泛型的(實(shí)際上每個(gè)表達(dá)式都是泛型的)。類型參數(shù)就是表達(dá)式要計(jì)算的值的類型。因此 Root< Person> 表示一個(gè)對(duì) Person.class 進(jìn)行計(jì)算的表達(dá)式。第 5 行構(gòu)造一個(gè) Predicate。Predicate 是計(jì)算結(jié)果為 true 或 false 的常見查詢表達(dá)式形式。謂詞由 QueryBuilder 構(gòu)造,QueryBuilder 不僅是 CriteriaQuery 的工廠,同時(shí)也是查詢表達(dá)式的工廠。QueryBuilder 包含構(gòu)造傳統(tǒng) JPQL 語法支持的所有查詢表達(dá)式的 API 方法,并且還包含額外的方法。在 清單 2 中,QueryBuilder 用于構(gòu)造一個(gè)表達(dá)式,它將計(jì)算第一個(gè)表達(dá)式參數(shù)的值是否大于第二個(gè)參數(shù)的值。方法簽名為:

  • Predicate gt(Expression< ? extends Number> x, Number y);

    這個(gè)方法簽名是展示使用強(qiáng)類型語言(比如 Java)定義能夠檢查正確性并阻止錯(cuò)誤的 API 的好例子。該方法簽名指定,僅能將值為 Number 的表達(dá)式與另一個(gè)值也為 Number 的表達(dá)式進(jìn)行比較(例如,不能與值為 String 的表達(dá)式進(jìn)行比較):

    Predicate condition = qb.gt(p.get(Person_.age), 20);

    第 5 行有更多學(xué)問。注意 qb.gt() 方法的第一個(gè)輸入?yún)?shù):p.get(Person_.age),其中 p 是先前獲得的 Root< Person> 表達(dá)式。p.get(Person_.age) 是一個(gè)路徑表達(dá)式。路徑表達(dá)式是通過一個(gè)或多個(gè)持久化屬性從根表達(dá)式進(jìn)行導(dǎo)航得到的結(jié)果。因此,表達(dá)式 p.get(Person_.age) 表示使用 Personage 屬性從根表達(dá)式 p 導(dǎo)航。您可能不明白 Person_.age 是什么。您可以將其暫時(shí)看作一種表示 Personage 屬性的方法。我將在談?wù)?JPA 2.0 引入的新 Metamodel API 時(shí)詳細(xì)解釋 Person_.age。

    如前所述,每個(gè)查詢表達(dá)式都是泛型的,以表示表達(dá)式計(jì)算的值的類型。如果 Person.class 中的 age 屬性被聲明為類型 Integer(或 int),則表達(dá)式 p.get(Person_.age) 的計(jì)算結(jié)果的類型為 Integer。由于 API 中的類型安全繼承,編輯器本身將對(duì)無意義的比較拋出錯(cuò)誤,比如:

    Predicate condition = qb.gt(p.get(Person_.age, "xyz"));

  • 第 6 行在 CriteriaQuery 上將謂詞設(shè)置為其 WHERE 子句。

  • 在第 7 行中,EntityManager 創(chuàng)建一個(gè)可執(zhí)行查詢,其輸入為 CriteriaQuery。這類似于構(gòu)造一個(gè)輸入為 JPQL 字符串的可執(zhí)行查詢。但是由于輸入 CriteriaQuery 包含更多的類型信息,所以得到的結(jié)果是 TypedQuery,它是熟悉的 javax.persistence.Query 的一個(gè)擴(kuò)展。如其名所示,TypedQuery 知道執(zhí)行它返回的結(jié)果的類型。它是這樣定義的:

    public interface TypedQuery< T> extends Query {
                 List< T> getResultList();
    }

    與對(duì)應(yīng)的無類型超接口相反:

    public interface Query {
    List getResultList();
    }

    很明顯,TypedQuery 結(jié)果具有相同的 Person.class 類型,該類型在構(gòu)造輸入 CriteriaQuery 時(shí)由 QueryBuilder 指定(第 3 行)。

  • 在第 8 行中,當(dāng)最終執(zhí)行查詢以獲得結(jié)果列表時(shí),攜帶的類型信息展示了其優(yōu)勢(shì)。得到的結(jié)果是帶有類型的 Person 列表,從而使開發(fā)人員在遍歷生成的元素時(shí)省去麻煩的強(qiáng)制類型轉(zhuǎn)換(同時(shí)減少了 ClassCastException 運(yùn)行時(shí)錯(cuò)誤)。

現(xiàn)在歸納 清單 2 中的簡(jiǎn)單例子的基本方面:

  • CriteriaQuery 是一個(gè)查詢表達(dá)式節(jié)點(diǎn)樹。在傳統(tǒng)的基于字符串的查詢語言中,這些表達(dá)式節(jié)點(diǎn)用于指定查詢子句,比如 FROM、WHEREORDER BY。圖 2 顯示了與查詢相關(guān)的子句:
    圖 2. CriteriaQuery 封裝了傳統(tǒng)查詢的子句
    JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用

  • 查詢表達(dá)式被賦予泛型。一些典型的表達(dá)式是:

    • Root< T>,相當(dāng)于一個(gè) FROM 子句。

    • Predicate,其計(jì)算為布爾值 true 或 false(事實(shí)上,它被聲明為 interface Predicate extends Expression< Boolean>)。

    • Path< T>,表示從 Root< ?> 表達(dá)式導(dǎo)航到的持久化屬性。Root< T> 是一個(gè)沒有父類的特殊 Path< T>。

  • QueryBuilderCriteriaQuery 和各種查詢表達(dá)式的工廠。

  • CriteriaQuery 被傳遞給一個(gè)可執(zhí)行查詢并保留類型信息,這樣可以直接訪問選擇列表的元素,而不需要任何運(yùn)行時(shí)強(qiáng)制類型轉(zhuǎn)換。

持久化域的元模型

討論 清單 2 時(shí)指出了一個(gè)不常見的構(gòu)造:Person_.age,它表示 Person 的持久化屬性 age。清單 2 使用 Person_.age 形成一個(gè)路徑表達(dá)式,它通過 p.get(Person_.age)Root< Person> 表達(dá)式 p 導(dǎo)航而來。Person_.agePerson_ 類中的公共靜態(tài)字段,Person_靜態(tài)、已實(shí)例化的規(guī)范元模型類,對(duì)應(yīng)于原來的 Person 實(shí)體類。

元模型類描述持久化類的元數(shù)據(jù)。如果一個(gè)類安裝 JPA 2.0 規(guī)范精確地描述持久化實(shí)體的元數(shù)據(jù),那么該元模型類就是規(guī)范的。規(guī)范的元模型類是靜態(tài)的,因此它的所有成員變量都被聲明為靜態(tài)的(也是 public 的)。Person_.age 是靜態(tài)成員變量之一。您可以在開發(fā)時(shí)在源代碼中生成一個(gè)具體的 Person_.java實(shí)例化 一個(gè)規(guī)范類。實(shí)例化之后,它就可以在編譯期間以強(qiáng)類型的方式引用 Person 的持久化屬性。

這個(gè) Person_metamodel 類是引用 Person 的元信息的一種代替方法。這種方法類似于經(jīng)常使用(有人可能認(rèn)為是濫用)的 Java Reflection API,但概念上有很大的不同。您可以使用反射獲得關(guān)于 java.lang.Class 的實(shí)例的元信息,但是不能以編譯器能夠檢查的方式引用關(guān)于 Person.class 的元信息。例如,使用反射時(shí),您將這樣引用 Person.class中的 age 字段:

Field field = Person.class.getField("age");

不過,這種方法也存在很大的限制,類似于 清單 1 中基于字符串的 JPQL 查詢存在的限制。編譯器能夠順利編譯該代碼,但不能確定它是否可以正常工作。如果該代碼包含任何錯(cuò)誤輸入,它在運(yùn)行時(shí)肯定會(huì)失敗。反射不能實(shí)現(xiàn) JPA 2.0 的類型安全查詢 API 要實(shí)現(xiàn)的功能。

類型安全查詢 API 必須讓您的代碼能夠引用 Person 類中的持久化屬性 age,同時(shí)讓編譯器能夠在編譯期間檢查錯(cuò)誤。JPA 2.0 提供的解決辦法通過靜態(tài)地公開相同的持久化屬性實(shí)例化名為 Person_ 的元模型類(對(duì)應(yīng)于 Person)。

關(guān)于元信息的討論通常都是令人昏昏欲睡的。所以我將為熟悉的 Plain Old Java Object (POJO) 實(shí)體類展示一個(gè)具體的元模型類例子(domain.Person),如清單 3 所示:


清單 3. 一個(gè)簡(jiǎn)單的持久化實(shí)體

package domain;
@Entitypublic class Person {
  @Id  private long ssn;  private string name;  private int age;

  // public gettter/setter methods  public String getName() {...}
}

這是 POJO 的典型定義,并且包含注釋(比如 @Entity@Id),從而讓 JPA 提供者能夠?qū)⑦@個(gè)類的實(shí)例作為持久化實(shí)體管理。

清單 4 顯示了 domain.Person 的對(duì)應(yīng)靜態(tài)規(guī)范元模型類:


清單 4. 一個(gè)簡(jiǎn)單實(shí)體的規(guī)范元模型

package domain;import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(domain.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;
}

元模型類將原來的 domain.Person 實(shí)體的每個(gè)持久化屬性聲明為類型為 SingularAttribute< Person,?> 的靜態(tài)公共字段。通過利用這個(gè) Person_元模型類,可以在編譯期間引用 domain.Person 的持久化屬性 age — 不是通過 Reflection API,而是直接引用靜態(tài)的 Person_.age 字段。然后,編譯器可以根據(jù) age 屬性聲明的類型實(shí)施類型檢查。我已經(jīng)列舉了一個(gè)關(guān)于此類限制的例子:QueryBuilder.gt(p.get(Person_.age), "xyz") 將導(dǎo)致編譯器錯(cuò)誤,因?yàn)榫幾g器通過 QueryBuilder.gt(..) 的簽名和 Person_.age 的類型可以確定 Personage 屬性是一個(gè)數(shù)字字段,不能與 String 進(jìn)行比較。

其他一些需要注意的要點(diǎn)包括:

  • 元模型 Person_.age 字段被聲明為類型 javax.persistence.metamodel.SingularAttribute。SingularAttribute 是 JPA Metamodel API 中定義的接口之一,我將在下一小節(jié)描述它。SingularAttribute< Person, Integer> 的泛型參數(shù)表示該類聲明原來的持久化屬性和持久化屬性本身的類型。

  • 元模型類被注釋為 @StaticMetamodel(domain.Person.class) 以將其標(biāo)記為一個(gè)與原來的持久化 domain.Person 實(shí)體對(duì)應(yīng)的元模型類。

Metamodel API

我將一個(gè)元模型類定義為一個(gè)持久化實(shí)體類的描述。就像 Reflection API 需要其他接口(比如 java.lang.reflect.Fieldjava.lang.reflect.Method)來描述 java.lang.Class 的組成一樣,JPA Metamodel API 也需要其他接口(比如 SingularAttributePluralAttribute)來描述元模型類的類型及其屬性。

圖 3 顯示了在 Metamodel API 中定義用于描述類型的接口:


圖 3. Metamodel API 中的持久化類型的接口的層次結(jié)構(gòu)
JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用

圖 4 顯示了在 Metamodel API 中定義用于描述屬性的接口:


圖 4. Metamodel API 中的持久化屬性的接口的層次結(jié)構(gòu)
JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用

JPA 的 Metamodel API 接口比 Java Reflection API 更加專業(yè)化。需要更細(xì)微的差別來表達(dá)關(guān)于持久化的豐富元信息。例如,Java Reflection API 將所有 Java 類型表示為 java.lang.Class。即沒有通過獨(dú)立的定義對(duì)概念進(jìn)行區(qū)分,比如類、抽象類和接口。當(dāng)然,您可以詢問 Class 它是一個(gè)接口還是一個(gè)抽象類,但這與通過兩個(gè)獨(dú)立的定義表示接口和抽象類的差別不同。

Java Reflection API 在 Java 語言誕生時(shí)就被引入(對(duì)于一種常見的多用途編程語言而言,這曾經(jīng)是一個(gè)非常前沿的概念),但是經(jīng)過多年的發(fā)展才認(rèn)識(shí)到強(qiáng)類型系統(tǒng)的用途和強(qiáng)大之處。JPA Metamodel API 將強(qiáng)類型引入到持久化實(shí)體中。例如,持久化實(shí)體在語義上區(qū)分為 MappedSuperClass、EntityEmbeddable。在 JPA 2.0 之前,這種語義區(qū)分是通過持久化類定義中的對(duì)應(yīng)類級(jí)別注釋來表示的。JPA Metamodel 在 javax.persistence.metamodel 包中描述了 3 個(gè)獨(dú)立的接口( MappedSuperclassType、EntityTypeEmbeddableType),以更加鮮明的對(duì)比它們的語義特征。類似地,可以通過接口(比如 SingularAttribute、CollectionAttributeMapAttribute)在類型定義級(jí)別上區(qū)分持久化屬性。

除了方便描述之外,這些專門化的元模型接口還有實(shí)用優(yōu)勢(shì),能夠幫助構(gòu)建類型安全的查詢從而減少運(yùn)行時(shí)錯(cuò)誤。您在前面的例子中看到了一部分優(yōu)勢(shì),隨著我通過 CriteriaQuery 描述關(guān)于連接的例子,您將看到更多優(yōu)勢(shì)。

運(yùn)行時(shí)作用域

一般而言,可以將 Java Reflection API 的傳統(tǒng)接口與專門用于描述持久化元數(shù)據(jù)的 javax.persistence.metamodel 的接口進(jìn)行比較。要進(jìn)一步進(jìn)行類比,則需要對(duì)元模型接口使用等效的運(yùn)行時(shí)作用域概念。java.lang.Class 實(shí)例的作用域由 java.lang.ClassLoader 在運(yùn)行時(shí)劃分。一組相互引用的 Java 類實(shí)例必須在 ClassLoader 作用域下定義。作用域的邊界是嚴(yán)格封閉 的,如果在 ClassLoader L 作用域下定義的類 A 試圖引用不在 ClassLoader L 作用域之內(nèi)的類 B,結(jié)果將收到可怕的 ClassNotFoundExceptionNoClassDef FoundError(對(duì)于處理包含多個(gè) ClassLoader 的環(huán)境的開發(fā)人員或部署人員而言,問題就復(fù)雜了)。

現(xiàn)在將一組嚴(yán)格的可相互引用的類稱為運(yùn)行時(shí)作用域,而在 JPA 1.0 中稱為持久化單元。持久化單元作用域的持久化實(shí)體在 META-INF/persistence.xml 文件的 < class> 子句中枚舉。在 JPA 2.0 中,通過 javax.persistence.metamodel.Metamodel 接口讓開發(fā)人員可以在運(yùn)行時(shí)使用作用域。Metamodel 接口是特定持久化單元知道的所有持久化實(shí)體的容器,如圖 5 所示:


圖 5. 元模型接口是持久化單元中的類型的容器
JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用

這個(gè)接口允許通過元模型元素的對(duì)應(yīng)持久化實(shí)體類訪問元模型元素。例如,要獲得對(duì) Person 持久化實(shí)體的持久化元數(shù)據(jù)的引用,可以編寫:

EntityManagerFactory emf = ...;
Metamodel metamodel = emf.getMetamodel();
EntityType< Person> pClass = metamodel.entity(Person.class);

這是一個(gè)用類的名稱通過 ClassLoader 獲得 Class 的類比:

ClassLoader classloader =  Thread.currentThread().getContextClassLoader();
Class< ?> clazz = classloader.loadClass("domain.Person");

可以在運(yùn)行時(shí)瀏覽 EntityType< Person> 獲得在 Person 實(shí)體中聲明的持久化屬性。如果應(yīng)用程序在 pClass(比如 pClass.getSingularAttribute("age", Integer.class))上調(diào)用一個(gè)方法,它將返回一個(gè) SingularAttribute< Person, Integer> 實(shí)例,該實(shí)例與實(shí)例化規(guī)范元模型類的靜態(tài) Person_.age 成員相同。最重要的是,對(duì)于應(yīng)用程序可以通過 Metamodel API 在運(yùn)行時(shí)引用的屬性,是通過實(shí)例化靜態(tài)規(guī)范元模型 Person_類向 Java 編譯器提供的。

除了將持久化實(shí)體分解為對(duì)應(yīng)的元模型元素之外,Metamodel API 還允許訪問所有已知的元模型類 (Metamodel.getManagedTypes()),或者通過類的持久化信息訪問元模型類,例如 embeddable(Address.class),它將返回一個(gè) EmbeddableType< Address> 實(shí)例(ManagedType< > 的子接口)。

在 JPA 中,關(guān)于 POJO 的元信息使用帶有源代碼注釋(或 XML 描述符)的持久化元信息進(jìn)一步進(jìn)行區(qū)分 —— 比如類是否是嵌入的,或者哪個(gè)字段用作主鍵。持久化元信息分為兩大類:持久化(比如 @Entity)和映射(比如 @Table)。在 JPA 2.0 中,元模型僅為持久化注釋(不是映射注釋)捕捉元數(shù)據(jù)。因此,使用當(dāng)前版本的 Metamodel API 可以知道哪些字段是持久化的,但不能找到它們映射到的數(shù)據(jù)庫列。

規(guī)范和非規(guī)范

盡管 JPA 2.0 規(guī)范規(guī)定了規(guī)范的靜態(tài)元模型類的精確樣式(包括元模型類的完整限定名及其靜態(tài)字段的名稱),應(yīng)用程序也能夠編寫這些元模型類。如果應(yīng)用程序開發(fā)人員編寫元模型類,這些類就稱為非規(guī)范元模型?,F(xiàn)在,關(guān)于非規(guī)范元模型的規(guī)范還不是很詳細(xì),因此對(duì)非規(guī)范元模型的支持不能在 JPA 提供者之間移植。您可能已經(jīng)注意到,公共靜態(tài)字段僅在規(guī)范元模型中聲明,而沒有初始化。聲明之后就可以在開發(fā) CriteriaQuery 時(shí)引用這些字段。但是,必須在運(yùn)行時(shí)給它們賦值才有意義。盡管為規(guī)范元模型的字段賦值是 JPA 提供者的責(zé)任,但非規(guī)范元模型則不存在這一要求。使用非規(guī)范元模型的應(yīng)用程序必須依賴于特定供應(yīng)商機(jī)制,或開發(fā)自己的機(jī)制來在運(yùn)行時(shí)初始化元模型屬性的字段值。

JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用 

注釋處理和元模型生成

如果您有許多持久化實(shí)體,您將傾向于不親自編寫元模型類,這是很自然的事情。持久化提供者應(yīng)該 為您生成這些元模型類。在規(guī)范中沒有強(qiáng)制規(guī)定這種工具或生成機(jī)制,但是 JPA 之間已經(jīng)私下達(dá)成共識(shí),他們將使用在 Java 6 編譯器中集成的 Annotation Processor 工具生成規(guī)范元模型。Apache OpenJPA 提供一個(gè)工具來生成這些元模型類,其生成方式有兩種,一是在您為持久化實(shí)體編譯源代碼時(shí)隱式地生成,二是通過顯式地調(diào)用腳本生成。在 Java 6 以前,有一個(gè)被廣泛使用的稱為 apt 的 Annotation Processor 工具,但在 Java 6 中,編譯器和 Annotation Processor 的合并被定義為標(biāo)準(zhǔn)的一部分。

要像持久化提供者一樣在 OpenJPA 中生成這些元模型類,僅需在編譯器的類路徑中使用 OpenJPA 類庫編譯 POJO 實(shí)體:

$ javac domain/Person.java

將生成規(guī)范元模型 Person_ 類,它將位于 Person.java 所在的目錄,并且作為該編譯的一部分。

編寫類型安全的查詢

到目前為止,我已經(jīng)構(gòu)建了 CriteriaQuery 的組件和相關(guān)的元模型類?,F(xiàn)在,我將展示如何使用 Criteria API 開發(fā)一些查詢。

函數(shù)表達(dá)式

函數(shù)表達(dá)式將一個(gè)函數(shù)應(yīng)用到一個(gè)或多個(gè)輸入?yún)?shù)以創(chuàng)建新的表達(dá)式。函數(shù)表達(dá)式的類型取決于函數(shù)的性質(zhì)及其參數(shù)的類型。輸入?yún)?shù)本身可以是表達(dá)式或文本值。編譯器的類型檢查規(guī)則與 API 簽名結(jié)合確定什么是合法輸入。

考慮一個(gè)對(duì)輸入表達(dá)式應(yīng)用平均值的單參數(shù)表達(dá)式。CriteriaQuery 選擇所有 Account 的平均余額,如清單 5 所示:


清單 5. CriteriaQuery 中的函數(shù)表達(dá)式

CriteriaQuery< Double> c = cb.createQuery(Double.class);Root< Account> a = c.from(Account.class);

c.select(cb.avg(a.get(Account_.balance)));

等效的 JPQL 查詢?yōu)椋?/p>

String jpql = "select avg(a.balance) from Account a";

在 清單 5 中,QueryBuilder 工廠(由變量 cb 表示)創(chuàng)建一個(gè) avg() 表達(dá)式,并將其用于查詢的 select() 子句。

JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用 

該查詢表達(dá)式是一個(gè)構(gòu)建塊,可以通過組裝它為查詢定義最后的選擇謂詞。清單 6 中的例子顯示了通過導(dǎo)航到 Account 的余額創(chuàng)建的 Path 表達(dá)式,然后 Path 表達(dá)式被用作兩個(gè)二進(jìn)制函數(shù)表達(dá)式( greaterThan()lessThan())的輸入表達(dá)式,這兩個(gè)表達(dá)式的結(jié)果都是一個(gè)布爾表達(dá)式或一個(gè)謂詞。然后,通過 and() 操作合并謂詞以形成最終的選擇謂詞,查詢的 where() 子句將計(jì)算該謂詞:


清單 6. CriteriaQuery 中的 where() 謂詞

CriteriaQuery< Account> c = cb.createQuery(Account.class);Root< Account> account = c.from(Account.class);Path< Integer> balance = account.get(Account_.balance);
c.where(cb.and
       (cb.greaterThan(balance, 100), 
        cb.lessThan(balance), 200)));

等效的 JPQL 查詢?yōu)椋?/p>

"select a from Account a where a.balance>100 and a.balance< 200";

符合謂詞

某些表達(dá)式(比如 in())可以應(yīng)用到多個(gè)表達(dá)式。清單 7 給出了一個(gè)例子:


清單 7. CriteriaQuery 中的多值表達(dá)式

CriteriaQuery< Account> c = cb.createQuery(Account.class);Root< Account> account = c.from(Account.class);Path< Person> owner = account.get(Account_.owner);Path< String> name = owner.get(Person_.name);
c.where(cb.in(name).value("X").value("Y").value("Z"));

這個(gè)例子通過兩個(gè)步驟從 Account 進(jìn)行導(dǎo)航,創(chuàng)建一個(gè)表示帳戶所有者的名稱的路徑。然后,它創(chuàng)建一個(gè)使用路徑表達(dá)式作為輸入的 in() 表達(dá)式。in() 表達(dá)式計(jì)算它的輸入表達(dá)式是否等于它的參數(shù)之一。這些參數(shù)通過 value() 方法在 In< T> 表達(dá)式上指定,In< T> 的簽名如下所示:

In< T> value(T value);

注意如何使用 Java 泛型指定僅對(duì)值的類型為 T 的成員計(jì)算 In< T> 表達(dá)式。因?yàn)楸硎?Account 所有者的名稱的路徑表達(dá)式的類型為 String,所以與值為 String 類型的參數(shù)進(jìn)行比較才有效,String 值參數(shù)可以是字面量或計(jì)算結(jié)果為 String 的另一個(gè)表達(dá)式。

將 清單 7 中的查詢與等效(正確)的 JPQL 進(jìn)行比較:

"select a from Account a where a.owner.name in ('X','Y','Z')";

在 JPQL 中的輕微疏忽不僅不會(huì)被編輯器檢查到,它還可能導(dǎo)致意外結(jié)果。例如:

"select a from Account a where a.owner.name in (X, Y, Z)";

連接關(guān)系

盡管 清單 6 和 清單 7 中的例子將表達(dá)式用作構(gòu)建塊,查詢都是基于一個(gè)實(shí)體及其屬性之上的。但是查詢通常涉及到多個(gè)實(shí)體,這就要求您將多個(gè)實(shí)體連接 起來。CriteriaQuery 通過類型連接表達(dá)式 連接兩個(gè)實(shí)體。類型連接表達(dá)式有兩個(gè)類型參數(shù):連接源的類型和連接目標(biāo)屬性的可綁定類型。例如,如果您想查詢有一個(gè)或多個(gè) PurchaseOrder 沒有發(fā)出的 Customer,則需要通過一個(gè)表達(dá)式將 Customer 連接到 PurchaseOrder,其中 Customer 有一個(gè)名為 orders 類型為 java.util.Set< PurchaseOrder> 的持久化屬性,如清單 8 所示:


清單 8. 連接多值屬性

CriteriaQuery< Customer> q = cb.createQuery(Customer.class);Root< Customer> c = q.from(Customer.class);SetJoin< Customer, PurchaseOrder> o = c.join(Customer_.orders);

連接表達(dá)式從根表達(dá)式 c 創(chuàng)建,持久化屬性 Customer.orders 由連接源(Customer)和 Customer.orders 屬性的可綁定類型進(jìn)行參數(shù)化,可綁定類型是 PurchaseOrder不是 已聲明的類型 java.util.Set< PurchaseOrder>。此外還要注意,因?yàn)槌跏紝傩缘念愋蜑?java.util.Set,所以生成的連接表達(dá)式為 SetJoin,它是專門針對(duì)類型被聲明為 java.util.Set 的屬性的 Join。類似地,對(duì)于其他受支持的多值持久化屬性類型,該 API 定義 CollectionJoin、ListJoinMapJoin。(圖 1 顯示了各種連接表達(dá)式)。在 清單 8 的第 3 行不需要進(jìn)行顯式的轉(zhuǎn)換,因?yàn)?CriteriaQuery 和 Metamodel API 通過覆蓋 join() 的方法能夠識(shí)別和區(qū)分聲明為 java.util.CollectionList 或者 SetMap 的屬性類型。

在查詢中使用連接在連接實(shí)體上形成一個(gè)謂詞。因此,如果您想要選擇有一個(gè)或多個(gè)未發(fā)送 PurchaseOrderCustomer,可以通過狀態(tài)屬性從連接表達(dá)式 o 進(jìn)行導(dǎo)航,然后將其與 DELIVERED 狀態(tài)比較,并否定謂詞:

Predicate p = cb.equal(o.get(PurchaseOrder_.status), Status.DELIVERED)
        .negate();

創(chuàng)建連接表達(dá)式需要注意的一個(gè)地方是,每次連接一個(gè)表達(dá)式時(shí),都會(huì)返回一個(gè)新的表達(dá)式,如清單 9 所示:


清單 9. 每次連接創(chuàng)建一個(gè)唯一的實(shí)例

SetJoin< Customer, PurchaseOrder> o1 = c.join(Customer_.orders);SetJoin< Customer, PurchaseOrder> o2 = c.join(Customer_.orders);assert o1 == o2;

清單 9 中對(duì)兩個(gè)來自相同表達(dá)式 c 的連接表達(dá)式的等同性斷言將失敗。因此,如果查詢的謂詞涉及到未發(fā)送并且值大于 $200 的 PurchaseOrder,那么正確的構(gòu)造是將 PurchaseOrder 與根 Customer 表達(dá)式連接起來(僅一次),把生成的連接表達(dá)式分配給本地變量(等效于 JPQL 中的范圍變量),并在構(gòu)成謂詞時(shí)使用本地變量。

使用參數(shù)

回顧一下本文初始的 JPQL 查詢(正確那個(gè)):

String jpql = "select p from Person p where p.age > 20";

盡管編寫查詢時(shí)通常包含常量文本值,但這不是一個(gè)良好實(shí)踐。良好實(shí)踐是參數(shù)化查詢,從而僅解析或準(zhǔn)備查詢一次,然后再緩存并重用它。因此,編寫查詢的最好方法是使用命名參數(shù):

String jpql = "select p from Person p where p.age > :age";

參數(shù)化查詢?cè)诓樵儓?zhí)行之前綁定參數(shù)的值:

Query query = em.createQuery(jpql).setParameter("age", 20);
List result = query.getResultList();

在 JPQL 查詢中,查詢字符串中的參數(shù)以命名方式(前面帶有冒號(hào),例如 :age)或位置方式(前面帶有問號(hào),例如 ?3)編碼。在 CriteriaQuery 中,參數(shù)本身就是查詢表達(dá)式。與其他表達(dá)式一樣,它們是強(qiáng)類型的,并且由表達(dá)式工廠(即 QueryBuilder)構(gòu)造。然后,可以參數(shù)化 清單 2 中的查詢,如清單 10 所示:


清單 10. 在 CriteriaQuery 中使用參數(shù)

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

比較該參數(shù)使用和 JPQL 中的參數(shù)使用:參數(shù)表達(dá)式被創(chuàng)建為帶有顯式類型信息 Integer,并且被直接用于將值 20 綁定到可執(zhí)行查詢。額外的類型信息對(duì)減少運(yùn)行時(shí)錯(cuò)誤十分有用,因?yàn)樽柚箙?shù)與包含不兼容類型的表達(dá)式比較,或阻止參數(shù)與不兼容類型的值綁定。JPQL 查詢的參數(shù)不能提供任何編譯時(shí)安全。

清單 10 中的例子顯示了一個(gè)直接用于綁定的未命名表達(dá)式。還可以在構(gòu)造參數(shù)期間為參數(shù)分配第二個(gè)名稱。對(duì)于這種情況,您可以使用這個(gè)名稱將參數(shù)值綁定到查詢。不過,您不可以使用位置參數(shù)。線性 JPQL 查詢字符串中的整數(shù)位置有一定的意義,但是不能在概念模型為查詢表達(dá)式樹的 CriteriaQuery 上下文中使用整數(shù)位置。

JPA 查詢參數(shù)的另一個(gè)有趣方面是它們沒有內(nèi)部值。值綁定到可執(zhí)行查詢上下文中的參數(shù)。因此,可以合法地從相同的 CriteriaQuery 創(chuàng)建兩個(gè)獨(dú)立可執(zhí)行的查詢,并為這些可執(zhí)行查詢的相同參數(shù)綁定兩個(gè)整數(shù)值。

預(yù)測(cè)結(jié)果

您已經(jīng)看到 CriteriaQuery 在執(zhí)行時(shí)返回的結(jié)果已經(jīng)在 QueryBuilder 構(gòu)造 CriteriaQuery 時(shí)指定。查詢的結(jié)果被指定為一個(gè)或多個(gè)預(yù)測(cè)條件。可以通過兩種方式之一在 CriteriaQuery 接口上指定預(yù)測(cè)條件:

CriteriaQuery< T> select(Selection< ? extends T> selection);CriteriaQuery< T> multiselect(Selection< ?>... selections);

最簡(jiǎn)單并且最常用的預(yù)測(cè)條件是查詢候選類。它可以是隱式的,如清單 11 所示:


清單 11. CriteriaQuery 默認(rèn)選擇的候選區(qū)段

CriteriaQuery< Account> q = cb.createQuery(Account.class);Root< Account> account = q.from(Account.class);
List< Account> accounts = em.createQuery(q).getResultList();

在 清單 11 中,來自 Account 的查詢沒有顯式地指定它的選擇條件,并且和顯式地選擇的候選類一樣。清單 12 顯示了一個(gè)使用顯式選擇條件的查詢:


清單 12. 使用單個(gè)顯式選擇條件的 CriteriaQuery

CriteriaQuery< Account> q = cb.createQuery(Account.class);Root< Account> account = q.from(Account.class);
q.select(account);
List< Account> accounts = em.createQuery(q).getResultList();

如果查詢的預(yù)測(cè)結(jié)果不是候選持久化實(shí)體本身,那么可以通過其他幾個(gè)構(gòu)造方法來生成查詢的結(jié)果。這些構(gòu)造方法包含在 QueryBuilder 接口中,如清單 13 所示:


清單 13. 生成查詢結(jié)果的方法

< Y> CompoundSelection< Y> construct(Class< Y> result, Selection< ?>... terms);CompoundSelection< Object[]> array(Selection< ?>... terms);CompoundSelection< Tuple> tuple(Selection< ?>... terms);

清單 13 中的方法構(gòu)建了一個(gè)由其他幾個(gè)可選擇的表達(dá)式組成的預(yù)測(cè)條件。construct() 方法創(chuàng)建給定類參數(shù)的一個(gè)實(shí)例,并使用來自輸入選擇條件的值調(diào)用一個(gè)構(gòu)造函數(shù)。例如,如果 CustomerDetails — 一個(gè)非持久化實(shí)體 — 有一個(gè)接受 Stringint 參數(shù)的構(gòu)造方法,那么 CriteriaQuery 可以通過從選擇的 Customer — 一個(gè)持久化實(shí)體 — 實(shí)例的名稱和年齡創(chuàng)建實(shí)例,從而返回 CustomerDetails 作為它的結(jié)果,如清單 14 所示:


清單 14. 通過 construct() 將查詢結(jié)果包放入類的實(shí)例

CriteriaQuery< CustomerDetails> q = cb.createQuery(CustomerDetails.class);Root< Customer> c = q.from(Customer.class);
q.select(cb.construct(CustomerDetails.class,
              c.get(Customer_.name), c.get(Customer_.age));

可以將多個(gè)預(yù)測(cè)條件合并在一起,以組成一個(gè)表示 Object[]Tuple 的復(fù)合條件。清單 15 顯示了如何將結(jié)果包裝到 Object[] 中:


清單 15. 將結(jié)果包裝到 Object[]

CriteriaQuery< Object[]> q = cb.createQuery(Object[].class);Root< Customer> c = q.from(Customer.class);
q.select(cb.array(c.get(Customer_.name), c.get(Customer_.age));
List< Object[]> result = em.createQuery(q).getResultList();

這個(gè)查詢返回一個(gè)結(jié)果列表,它的每個(gè)元素都是一個(gè)長(zhǎng)度為 2 的 Object[],第 0 個(gè)數(shù)組元素為 Customer 的名稱,第 1 個(gè)數(shù)組元素為 Customer 的年齡。

Tuple 是一個(gè)表示一行數(shù)據(jù)的 JPA 定義接口。從概念上看,Tuple 是一個(gè) TupleElement 列表 — 其中 TupleElement 是源自單元和所有查詢表達(dá)式的根。包含在 Tuple 中的值可以被基于 0 的整數(shù)索引訪問(類似于熟悉的 JDBC 結(jié)果),也可以被 TupleElement 的別名訪問,或直接通過 TupleElement 訪問。清單 16 顯示了如何將結(jié)果包裝到 Tuple 中:


清單 16. 將查詢結(jié)果包裝到 Tuple

CriteriaQuery< Tuple> q = cb.createTupleQuery();Root< Customer> c = q.from(Customer.class);
TupleElement< String> tname = c.get(Customer_.name).alias("name");
q.select(cb.tuple(tname, c.get(Customer_.age).alias("age");
List< Tuple> result = em.createQuery(q).getResultList();
String name = result.get(0).get(name);
String age  = result.get(0).get(1);
JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用 

這個(gè)查詢返回一個(gè)結(jié)果列表,它的每個(gè)元素都是一個(gè) Tuple。反過來,每個(gè)二元組都帶有兩個(gè)元素 — 可以被每個(gè) TupleElement 的索引或別名(如果有的話)訪問,或直接被 TupleElement 訪問。清單 16 中需要注意的兩點(diǎn)是 alias() 的使用,它是將一個(gè)名稱綁定到查詢表達(dá)式的一種方式(創(chuàng)建一個(gè)新的副本),和 QueryBuilder 上的 createTupleQuery() 方法,它僅是 createQuery(Tuple.class) 的代替物。

這些能夠改變結(jié)果的方法的行為和在構(gòu)造期間被指定為 CriteriaQuery 的類型參數(shù)結(jié)果共同組成 multiselect() 方法的語義。這個(gè)方法根據(jù)最終實(shí)現(xiàn)結(jié)果的 CriteriaQuery 的結(jié)果類型解釋它的輸入條件。要像 清單 14 一樣使用 multiselect() 構(gòu)造 CustomerDetails 實(shí)例,您需要將 CriteriaQuery 的類型指定為 CustomerDetails,然后使用將組成 CustomerDetails 構(gòu)造方法的條件調(diào)用 multiselect(),如清單 17 所示:


清單 17. 基于結(jié)果類型的 multiselect() 解釋條件

CriteriaQuery< CustomerDetails> q = cb.createQuery(CustomerDetails.class);Root< Customer> c = q.from(Customer.class);
q.multiselect(c.get(Customer_.name), c.get(Customer_.age));

因?yàn)椴樵兘Y(jié)果類型為 CustomerDetails,multiselect() 將其預(yù)測(cè)條件解釋為 CustomerDetails 構(gòu)造方法參數(shù)。如將查詢指定為返回 Tuple,那么帶有相同參數(shù)的 multiselect() 方法將創(chuàng)建 Tuple 實(shí)例,如清單 18 所示:


清單 18. 使用 multiselect() 方法創(chuàng)建 Tuple 實(shí)例

CriteriaQuery< Tuple> q = cb.createTupleQuery();Root< Customer> c = q.from(Customer.class);
q.multiselect(c.get(Customer_.name), c.get(Customer_.age));

如果以 Object 作為結(jié)果類型或沒有指定類型參數(shù)時(shí),multiselect() 的行為會(huì)變得更加有趣。在這些情況中,如果 multiselect() 使用單個(gè)輸入條件,那么返回值將為所選擇的條件。但是如果 multiselect() 包含多個(gè)輸入條件,結(jié)果將得到一個(gè) Object[]。

高級(jí)特性

到目前為止,我主要強(qiáng)調(diào)了 Criteria API 的強(qiáng)類型,以及它如何幫助減少出現(xiàn)在基于字符串 JPQL 查詢中的語義錯(cuò)誤。Criteria API 還是以編程的方式構(gòu)建查詢的機(jī)制,因此通常被稱為動(dòng)態(tài) 查詢 API。編程式查詢構(gòu)造 API 的威力是無窮的,但它的利用還取決于用戶的創(chuàng)造能力。我將展示 4 個(gè)例子:

  • 使用弱類型的 API 構(gòu)建動(dòng)態(tài)查詢

  • 使用數(shù)據(jù)庫支持的函數(shù)作為查詢表達(dá)式來擴(kuò)展語法

  • 編輯查詢實(shí)現(xiàn) “在結(jié)果中搜索” 功能

  • 根據(jù)例子進(jìn)行查詢 — 數(shù)據(jù)庫社區(qū)熟悉的模式

弱類型和動(dòng)態(tài)查詢構(gòu)建

Criteria API 的強(qiáng)類型檢查基于開放期間的實(shí)例化元模型類的可用性。不過,在某些情況下,選擇的實(shí)體僅能夠在運(yùn)行時(shí)決定。為了支持這種用法,Criteria API 方法提供一個(gè)并列版本,其中持久化屬性通過它們的名稱進(jìn)行引用(類似于 Java Reflection API),而不是引用實(shí)例化靜態(tài)元模型屬性。該 API 的這個(gè)并列版本可以通過犧牲編譯時(shí)類型檢查來真正地支持動(dòng)態(tài)查詢構(gòu)造。清單 19 使用弱類型 API 重新編寫了 清單 6 中的代碼:


清單 19. 弱類型查詢

Class< Account> cls =Class.forName("domain.Account");Metamodel model = em.getMetamodel();
EntityType< Account> entity = model.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)));

不過,弱類型 API 不能夠返回正確的泛型表達(dá)式,因此生成一個(gè)編輯器來警告未檢查的轉(zhuǎn)換。一種消除這些煩人的警告消息的方法是使用 Java 泛型不常用的工具:參數(shù)化方法調(diào)用,比如 清單 19 中通過調(diào)用 get() 方法獲取路徑表達(dá)式。

可擴(kuò)展數(shù)據(jù)庫表達(dá)式

動(dòng)態(tài)查詢構(gòu)造機(jī)制的獨(dú)特優(yōu)勢(shì)是它的語法是可擴(kuò)展的。例如,您可以在 QueryBuilder 接口中使用 function() 方法創(chuàng)建數(shù)據(jù)庫支持的表達(dá)式:

< T> Expression< T> function(String name, Class< T> type, Expression< ?>...args);

function() 方法創(chuàng)建一個(gè)帶有給定名稱和 0 個(gè)或多個(gè)輸入表達(dá)式的表達(dá)式。function() 表達(dá)式的計(jì)算結(jié)果為給定的類型。這允許應(yīng)用程序創(chuàng)建一個(gè)計(jì)算數(shù)據(jù)庫的查詢。例如,MySQL 數(shù)據(jù)庫支持 CURRENT_USER() 函數(shù),它為服務(wù)器用于驗(yàn)證當(dāng)前客戶機(jī)的 MySQL 帳戶返回一個(gè)由用戶名和主機(jī)名組成的 UTF-8 字符串。應(yīng)用程序可以在 CriteriaQuery 中使用未帶參數(shù)的 CURRENT_USER() 函數(shù),如清單 20 所示:


清單 20. 在 CriteriaQuery 中使用特定于數(shù)據(jù)庫的函數(shù)

CriteriaQuery< Tuple> q = cb.createTupleQuery();Root< Customer> c = q.from(Customer.class);Expression< String> currentUser = 
    cb.function("CURRENT_USER", String.class, (Expression< ?>[])null);
q.multiselect(currentUser, c.get(Customer_.balanceOwed));

注意,在 JPQL 中不能表達(dá)等效的查詢,因?yàn)樗恼Z法僅支持固定數(shù)量的表達(dá)式。動(dòng)態(tài) API 不受固定數(shù)量表達(dá)式的嚴(yán)格限制。

可編輯查詢

可以以編程的方式編輯 CriteriaQuery??梢愿淖儾樵兊淖泳?,比如它的選擇條件、WHERE 子句中的選擇謂詞和 ORDER BY 子句中的排序條件??梢栽诘湫偷?“在結(jié)果中搜索” 工具中使用這個(gè)編輯功能,以添加更多限制在后續(xù)步驟中進(jìn)一步細(xì)化查詢謂詞。

清單 21 中的例子創(chuàng)建了一個(gè)根據(jù)名稱對(duì)結(jié)果進(jìn)行排序的查詢,然后編輯該查詢以根據(jù)郵政編碼進(jìn)行查詢:


清單 21. 編輯 CriteriaQuery

CriteriaQuery< Person> c = cb.createQuery(Person.class);Root< Person> p = c.from(Person.class);
c.orderBy(cb.asc(p.get(Person_.name)));
List< Person> result = em.createQuery(c).getResultList();// start editingList< Order> orders = c.getOrderList();
List< Order> newOrders = new ArrayList< Order>(orders);
newOrders.add(cb.desc(p.get(Person_.zipcode)));
c.orderBy(newOrders);
List< Person> result2 = em.createQuery(c).getResultList();
JPA 2.0動(dòng)態(tài)查詢機(jī)制Criteria API怎么用 

網(wǎng)站標(biāo)題:JPA2.0動(dòng)態(tài)查詢機(jī)制CriteriaAPI怎么用
網(wǎng)頁鏈接:http://weahome.cn/article/jpgdgg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部