初始化懒惰关系以及何时使用它们的5种方法
實體之間關系的延遲加載是JPA中公認的最佳實踐。 它的主要目標是僅從數據庫中檢索請求的實體,并僅在需要時加載相關實體。 如果我們只需要請求的實體,那是一個很好的方法。 但是,如果我們還需要一些相關實體,它會增加工作量,并可能導致性能問題。
讓我們看一下觸發初始化的不同方法及其特定的優點和缺點。
1.在映射關系上調用方法
讓我們從最顯而易見的方法開始,不幸的是也從效率最低的方法開始。 我們在EntityManager上使用find方法,并在關系上調用一個方法。
Order order = this.em.find(Order.class, orderId); order.getItems().size();此代碼工作得很好,易于閱讀并且經常使用。 那么,這是什么問題呢?
好吧,您可能知道。 此代碼執行附加查詢以初始化關系。 這聽起來不像是一個真正的問題,但是可以讓我們計算出一個更加真實的場景中執行的查詢的數量。
假設我們有一個具有5個關系的實體,需要初始化。 因此,我們將獲得1 + 5 = 6個查詢 。 好的,那是5個附加查詢。 這似乎仍然不是一個大問題。
但是我們的應用程序將被多個用戶并行使用(我希望)。 假設我們的系統必須為100個并行用戶提供服務器。 然后,我們將獲得100 + 5 * 100 = 600個查詢 。
好的,很明顯,這種方法提供了可行的解決方案,但不是一個好的解決方案。 或早或晚,額外執行的查詢數量將使我們的應用程序變慢。 因此,我們應該嘗試避免這種方法,并看看其他一些選擇。
2.在JPQL中獲取Join
初始化惰性關系的一個更好的選擇是將JPQL查詢與獲取聯接一起使用。
Query q = this.em.createQuery("SELECT o FROM Order o JOIN FETCH o.items i WHERE o.id = :id"); q.setParameter("id", orderId); newOrder = (Order) q.getSingleResult();這告訴實體管理器在同一查詢中獲取選定的實體和關系。 這種方法的優缺點很明顯:
優點是所有內容都在一個查詢中獲取。 從性能的角度來看,這比第一種方法要好得多。
主要缺點是我們需要編寫其他代碼來執行查詢。 但是,如果實體具有多個關系,并且我們需要針對不同的用例初始化不同的關系,那就更糟了。 在這種情況下,我們需要為獲取聯接關系的每個所需組合編寫查詢。 這會變得很混亂。
在JPQL語句中使用提取聯接可能需要大量查詢,這將使維護代碼庫變得困難。 因此,在開始編寫大量查詢之前,我們應該考慮可能需要的不同訪存聯接組合的數量。 如果數量很少,那么這是一種限制執行的查詢數量的好方法。
3.獲取條件API中的加入
好的,這種方法與以前的方法基本相同。 但是這次我們使用的是Criteria API,而不是JPQL查詢。
CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery q = cb.createQuery(Order.class); Root o = q.from(Order.class); o.fetch("items", JoinType.INNER); q.select(o); q.where(cb.equal(o.get("id"), orderId));Order order = (Order)this.em.createQuery(q).getSingleResult();優點和缺點與帶有訪存連接的JPQL查詢相同。 使用一個查詢從數據庫中檢索實體和關系,我們需要每種關系組合的特定代碼。 但是,如果我們使用的是Criteria API,我們通常已經有很多用例特定的查詢代碼。 因此,這可能不是一個大問題。
如果我們已經在使用Criteria API來構建查詢,那么這是減少執行查詢數量的好方法。
4.命名實體圖
命名實體圖是JPA 2.1的新功能。 它可用于定義應從數據庫中查詢的實體圖。 實體圖的定義是通過注釋完成的,并且與查詢無關。
如果您不熟悉此功能,則可以查看我以前的一篇博客文章 ,其中對它進行了更詳細的介紹。
@Entity @NamedEntityGraph(name = "graph.Order.items", attributeNodes = @NamedAttributeNode("items")) public class Order implements Serializable { ....然后,EntityManager的find方法可以使用命名的實體圖。
EntityGraph graph = this.em.getEntityGraph("graph.Order.items");Map hints = new HashMap(); hints.put("javax.persistence.fetchgraph", graph);Order order = this.em.find(Order.class, orderId, hints);這基本上是我們第一種方法的改進版本。 實體管理器將通過一個查詢從數據庫檢索定義的實體圖。 唯一的缺點是,我們需要為將在一個查詢中檢索到的每種關系組合注釋一個命名實體圖。 與第二種方法一樣,我們將需要更少的附加注釋,但是它仍然會變得非常混亂。
因此,如果我們只需要定義有限數量的實體圖并將其重用于不同的用例,則命名實體圖是一個很好的解決方案。 否則,代碼將變得難以維護。
5.動態實體圖
動態實體圖類似于命名實體圖,并且在以前的一篇文章中也進行了解釋。 唯一的區別是,實體圖是通過Java API定義的。
EntityGraph graph = this.em.createEntityGraph(Order.class); Subgraph itemGraph = graph.addSubgraph("items");Map hints = new HashMap(); hints.put("javax.persistence.loadgraph", graph);Order order = this.em.find(Order.class, orderId, hints);通過API進行定義既可以是優點,也可以是缺點。 如果我們需要大量用例特定的實體圖,則最好在特定的Java代碼中定義實體圖,并且不向該實體添加附加注釋。 這樣可以避免帶有數十個注釋的實體。 另一方面,動態實體圖需要??更多代碼和其他方法才能重用。
因此,我建議使用動態實體圖,如果我們需要定義用例特定的圖,則將不會重復使用該圖。 如果我們想重用實體圖,則更容易注釋命名的實體圖。
結論
我們研究了5種不同的初始化惰性關系的方法。 正如我們所看到的,它們每個都有其優點和缺點。 那么從這篇文章中要記住什么呢?
- 通過在映射關系上調用方法來初始化惰性關系會導致附加查詢。 出于性能原因,應避免這種情況。
- JPQL語句中的訪存聯接將查詢數量減少到一個,但是我們可能需要很多不同的查詢。
- Criteria API還支持提取連接,對于每種需要初始化的關系,我們都需要特定的代碼。
- 如果我們將在代碼中重用已定義的圖,則命名實體圖是一個很好的解決方案。
- 如果我們需要定義特定于用例的圖,則動態實體圖可能是更好的解決方案。
翻譯自: https://www.javacodegeeks.com/2014/12/5-ways-to-initialize-lazy-relations-and-when-to-use-them.html
總結
以上是生活随笔為你收集整理的初始化懒惰关系以及何时使用它们的5种方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EE Servlet 3:使用会话和过滤
- 下一篇: 理想汽车:暂无进军自动驾驶卡车领域计划,