專家評論: Roland Barcia:Java Persistence API 中帶注釋的命名查詢是否真的非常有用?

發表于:2007-05-24來源:作者:點擊數: 標簽:javaRolandBarciaPersi評論
對于開發人員來說,注釋的確使某些事情變得非常容易,但這又以喪失簡潔性為代價。在 Java Persistence API (JPA) 中,注釋用作將 Java 對象映射到底層數據庫的一種機制,不過即使在注釋沒有任何意義時,開發人員也常常使用它。本文介紹通過 JPA 訪問數據的一
對于開發人員來說,注釋的確使某些事情變得非常容易,但這又以喪失簡潔性為代價。在 Java™ Persistence API (JPA) 中,注釋用作將 Java 對象映射到底層數據庫的一種機制,不過即使在注釋沒有任何意義時,開發人員也常常使用它。本文介紹通過 JPA 訪問數據的一些其他方法,以及這些方法作為最佳選擇的時間和原因。

摘自 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 方法。下面是使用實體管理器通過主鍵查找對象的應用程序示例:

Customer customer = (Customer)em.find(Customer.class,customerId);

find 方法要求您知道主鍵和實際類的類型。

JPA-QL 查詢

JPA 還擁有可以用于對象模型的全功能查詢語言。JPA 查詢語言包含許多用于更復雜查詢的功能??梢酝ㄟ^動態方式將查詢傳遞到實體管理器:

Query q = em.createQuery("SELECT c FROM Customer c WHERE c.name LIKE
            :custName");
            q.setParameter("custName", name);
            q.setMaxResults(10);
            List result = q.getResultList();

能夠在運行時傳遞查詢是某些動態情形(如未知條件)所必需的。不過,在大多數情形中,您希望基于整個性能測試來鎖定查詢。

本機查詢

JPA 還使您能夠對基礎表使用本機 SQL 查詢,并提供映射回結果的能力:

Query q = em.createNativeQuery(
            "SELECT o.id, o.quantity, o.item, i.id, i.name, i.description "+
            "FROM Order o, Item i " +
            "WHERE (o.quantity > 25) AND (o.item = i.id)",
            "OrderItemResults");

標準 SQL 在許多情形中都是必需的。我在以前的評論專欄中給出了許多理由。

命名查詢

在大多數情形中,您希望定義可以重用的知名查詢。命名查詢使您能夠在單個位置定義一個查詢。命名查詢有許多優點:

  1. 外部化或分離查詢可以在更改查詢時實現某些靈活性。這在優化查詢和鎖定查詢時可能較為有用。
  2. 您可以從多個位置使用查詢。
  3. 您可以將查詢與知名的業務名稱關聯。

您可以將命名查詢定義為注釋,并在代碼中的其他位置執行它。下面是執行此類命名查詢的一個使用示例:

Query allOrders = em.createNamedQuery("getAllOrder");
            allOrders.setParameter("customerId",customerId);
            Collection <CustomerOrder>  results = allOrders.getResultList();
            return results;

下面是如何定義命名查詢,并將其與實體類關聯的一個示例:

@Entity
            @Table(name="ORDER")
            @NamedQuery(
            name="getAllOrder",
            queryString="SELECT OBJECT(o) FROM CustomerOrder as o WHERE
            o.customerId = :customerId")
            public class CustomerOrder implements Serializable {
            





回頁首


使用帶注釋的命名查詢所帶來的問題

稍等片刻! 我剛才不是說過嗎?命名查詢是外部化查詢的好方法。盡管將命名查詢定義為注釋可以使查詢在代碼中得到更多重用,但是優點很少,在本質上沒有使用價值。我可以方便地將命名查詢打包在一個方法中,并重復執行它。其主要優點是查詢的外部化,因此,帶注釋的命名查詢(如果有)幾乎沒有任何意義。

還可以使用 XML 元數據定義 NamedQuery:

<named-query name="getAllOrders" queryString="SELECT OBJECT(o)
            FROM CustomerOrder as o WHERE o.customerId = :customerId" />

現在,我可以方便地對命名查詢進行更改,而無需更改源代碼和重新編譯?;貞浳沂褂?CMP 的時候,查詢位于 XML 部署描述符中的這一事實致使此因素無任何意義。所有查詢都定義在單個 ejb-jar.xml 文件中,打包在 EJB JAR中,并進一步打包在 EAR 中。在 XML 文件中更改 JPA-QL 的工作量仍很大。(Martin Fowler 也說明了這一點。)

雖然可以采用備用描述符思想,但是單一元數據使它成為一個不引人注意的選擇。

JPA 有三個重要的不同之處:

  • 只要映射文件在類路徑中存在,那么它們就可以打包在任何 JAR 中(甚至松散打包)。
  • 可以有任意多個映射文件??梢赃M行設計,讓關聯實體映射到一個 XML 文件中,或將它們分離出去??梢栽谝粋€單獨的 XML 文件中保存所有查詢。這就是我要建議的內容。
  • 最后,您可以擁有命名本機查詢,并將結果映射回 POJO。

例如,可以將懷疑可能更改的任何查詢放在外部。在必須更改源、執行構建和安裝應用程序時,考慮花費的時間量。如果能夠僅更改查詢并重新啟動,則可以更快地進行測試。在性能測試過程中能夠快速更改查詢可以大大縮短測試周期。

另一種情況是銷售軟件的 ISV 需要針對他們銷售的產品優化查詢,以便與客戶選擇的特定數據庫供應商合作。例如,更改某些子句的排序可以幫助提高某些數據庫的性能。如果無法更改查詢,則不能改進性能。

使用本機命名查詢的能力更加強大,因為您可以使用本機 SQL 將復雜查詢映射回 POJO,即使它們已通過其他方式映射。請看以下示例:

<?xml version="1.0" encoding="UTF-8"?>
            <entity-mappings xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm jpa.xsd ">
            <named-native-query name=" OrderItemResults " query="SELECT o.id,
            o.quantity, o.item, i.id, i.name, i.description FROM Order o, Item i WHERE
            (o.quantity > 25) AND (o.item = i.id)" result-class=""
            result-set-mapping="OrderItemResults" />
            <sql-result-set-mapping name="">
            <entity-result entity-class="com.acme.Order.class">
            <!--       <field-result column="" name=""/>
            <etc..> -->
            </entity-result>
            <entity-result entity-class="com.acme.Item.class">
            <!--        <field-result column="" name=""/>
            <etc..> -->
            </entity-result>
            </sql-result-set-mapping>
            </entity-mapping>

在上面的代碼中,即使使用注釋、單獨的 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

評論列表(網友評論僅供網友表達個人看法,并不表明本站同意其觀點或證實其描述)
国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97