国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JPA2.1 中三個提升應用性能的新功能

ormsf / 1214人閱讀

摘要:只用語句,也能完成很多事情,如果不夠,你還可以調用數據庫的特定功能和存儲過程。在中,并沒有針對存儲過程的實際支持,本地查詢是調用存儲過程的唯一方式。規范引入了幾個新的功能以應對這些低效操作,比如實體圖,條件更新和存儲過程查詢。

經常在網上看到開發者們抱怨 JPA 性能低下的帖子或文章,但如果仔細查看這些性能問題,常會發現導致問題的根本原因大致包括以下幾個:

使用過多的 SQL 查詢從數據庫中獲取所需的實體信息,即我們常說的n+1查詢問題

逐個更新實體,而不是使用單條語句進行更新

使用 Java 應用程序而非數據庫進行大量數據處理

JPA提供了處理這類問題的方法,并給 JPA2.1 增加了一些額外功能,可以極大地提升性能表現,筆者將在本文中解釋如何利用 JPA2.1 的功能避免上述問題。

順便提一下,如果想了解Java項目中更多的典型性能問題,可以參考筆者最近發布的基于性能調查結果的深度報告,如果你在尋找 JPA 資源,點擊此鏈接便可獲取JPA2.1特征的備忘清單。接下來我們來看看如何用JPA來解決現有的性能問題。

解決「SQL 查詢過多」的問題

根據以往的經驗,使用過多的 SQL 查詢獲取所要求的實體是導致性能問題最普遍的原因。

即使是看起來最簡單的查詢,如果操作不當,也會觸發幾十次甚至上百次的 SQL 查詢。而且,你在本節中可以看到,這類不當操作不一定會出現在查詢語句中,而可能只是幾個配置不當的注解。所以,如果你覺得這個問題不會造成影響,請三思。

如果在你的項目中出現以下幾段代碼,你會怎么想?

List authors = this.em.createQuery("SELECT a FROM Author a",
        Author.class).getResultList();

for (Author a : authors) {
    System.out.println("作者 "
            + a.getFirstName()
            + " "
            + a.getLastName()
            + " 書籍信息 "
            + a.getBooks()
                    .stream()
                    .map(b -> b.getTitle() + "("
                            + b.getReviews().size() + " 評論)")
                    .collect(Collectors.joining(", ")));
}

上面的代碼段會打印所有作者的姓名及其書名,看起來非常簡單,但你是否想過它給數據庫發送了多少次查詢?一次?還是兩次?或者 Author、Book、Review 實體各一次?

實際上,這取決于數據庫中作者的人數。如果數據庫較小,里面只有11名作者和6本書。那么這段代碼會觸發12次查詢,其中1次用于獲取所有作者姓名,另外11次給每位作者匹配書名。這一問題被稱作 n+1 查詢問題,無論我們使用的是 MySQL、SqlServer 還是其他數據庫,都容易出現此類問題。因此在生產環境中,隨著數據量不斷增大,代碼的性能就越差。

我們可以通過多種方法,用一次查詢獲取所有要求的實體信息 ,從而避免這一情況。在筆者看來,使用 @NamedEntityGraph 來解決此問題是最新,也最好的方法。

實體圖通過獨立于查詢的方法指定應該從數據庫中獲取的實體的圖。這意味著,你需要為實體圖創建一個獨立的定義,并在需要時與查詢合并。下段代碼展示了如何定義根據作者名提取書名的 @NamedEntityGraph

@Entity
@NamedEntityGraph(name = "graph.AuthorBooks", attributeNodes = @NamedAttributeNode("books"))
public class Author implements Serializable {
…
}

現在,實體管理器可以用這個圖為參考,通過一次查詢獲取所有作者和書名。在圖的定義中可以看到,筆者只提供了包含相關實體的屬性名稱。因此,筆者將@NamedEntityGraph作為loadgraph (負載圖),這樣便可提取其他所有屬性及其定義的獲取類型,如下所示:

EntityGraph graph = this.em.getEntityGraph("graph.AuthorBooks");

List authors = this.em
.createQuery("SELECT DISTINCT a FROM Author a", Author.class)
.setHint("javax.persistence.loadgraph", graph).getResultList();

該示例展示了一個非常簡單的實體圖,在實際的應用中,很可能會用到更復雜的圖,但這也不成問題。你可以定義多個 @NamedAttributeNodes 以定義更復雜的圖,也可以用 @NamedSubGraph 注解來創建多層次的圖。如果想了解更多關于 @NamedEntityGraphs 的信息,請點擊實體圖使用方式詳解。

在某些使用案例中,你可能還需要用更動態的方式來定義實體圖,比如,根據一些輸入參數進行定義。在此類案例中,通過 Java API 用編程的方式定義實體圖效果更佳。

解決「逐個更新實體」的問題

逐個更新實體是造成 JPA 性能問題的另一個常見原因。作為 Java 開發者,我們習慣處理對象,并用面向對象的方式思考問題。盡管這是實現復雜邏輯和應用的好方法,但也是處理數據庫時導致性能退化的一個常見原因。

從面向對象的角度來看,對實體進行更新和刪除操作是完全可以接受的。但當你不得不更新一大組實體時,這種操作就會非常低效。持久性提供者(Persistence Provider)將為每個更新實體創建一個更新語句,并在下一次 flush 操作時發送至數據庫中。

