摘自 IBM WebSphere 開發者技術期刊。
對注釋的注釋
Java Persistence API (JPA) 定義了訪問數據的多種方法:通過實體管理器、通過 JPA-QL 或通過本機查詢。在 JPA 中,注釋用作將 Java 對象映射到底層數據庫的一種機制。您還可以提供 XML 元數據作為映射注釋的覆蓋或備選機制。不過,我看到的大多數 JPA 使用情況都明顯喜歡使用注釋。規范文檔使用注釋,而不使用基于 XML 的映射示例(僅向您顯示 XML 模式)來表示所有示例這一事實可能是覆蓋的原因之一。創建對象關系映射,以便從 Java 對象模型抽象底層數據庫的詳細信息。不過,JPA 可以讓數據庫詳細信息快速返回到 Java 源。在本文中,將檢查 JPA 中的各種查詢樣式,解釋它們存在的原因,并解釋為什么對某些樣式(如命名查詢)進行注釋沒有任何意義。最后得出的結論是,這個小示例實際上是更大的問題的一部分。
![]() ![]() |
![]()
|
使用 JPA 訪問數據
讓我們快速瀏覽一下使用 JPA 訪問數據的各種方法,假定您非常熟悉在 JPA 中映射 Java 對象的方式。(有關詳細信息,請參閱參考資料。)
EntityManager
應用程序在運行時與 Java 對象交互。通過使用稱為實體管理器的特殊對象,應用程序可以查詢或保持對象。EntityManager 實例與永久性上下文關聯。在永久性上下文中,實體實例及其生命周期得到管理??梢哉J為 EntityManager 是底層永久性機制的 Facade。EntityManager 包含訪問數據的必要方法。最簡單的訪問持久性數據的方法是使用 find 方法。下面是使用實體管理器通過主鍵查找對象的應用程序示例:
|
find 方法要求您知道主鍵和實際類的類型。
JPA-QL 查詢
JPA 還擁有可以用于對象模型的全功能查詢語言。JPA 查詢語言包含許多用于更復雜查詢的功能??梢酝ㄟ^動態方式將查詢傳遞到實體管理器:
|
能夠在運行時傳遞查詢是某些動態情形(如未知條件)所必需的。不過,在大多數情形中,您希望基于整個性能測試來鎖定查詢。
本機查詢
JPA 還使您能夠對基礎表使用本機 SQL 查詢,并提供映射回結果的能力:
|
標準 SQL 在許多情形中都是必需的。我在以前的評論專欄中給出了許多理由。
命名查詢
在大多數情形中,您希望定義可以重用的知名查詢。命名查詢使您能夠在單個位置定義一個查詢。命名查詢有許多優點:
您可以將命名查詢定義為注釋,并在代碼中的其他位置執行它。下面是執行此類命名查詢的一個使用示例:
|
下面是如何定義命名查詢,并將其與實體類關聯的一個示例:
|
![]() ![]() |
![]()
|
使用帶注釋的命名查詢所帶來的問題
稍等片刻! 我剛才不是說過嗎?命名查詢是外部化查詢的好方法。盡管將命名查詢定義為注釋可以使查詢在代碼中得到更多重用,但是優點很少,在本質上沒有使用價值。我可以方便地將命名查詢打包在一個方法中,并重復執行它。其主要優點是查詢的外部化,因此,帶注釋的命名查詢(如果有)幾乎沒有任何意義。
還可以使用 XML 元數據定義 NamedQuery:
|
現在,我可以方便地對命名查詢進行更改,而無需更改源代碼和重新編譯?;貞浳沂褂?CMP 的時候,查詢位于 XML 部署描述符中的這一事實致使此因素無任何意義。所有查詢都定義在單個 ejb-jar.xml 文件中,打包在 EJB JAR中,并進一步打包在 EAR 中。在 XML 文件中更改 JPA-QL 的工作量仍很大。(Martin Fowler 也說明了這一點。)
雖然可以采用備用描述符思想,但是單一元數據使它成為一個不引人注意的選擇。
JPA 有三個重要的不同之處:
例如,可以將懷疑可能更改的任何查詢放在外部。在必須更改源、執行構建和安裝應用程序時,考慮花費的時間量。如果能夠僅更改查詢并重新啟動,則可以更快地進行測試。在性能測試過程中能夠快速更改查詢可以大大縮短測試周期。
另一種情況是銷售軟件的 ISV 需要針對他們銷售的產品優化查詢,以便與客戶選擇的特定數據庫供應商合作。例如,更改某些子句的排序可以幫助提高某些數據庫的性能。如果無法更改查詢,則不能改進性能。
使用本機命名查詢的能力更加強大,因為您可以使用本機 SQL 將復雜查詢映射回 POJO,即使它們已通過其他方式映射。請看以下示例:
|
在上面的代碼中,即使使用注釋、單獨的 XML 文件或不使用任何內容(必須映射每個字段)來映射 Order 或 Item 類,我仍能夠使用此 XML 覆蓋它。當需要 SQL 的靈活性時,通過 JPA 本機查詢,可以將 JPA 用作 JDBC 框架(類似于 IBatis)。(在我的博客和以后的文章中,我將說明此示例。)
![]() ![]() |
![]()
|
更大的問題
我選擇使用命名查詢來說明一個更大的問題。將對象關系域映射 (ORM) 創建為支持抽象的應用程序,以便使用面向對象的技術來滿足他們的域。ORM 將抽象出數據庫。帶注釋的映射不能滿足此類抽象的要求。盡管此類抽象并不是一直需要,但它適合于許多情形。
JPA 規范本身就很有吸引力。JPA 使用外部 XML 映射文件支持映射您的域模型。不過,示例和文檔中幾乎沒有這方面的說明。規范委員會有很大責任,因為他們僅提供帶注釋的實例,這暗示注釋是 JPA 的首選機制。但是,他們有正當理由建議映射文件可能是首選機制:
客戶端應用程序可以共享源代碼,因此,它們完全了解底層數據庫的知識。甚至可以嘗試在客戶端計算機上執行 SQL 操作。
如果希望將對象映射到多個數據庫,又該如何操作呢?盡管可以使用 XML 覆蓋注釋,但是如果對象有幾個映射,那么我可能不會喜歡任何一個。在 SOA 環境中,我可以跨 ESB 發送對象,每個服務都需要有指向自已環境的映射。
如果數據庫發生更改——即使使用 XML 覆蓋映射——則源代碼無法在語義上映射底層映射。任何人都不知道 XML 覆蓋會出現錯誤和假設。在正常部署過程中,信息(如數據庫架構名稱)會不斷變化,而在源中具有此信息無疑會阻礙部署。
對于開發人員來說,注釋的確使某些事情變得非常容易,但這又以喪失簡潔性為代價。我認為全球的 JPA 編寫者通常在進行一些不利于社區的活動,因為不能記錄使用 JPA 的 XML 映射樣式的清晰示例。
![]() ![]() |
![]()
|
結束語
命名查詢只是注釋如何被過度使用的一個示例。注釋有許多用途,但是我擔心在 ORM 范圍內濫用了它們。在我的網絡日志中,我將花費一些時間來說明使用 JPA 的 XML 映射樣式的示例。
![]() ![]() |
![]()
|
致謝
感謝 Keys Botzum 和 Tom Alcott,他們對本文提出了寶貴意見。
原文轉自:http://www.anti-gravitydesign.com