JPA技巧:避免N + 1选择问题
介紹
諸如JPA的ORM框架通過幫助我們在對象<->關系數據映射期間避免了很多樣板代碼,從而簡化了我們的開發過程。 但是,它們還會給表帶來一些其他問題,N + 1是其中之一。 在本文中,我們將簡短地探討該問題以及避免這些問題的一些方法。
問題
作為示例,我將使用在線圖書訂購應用程序的簡化版本。 在這樣的應用程序中,我可能會創建一個如下所示的實體來代表采購訂單–
@Entity public class PurchaseOrder {@Idprivate String id;private String customerId;@OneToMany(cascade = ALL, fetch = EAGER)@JoinColumn(name = "purchase_order_id")private List<PurchaseOrderItem> purchaseOrderItems = new ArrayList<>(); }采購訂單由訂單ID,客戶ID和要購買的一個或多個項目組成。 PurchaseOrderItem實體可能具有以下結構–
@Entity public class PurchaseOrderItem {@Idprivate String id;private String bookId; }這些實體已經簡化了很多,但是出于本文的目的,這是可以做到的。
現在假設我們需要找到一個客戶的訂單以在他們的采購訂單歷史中顯示它們。 以下查詢將用于此目的–
SELECTP FROMPurchaseOrder P WHEREP.customerId = :customerId轉換為SQL后,其外觀如下所示–
selectpurchaseor0_.id as id1_1_,purchaseor0_.customer_id as customer2_1_ frompurchase_order purchaseor0_ wherepurchaseor0_.customer_id = ?此查詢將返回客戶擁有的所有采購訂單。 但是,為了獲取訂單項,JPA將針對每個單獨的訂單發出單獨的查詢。 例如,如果客戶有5個訂單,那么JPA將發出5個附加查詢以獲取這些訂單中包含的訂單項。 這基本上被稱為N + 1問題-1個查詢以獲取所有N個采購訂單,N個查詢以獲取所有訂單商品。
當我們的數據增長時,此行為為我們帶來了可伸縮性問題。 即使適量的訂單和物品也會造成嚴重的性能問題。
解決方案
避免渴望獲取
這是問題背后的主要原因。 我們應該擺脫從映射中獲取的所有渴望。 它們幾乎沒有任何好處可證明其可用于生產級應用程序。 我們應該將所有關系標記為“懶惰”。
需要注意的重要一點–將關系映射標記為“惰性”并不保證基礎持久性提供程序也將其同樣對待。 JPA規范不保證延遲獲取。 充其量對持久性提供程序而言是一個提示。 但是,考慮到Hibernate,我從未見過這樣做。
僅獲取實際需要的數據
始終建議使用此方法,而不考慮是否要進行急切/懶惰的訪存。
我記得我進行過一次N + 1優化,將REST端點的最大響應時間從17分鐘提高到1.5秒 。 端點正在根據某些條件獲取單個實體,對于我們當前的示例,該實體將遵循以下原則:
TypedQuery<PurchaseOrder> jpaQuery = entityManager.createQuery("SELECT P FROM PurchaseOrder P WHERE P.customerId = :customerId", PurchaseOrder.class); jpaQuery.setParameter("customerId", "Sayem"); PurchaseOrder purchaseOrder = jpaQuery.getSingleResult();// after some calculation anotherRepository.findSomeStuff(purchaseOrder.getId());id是結果中唯一用于后續計算的數據。
有幾個客戶有超過一千個訂單。 每個命令依次又有數千個其他幾種不同類型的子級。 不用說,每當在此端點接收到針對這些訂單的請求時,數據庫中就會執行數千個查詢。
為了提高性能,我所做的就是-
只是此更改導致680倍的改進 。
如果我們要獲取多個屬性,則可以利用JPA提供的Constructor表達式–
使用構造函數表達式的一些注意事項–
使用聯接提取/實體圖
每當我們需要同時獲取帶有所有子元素的實體時,就可以在查詢中使用JOIN FETCH 。 這樣可以減少數據庫流量,從而提高性能。
JPA 2.1規范引入了實體圖,它使我們可以創建靜態/動態查詢負載計劃。
Thorben Janssen ( 這里和這里 )寫了幾篇文章,詳細介紹了它們的用法,值得一看。
這篇文章的一些示例代碼可以在Github上找到。
翻譯自: https://www.javacodegeeks.com/2018/04/jpa-tips-avoiding-the-n-1-select-problem.html
總結
以上是生活随笔為你收集整理的JPA技巧:避免N + 1选择问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓投影到电视上(安卓投影到电视)
- 下一篇: 回调函数中有回调函数吗_嗨,那里有回调!