2012-11-15 38 views
5

我有JPA實體與客戶的ManyToOne關係的訂單。它是雙向的,所以客戶也有OneToMany現場訂單。這兩種關係都使用EAGER抓取(或者在OpenJPA fetchplan中)。阻止JPA中的N + 1選擇

當我從訂單中選擇時,我得到1選擇訂單和N選擇Customer.orders字段。令我驚訝的是,即使當我使用JOIN FETCH(它可以在單向情況下工作)時,OpenJPA,EclipseLink和Hibernate也會出現這個問題。

有沒有解決這個問題的好方法? 有沒有解決方案來解決更復雜圖形的N + 1選擇問題?

編輯:我自己的研究結果: - 對於OpenJPA的(我使用的),我不知道解決辦法還沒有 - 對於休眠@Fetch(FetchMode.SUBSELECT)解決了這個問題。使用@BatchSize也有幫助,它同時選擇給定數量的customer.orders字段。 - 對於EclipseLink,我發現了一個類似的特性@BatchFetch(value = BatchFetchType.IN),但在這種情況下它並沒有幫助,我想它無法在雙向關係中有效地處理這個問題。

+0

這是一個棘手的問題。您是否有*想要幫助的特定問題?或者您只是想抱怨JPA? – millimoose

+0

你是對的,我對JPA感到有些沮喪,我會編輯我的問題,以達到更高的要求。 –

+1

您是否真的需要EAGER獲取Customer.orders? – Esteve

回答

-1

在任何ORM框架中,N + 1問題很常見。你無法避免這種情況。但是,這更多的是你採取什麼樣的方法來解決這個問題。您可以根據您的實施使用關聯和延遲加載或急切加載日期。您還可以執行映射數據庫並在單個查詢中獲取所有關聯數據,並將其映射到您的模型。由於數據庫已建立索引,因此此操作可能比您使用N + 1個查詢獲取數據和映射(如果您的網絡延遲允許)更快。

1

看看:What is SELECT N+1?因爲那裏有很多很好的信息。

如果使用Hibernate:Hibernate - Chapter 19: Improving Performance - Fetching Strategies

My own personal solution is to use native SQL and tmp ids table,這是因爲通常的IMHO N + 1選擇問題主要與批量處理的問題。否則,延遲加載(通常是N + 1解決方案)可能對性能有益。

+1

謝謝,使用本機查詢肯定會有所幫助。但是,這感覺就像你必須手寫一些應該是ORM執行責任的東西。這可能會導致大量的手動工作和維修噩夢。 –

+0

@SlowStrider這個想法是你不應該經常這樣做。 N + 1延遲加載通常是解決大多數問題的正確方法。除此之外,當涉及到像這樣的性能可靠優化時,您會發現SQL(不是JPA HQL)是您唯一的選擇......所以除非必須,否則不要這樣做。 –

+0

對於選民來說,如果你能告訴我爲什麼,它會有所幫助。 N + 1選擇問題不能神奇修復。你要麼有笛卡爾積,要麼懶惰加載每個對象。我不確定人們期待什麼。沒有一個該死的ORM可以預測數據庫性能,並隨機決定何時做渴望(笛卡爾乘積)與懶惰(N + 1)。我不確定人們在期待什麼。對不起,我不能只提供一段代碼來使問題消失。 –

0

這裏是一個解決方案:

  1. 單獨從API層實體層,並用單獨在你的應用的API實例進行交互。在這方面的API也可以被稱爲DTO。

  2. 完全從實體中刪除關係。

  3. 創建一種機制來表明您希望獲取孩子。例如:將fetchRequestList傳播到將API映射到實體的層(可以有條件地獲取)。

  4. 在查詢執行過程中,像平常一樣收集父對象。

  5. 使用帶IN子句的命名參數化查詢檢索整個子集合,子查詢基於FK到父親PK。

  6. 循環瀏覽結果並將其與父母進行匹配。

這將強制ORM執行n + 1個查詢而不是n(n + 1)個查詢。請記住,您現在必須使用自定義邏輯來執行級聯保存,刪除,更新等操作。