jpa获取session_JPA 2 | 获取联接以及我们是否应该使用它们
jpa獲取session
介紹
最近,我一直在與JPA 2中的FETCH JOINS一起使用,以期從數據庫中急切地獲取數據,并且我學到了很多關于為什么在日常操作中應避免使用Fetch Joins的知識。
今天的博客文章談論了我在Fetch上的經歷和學習(主要基于當我在查詢中有很多FETCH JOINS時獲得的評論)。
問題陳述
在我們的項目中,有一種情況是從定義了許多集合值關聯的數據庫(OneToMany,ManyToMany,也稱為ToMany關聯)中獲取實體。 這是實體外觀的概念圖(為清楚起見,省略了getter和setter)。 這是實體的極其簡化的示例。 在實際情況下,我們大約有11個關聯。
public class ItemRecord {@Idprivate String itemId;@Column(name="author")private String author;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private List costs;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private List notes;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private List stats;}關于上述實體,有幾件事需要注意:
- 它具有3個收藏珍貴的協會。
- 所有這些關聯都被延遲獲取,因為JPA中“集合有價值的關聯”的默認獲取策略是“惰性”。
在我們的業務實現中,我們有一個轉換器,該轉換器將DAO層返回的值轉換為Business DTO。
因此,我們的業務方法的算法如下:
@TransactionAttribute public List searchItemRecords (SearchCriteria sc) {List ir = itemRecordDao.search(sc);List convertedData = recordConverter.convert(ir);return convertedData; }請注意,整個方法都在Transaction內部運行。
每當我們從數據庫中獲取數據時,都不會急切地獲取與成本,統計信息等相關的數據。 但是我們的ItemInformation DTO期望所有數據。 因此,當首次調用getCosts或getStatistics時,持久性提供程序(在本例中為Hibernate)向數據庫激發查詢以獲取指定的數據。 這為我們創建了N + 1個選擇查詢問題。 如果您不熟悉N + 1選擇或需要刷新,可以在DZone上查看此文章 。
解
我們大多數人,包括我在內,都會選擇N + 1選擇問題的最快,最簡單的解決方案,即使用Fetch Join。 在Internet上發布的不同博客/文章中也有很多建議。
因此,我也采用了相同的方法。 就我而言,這至少是一種糟糕的方法。
首先讓我們看看如何使用FETCH JOIN。
使用Fetch Join之前的查詢如下:
SELECT item FROM ItemRecord item WHERE author=:author;請注意,查詢采用JPA形式。
該查詢未獲取集合值。 結果,在翻譯器中,當我們對每個ItemRecord執行getCosts時,將觸發類似于以下的查詢:
SELECT cost FROM Cost where itemId = :itemId因此,如果我們有3個ItemRecords,則激發到數據庫的SELECT查詢總數為:
- 1用于獲取所有ItemRecords
- 3個用于獲取每個ItemRecord的成本
- 3用于獲取每個ItemRecord的注釋
- 3個用于獲取每個ItemRecord的統計信息
即(3乘3)+ 1
當將其轉換為N乘以M +1時,
哪里,
- N是找到的主要實體記錄的數量
- M是主要實體中Collection值關聯的數量
- 1個查詢,用于獲取所有主要實體
在實際情況下,我們有11個關聯。 因此,對于每個主要ItemRecord實體,我們將觸發11個SELECT查詢。 激發的查詢數量乘以找到的每個ItemRecord。
使得集合值關聯成為EAGER是不可行的,因為在實體上運行的許多其他查詢僅需要選定的數據。
這不是最佳解決方案。 必須做一些事情,很多互聯網文章建議使用FETCH JOINS,因為它們最容易實現并解決了(N Time M +1)個查詢問題。
因此,我決定在查詢中使用FETCH Joins來針對給定場景急切地獲取所有數據。
該查詢類似于:
SELECT item FROM ItemRecordJOIN FETCH item.costs, JOIN FETCH item.notes, JOIN FETCHitem.stats;跨欄1
我很快意識到該查詢在我的情況下將無法使用,因為我可以擁有一個與之不相關的統計信息,而在這種情況下,上述查詢不會向我返回那個ItemRecord(因為ORM的工作方式)因此我將獲得不完整的數據。
因此,接下來我轉到了LEFT JOIN FETCH,即使某些關聯關系為空,它也將給我ItemRecord實體返回。 查詢如下所示:
SELECT item FROM ItemRecordLEFT JOIN FETCH item.costs, LEFT JOIN FETCH item.notes, LEFT JOIN FETCH item.stats;跨欄2
當我運行單元測試以測試上述查詢時,出現了異常:
javax.persistence.PersistenceException: org.hibernate.HibernateException: cannot simultaneously fetch multiple bags問題是什么?
問題是我在實體中使用列表作為集合類型。 使用列表會混淆JPA /Hibernate。 這篇文章很好地記錄了這種困惑。
為了解決此問題,我選擇使用Set而不是List,主要是因為它是上述博客文章提供的三種解決方案中最簡單的方法,并且也很有意義(至少在我實現時)。
因此,我將Entity更改為Set而不是List,并且修改后的實體如下所示:
public class ItemRecord {@Idprivate String itemId;@Column(name="author")private String author;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private Set costs;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private Set notes;@OneToMany(cascade = CascadeType.ALL, mappedBy = "item")private Set stats;}我再次運行測試用例,并且測試查詢的測試用例成功。 耶皮 但是,我的另一個測試用例正在測試注釋部分中的條目失敗。 看一下測試用例,我意識到我需要按照特定的順序輸入數據,并且我使用的是HashSet,但沒有順序。 解決方案很簡單。 使用LinkedHashSet維護元素的順序。
使用LinkedHashSet可以解決問題,并且我的測試用例通過了。
我很高興,但我的幸福短暫。
跨欄3
我還有另一個測試用例,對于給定的ItemRecord來說,它需要3個成本對象。 我轉到設置實現后,測試立即開始失敗。 事實證明,我的哈希碼不正確,并且我的“成本實體”的實現等于“實現”,“成本實體”為兩個不同的實體返回相同的哈希碼,結果,由于Set不允許重復值,因此僅持久保留了一個實體。
因此,我接下來要做的就是為我的所有實體使用適當的HashCode和Equals實現。
最終關卡
最終,當我所有的測試用例開始通過時,我進行了代碼審查,并將其發送給團隊。
第一個嚇到我的是我的技術主管。 :)
他只是生氣地看著FETCH JOINS。 原因? 原因是LEFT FETCH JOINs返回所有數據的笛卡爾積。 隨著我們生產中的數據量的增加,甚至無法支持ItemRecord上的多個選擇將成為一場噩夢。 整個問題可以在此博客文章中輕松理解。
因此,我試圖解決性能問題,事實證明我實際上是在制造更大的性能問題。 :)
刪除了轉移到FETCH JOIN的整個解決方案,因此決定進一步研究為什么我們需要UI上的全部數據,以及為什么不能將獲取數據分成較小的專用事務。
摘要:
使用Fetch Joins的整個過程使我很好地了解了Joins的總體工作原理以及使用它們時的期望。
希望您喜歡這篇博客文章。 如果您想繼續閱讀有趣的帖子,可以關注我的博客。
翻譯自: https://www.javacodegeeks.com/2013/07/jpa-2-fetch-joins-and-whether-we-should-use-them.html
jpa獲取session
總結
以上是生活随笔為你收集整理的jpa获取session_JPA 2 | 获取联接以及我们是否应该使用它们的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad重合命令快捷键(cad的重置快捷键
- 下一篇: 裙房是什么意思(裙房和主楼的关系是怎样的