程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java Persistence API中帶注釋的命名查詢是否真的非常有用?

Java Persistence API中帶注釋的命名查詢是否真的非常有用?

編輯:關於JAVA

對注釋的注釋

Java Persistence API (JPA) 定義了訪問數據的多種方法:通過實體管理器、通過 JPA-QL 或通過本機查詢。在 JPA 中,注釋用作將 Java 對象映射到底層數據庫的一種機制。您還可以提供 XML 元數據作為映射注釋的覆蓋或備選機制。不過,我看到的大多數 JPA 使用情況都明顯喜歡使用注釋。規范文檔使用注釋,而不使用基於 XML 的映射示例(僅向您顯示 XML 模式)來表示所有示例這一事實可能是覆蓋的原因之一。創建對象關系映射,以便從 Java 對象模型抽象底層數據庫的詳細信息。不過,JPA 可以讓數據庫詳細信息快速返回到 Java 源。在本文中,將檢查 JPA 中的各種查詢樣式,解釋它們存在的原因,並解釋為什麼對某些樣式(如命名查詢)進行注釋沒有任何意義。最後得出的結論是,這個小示例實際上是更大的問題的一部分。

使用 JPA 訪問數據

讓我們快速浏覽一下使用 JPA 訪問數據的各種方法,假定您非常熟悉在 JPA 中映射 Java 對象的方式。

EntityManager

應用程序在運行時與 Java 對象交互。通過使用稱為實體管理器的特殊對象,應用程序可以查詢或保持對象。EntityManager 實例與永久性上下文關聯。在永久性上下文中,實體實例及其生命周期得到管理。可以認為 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 在許多情形中都是必需的。我在以前的評論專欄中給出了許多理由。

命名查詢

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

外部化或分離查詢可以在更改查詢時實現某些靈活性。這在優化查詢和鎖定查詢時可能較為有用。

您可以從多個位置使用查詢。

您可以將查詢與知名的業務名稱關聯。

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

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 中(甚至松散打包)。

可以有任意多個映射文件。可以進行設計,讓關聯實體映射到一個 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 映射樣式的示例。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved