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