jpa避免n+1_JPA技巧:避免N + 1选择问题
jpa避免n+1
介紹
像JPA這樣的ORM框架通過幫助我們?cè)趯?duì)象<->關(guān)系數(shù)據(jù)映射期間避免了很多樣板代碼,從而簡(jiǎn)化了我們的開發(fā)過程。 但是,它們還給表帶來了一些其他問題,N + 1是其中之一。 在本文中,我們將簡(jiǎn)要介紹該問題以及避免這些問題的一些方法。
問題
作為示例,我將使用在線圖書訂購應(yīng)用程序的簡(jiǎn)化版本。 在這樣的應(yīng)用程序中,我可能會(huì)創(chuàng)建一個(gè)如下所示的實(shí)體來代表采購訂單–
@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和要購買的一個(gè)或多個(gè)項(xiàng)目組成。 PurchaseOrderItem實(shí)體可能具有以下結(jié)構(gòu)–
@Entity public class PurchaseOrderItem {@Idprivate String id;private String bookId; }這些實(shí)體已經(jīng)簡(jiǎn)化了很多,但是出于本文的目的,這是可以做到的。
現(xiàn)在假設(shè)我們需要找到一個(gè)客戶的訂單以在他們的采購訂單歷史記錄中顯示它們。 以下查詢將用于此目的–
SELECTP FROMPurchaseOrder P WHEREP.customerId = :customerId轉(zhuǎn)換為SQL后,其外觀如下所示–
selectpurchaseor0_.id as id1_1_,purchaseor0_.customer_id as customer2_1_ frompurchase_order purchaseor0_ wherepurchaseor0_.customer_id = ?此查詢將返回客戶擁有的所有采購訂單。 但是,為了獲取訂單項(xiàng),JPA將為每個(gè)單獨(dú)的訂單發(fā)出單獨(dú)的查詢。 例如,如果某個(gè)客戶有5個(gè)訂單,那么JPA將發(fā)出5個(gè)附加查詢以獲取這些訂單中包含的訂單項(xiàng)。 這基本上被稱為N + 1問題-1個(gè)查詢以獲取所有N個(gè)采購訂單,N個(gè)查詢以獲取所有訂單商品。
當(dāng)我們的數(shù)據(jù)增長(zhǎng)時(shí),此行為為我們帶來了可伸縮性問題。 即使適量的訂單和項(xiàng)目也會(huì)造成嚴(yán)重的性能問題。
解決方案
避免熱切獲取
這是問題背后的主要原因。 我們應(yīng)該擺脫從映射中獲取的所有渴望。 它們幾乎沒有任何好處可證明其可用于生產(chǎn)級(jí)應(yīng)用程序。 我們應(yīng)該將所有關(guān)系標(biāo)記為“懶惰”。
需要注意的重要一點(diǎn)–將關(guān)系映射標(biāo)記為“惰性”并不保證基礎(chǔ)持久性提供程序也將其同樣對(duì)待。 JPA規(guī)范不保證延遲獲取。 充其量對(duì)持久提供者來說是一個(gè)提示。 但是,考慮到Hibernate,我從未見過這樣做。
僅獲取實(shí)際需要的數(shù)據(jù)
始終建議使用此方法,而不考慮是否要進(jìn)行急切/懶惰的訪存。
我記得我進(jìn)行過一次N + 1優(yōu)化,將REST端點(diǎn)的最大響應(yīng)時(shí)間從17分鐘提高到1.5秒 。 端點(diǎn)根據(jù)某些條件獲取單個(gè)實(shí)體,對(duì)于我們當(dāng)前的示例,該實(shí)體將遵循以下原則:
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是結(jié)果中唯一用于后續(xù)計(jì)算的數(shù)據(jù)。
有幾個(gè)客戶有上千個(gè)訂單。 每個(gè)命令依次又有數(shù)千個(gè)其他幾種不同類型的子級(jí)。 不用說,每當(dāng)在此端點(diǎn)接收到針對(duì)這些訂單的請(qǐng)求時(shí),數(shù)據(jù)庫中就會(huì)執(zhí)行數(shù)千個(gè)查詢。
為了提高性能,我所做的就是-
只是此更改導(dǎo)致680倍的改進(jìn) 。
如果我們要獲取多個(gè)屬性,則可以利用JPA提供的Constructor表達(dá)式–
使用構(gòu)造函數(shù)表達(dá)式的一些注意事項(xiàng)–
使用聯(lián)接提取/實(shí)體圖
每當(dāng)我們需要同時(shí)獲取帶有所有子元素的實(shí)體時(shí),就可以在查詢中使用JOIN FETCH 。 這樣可以減少數(shù)據(jù)庫流量,從而提高性能。
JPA 2.1規(guī)范引入了實(shí)體圖,它使我們能夠創(chuàng)建靜態(tài)/動(dòng)態(tài)查詢負(fù)載計(jì)劃。
Thorben Janssen ( 這里和這里 )寫了幾篇文章,詳細(xì)介紹了它們的用法,值得一看。
這篇文章的一些示例代碼可以在Github上找到。
翻譯自: https://www.javacodegeeks.com/2018/04/jpa-tips-avoiding-the-n-1-select-problem.html
jpa避免n+1
總結(jié)
以上是生活随笔為你收集整理的jpa避免n+1_JPA技巧:避免N + 1选择问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .cs文件怎么生成dll文件(dll文件
- 下一篇: 海域动态监视监测管理系统_监视和管理备份