然而,SQL 提供了一個更為高效的方式。它允許你創建可一次性更新多個實體的更新語句。你還可以對 JPA 2.1 引入的 CriteriaUpdateCriteriaDelete 語句進行同樣的操作。

如果你之前用過 criteria 條件查詢,肯定對新的 CriteriaUpdate 以及 CriteriaDelete 語句非常熟悉,更新和刪除操作的創建方式幾乎與 JPA 2.0 中引入的 criteria 條件查詢創建方式一樣。

在下面的代碼段中可以看到,你需要從實體管理器中獲取 CriteriaBuilder 并用它創建 CriteriaUpdate 對象,對 CriteriaQuery 進行的操作與此類似,主要區別在于用于定義更新操作的 set 方法。

CriteriaBuilder cb = this.em.getCriteriaBuilder();
// create update
CriteriaUpdate update = cb.createCriteriaUpdate(Author.class);
// set the root class
Root a = update.from(Author.class);
// set update and where clause
update.set(Author_.firstName, cb.concat(a.get(Author_.firstName), " - updated"));
update.where(cb.greaterThanOrEqualTo(a.get(Author_.id), 3L));

// perform update
Query q = this.em.createQuery(update);
q.executeUpdate();

CriteriaDelete 操作中,你只需要在實體管理器中調用 createCriteriaDelete 方法以獲取 CriteriaDelete 對象,并用它來定義與上例類似的 FROMWHERE 查詢部分。

在數據庫中處理數據

作為 Java 開發者,我們傾向于在 Java 中實現所有的應用邏輯,這也是造成性能問題的一大常見原因。別誤會,在 Java 中實現邏輯的好處很多,但如果將部分邏輯實現在數據庫中,只把結果發送到業務邏輯層,也能得到很好的效果。

在數據庫中執行邏輯的方法很多。只用 SQL 語句,也能完成很多事情,如果不夠,你還可以調用數據庫的特定功能和存儲過程。在本文中,筆者將仔細探討存儲過程,更確切地說是探討調用存儲過程的方式。

在 JPA 2.0 中,并沒有針對存儲過程的實際支持,本地查詢是調用存儲過程的唯一方式。JPA 2.1.引入了 @NamedStoredProcedureQuery 和更為動態的 StoredProcedureQuery,改變了這一現狀。在本文中,筆者將重點關注基于注解的、用 @NamedStoredProcedureQuery 進行調用的存儲過程的定義。筆者在自己的博客中詳細介紹了動態存儲過程查詢 。

在下面代碼段中可以看到, @NamedStoredProcedureQuery 的定義非常簡潔,你需要指定查詢的名稱、數據庫中的存儲過程名稱以及輸入和輸出參數。在本例中,筆者用輸入參數 xy 調用存儲過程 calculate,期望的輸出參數為 sum,其它支持的參數類型還有用于輸入和輸出的參數 INPUT 和用于檢索結果集的 REF_COURSOR

@NamedStoredProcedureQuery(
name = "calculate",
procedureName = "calculate",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "x"),
@StoredProcedureParameter(mode = ParameterMode.IN, type = Double.class, name = "y"),
@StoredProcedureParameter(mode = ParameterMode.OUT, type = Double.class, name = "sum") })

@NamedStoredProcedureQuery 的使用方法與 @NamedQuery 相似,你需要向實體管理器的createNamedStoredProcedureQuery 方法提供查詢名稱,以便在本次查詢中獲取 StoredProcedureQuery 對象,然后,用 setParameter 方法設定輸入參數,之后再用 execute 方法調用存儲過程。

StoredProcedureQuery query = this.em.createNamedStoredProcedureQuery("calculate");
query.setParameter("x", 1.23d);
query.setParameter("y", 4.56d);
query.execute();
Double sum = (Double) query.getOutputParameterValue("sum");
總結

JPA 給數據庫存儲和檢索帶來諸多便利。通過這一工具,可快速開展項目,解決大部分問題,但也更容易導致實現非常低效的持久層。由此,普遍存在的問題包括:使用過多查詢獲取所需數據、逐個更新實體以及在 Java 中執行所有邏輯。

JPA 2.1規范引入了幾個新的功能以應對這些低效操作,比如實體圖(entity graphs),條件更新(criteria update)和存儲過程查詢(stored procedure queries)。筆者的JPA2.1新功能備忘單囊括了JPA 2.1的這些功能及其他新功能,你可以免費下載。

(編譯自:http://zeroturnaround.com/rebellabs/three-jpa-2-1-features-that-will-boost-your-applications-performance/)

OneAPM 為您提供端到端的 Java 應用性能解決方案,我們支持所有常見的 Java 框架及應用服務器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。

本文轉自 OneAPM 官方博客

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65344.html

相關文章

  • JPA2.1 三個提升應用性能功能

    摘要:只用語句,也能完成很多事情,如果不夠,你還可以調用數據庫的特定功能和存儲過程。在中,并沒有針對存儲過程的實際支持,本地查詢是調用存儲過程的唯一方式。規范引入了幾個新的功能以應對這些低效操作,比如實體圖,條件更新和存儲過程查詢。 經常在網上看到開發者們抱怨 JPA 性能低下的帖子或文章,但如果仔細查看這些性能問題,常會發現導致問題的根本原因大致包括以下幾個: 使用過多的 SQL 查詢從...

    qpwoeiru96 評論0 收藏0

發表評論

0條評論

ormsf

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<