eager_EAGER的获取是代码的味道
eager
介紹
Hibernate獲取策略確實(shí)可以使幾乎沒(méi)有爬網(wǎng)的應(yīng)用程序和響應(yīng)Swift的應(yīng)用程序有所不同。 在這篇文章中,我將解釋為什么您應(yīng)該選擇基于查詢的獲取而不是全局獲取計(jì)劃。
取得101
Hibernate定義了四種關(guān)聯(lián)檢索策略 :
| 加入 | 原始SELECT語(yǔ)句中的關(guān)聯(lián)是OUTER JOINED |
| 選擇 | 附加的SELECT語(yǔ)句用于檢索關(guān)聯(lián)的實(shí)體(實(shí)體) |
| 子選擇 | 附加的SELECT語(yǔ)句用于檢索整個(gè)關(guān)聯(lián)的集合。 此模式適用于多個(gè)關(guān)聯(lián) |
| 批量 | 其他數(shù)量的SELECT語(yǔ)句用于檢索整個(gè)關(guān)聯(lián)的集合。 每個(gè)其他的SELECT都會(huì)檢索固定數(shù)量的關(guān)聯(lián)實(shí)體。 此模式適用于多個(gè)關(guān)聯(lián) |
這些獲取策略可能適用于以下情況:
- 關(guān)聯(lián)總是與其所有者一起初始化(例如EAGER FetchType)
- 導(dǎo)航未初始化的關(guān)聯(lián)(例如LAZY FetchType),因此必須使用輔助SELECT檢索該關(guān)聯(lián)
Hibernate映射獲取信息形成了全局獲取計(jì)劃 。 在查詢時(shí),我們可以覆蓋全局獲取計(jì)劃,但僅適用于LAZY關(guān)聯(lián) 。 為此,我們可以使用訪存HQL / JPQL / Criteria指令。 EAGER關(guān)聯(lián)不能被覆蓋,因此將您的應(yīng)用程序與全局獲取計(jì)劃綁定在一起。
Hibernate 3承認(rèn)LAZY應(yīng)該是默認(rèn)的關(guān)聯(lián)獲取策略:
默認(rèn)情況下,Hibernate3對(duì)集合使用延遲選擇獲取,對(duì)單值關(guān)聯(lián)使用延遲代理獲取。 對(duì)于大多數(shù)應(yīng)用程序中的大多數(shù)關(guān)聯(lián)而言,這些默認(rèn)設(shè)置有意義。
在注意到與Hibernate 2默認(rèn)渴望獲取有關(guān)的許多性能問(wèn)題后,做出了此決定。 不幸的是,JPA采取了不同的方法,并決定對(duì)許多關(guān)聯(lián)為L(zhǎng)AZY,而渴望獲得一對(duì)一的關(guān)系。
| @OneTMany | 懶 |
| @多多多 | 懶 |
| @多多 | 急于 |
| @OneToOne | 急于 |
EAGER獲取不一致
盡管將關(guān)聯(lián)標(biāo)記為EAGER(將獲取職責(zé)委托給Hibernate)可能很方便,但還是建議使用基于查詢的獲取計(jì)劃。
始終會(huì)獲取EAGER關(guān)聯(lián),并且獲取策略在所有查詢技術(shù)之間均不一致。
接下來(lái),我將演示EAGER的獲取對(duì)于所有Hibernate查詢變量的行為。 我將重用我先前在獲取策略文章中介紹的實(shí)體模型:
產(chǎn)品實(shí)體具有以下關(guān)聯(lián):
@ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "company_id", nullable = false) private Company company;@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "product", optional = false) private WarehouseProductInfo warehouseProductInfo;@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "importer_id") private Importer importer;@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true) @OrderBy("index") private Set<Image> images = new LinkedHashSet<Image>();公司協(xié)會(huì)被標(biāo)記為EAGER,并且Hibernate將始終采用獲取策略來(lái)對(duì)其及其所有者實(shí)體進(jìn)行初始化。
持久性上下文加載
首先,我們將使用Persistence Context API加載實(shí)體:
Product product = entityManager.find(Product.class, productId);它將生成以下SQL SELECT語(yǔ)句:
Query:{[ select product0_.id as id1_18_1_, product0_.code as code2_18_1_, product0_.company_id as company_6_18_1_, product0_.importer_id as importer7_18_1_, product0_.name as name3_18_1_, product0_.quantity as quantity4_18_1_, product0_.version as version5_18_1_, company1_.id as id1_6_0_, company1_.name as name2_6_0_ from Product product0_ inner join Company company1_ on product0_.company_id=company1_.id where product0_.id=?][1]使用內(nèi)部聯(lián)接檢索了EAGER公司關(guān)聯(lián)。 對(duì)于M個(gè)這樣的關(guān)聯(lián),所有者實(shí)體表將被連接M次。
每個(gè)額外的聯(lián)接加起來(lái)將增加總體查詢復(fù)雜度和執(zhí)行時(shí)間。 如果我們甚至在所有可能的業(yè)務(wù)場(chǎng)景中都沒(méi)有使用所有這些關(guān)聯(lián),那么我們只是付出了額外的性能損失,卻沒(méi)有得到任何回報(bào)。
使用JPQL和條件進(jìn)行獲取
Product product = entityManager.createQuery("select p " +"from Product p " +"where p.id = :productId", Product.class).setParameter("productId", productId).getSingleResult();或搭配
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Product> cq = cb.createQuery(Product.class); Root<Product> productRoot = cq.from(Product.class); cq.where(cb.equal(productRoot.get("id"), productId)); Product product = entityManager.createQuery(cq).getSingleResult();生成以下SQL SELECT語(yǔ)句:
Query:{[ select product0_.id as id1_18_, product0_.code as code2_18_, product0_.company_id as company_6_18_, product0_.importer_id as importer7_18_, product0_.name as name3_18_, product0_.quantity as quantity4_18_, product0_.version as version5_18_ from Product product0_ where product0_.id=?][1]} Query:{[ select company0_.id as id1_6_0_, company0_.name as name2_6_0_ from Company company0_ where company0_.id=?][1]}JPQL和Criteria查詢都默認(rèn)選擇獲取,因此為每個(gè)單獨(dú)的EAGER關(guān)聯(lián)發(fā)布輔助選擇。 關(guān)聯(lián)數(shù)越大,單個(gè)SELECTS越多,對(duì)我們應(yīng)用程序性能的影響就越大。
Hibernate標(biāo)準(zhǔn)API
JPA 2.0添加了對(duì)Criteria查詢的支持,而Hibernate長(zhǎng)期以來(lái)一直提供特定的動(dòng)態(tài)查詢實(shí)現(xiàn) 。
如果EntityManager實(shí)現(xiàn)委托方法調(diào)用舊版Session API,則JPA Criteria實(shí)現(xiàn)是從頭開(kāi)始編寫(xiě)的。 這就是為什么Hibernate和JPA Criteria API在類似的查詢方案中表現(xiàn)不同的原因。
前面的示例Hibernate Criteria等效項(xiàng)如下所示:
Product product = (Product) session.createCriteria(Product.class).add(Restrictions.eq("id", productId)).uniqueResult();關(guān)聯(lián)SQL SELECT是:
Query:{[ select this_.id as id1_3_1_, this_.code as code2_3_1_, this_.company_id as company_6_3_1_, this_.importer_id as importer7_3_1_, this_.name as name3_3_1_, this_.quantity as quantity4_3_1_, this_.version as version5_3_1_, hibernatea2_.id as id1_0_0_, hibernatea2_.name as name2_0_0_ from Product this_ inner join Company hibernatea2_ on this_.company_id=hibernatea2_.id where this_.id=?][1]}此查詢使用連接抓取策略,而不是選擇抓取,通過(guò)JPQL / HQL和標(biāo)準(zhǔn)的API使用。
Hibernate條件和多個(gè)EAGER集合
讓我們看看將圖像收集獲取策略設(shè)置為EAGER時(shí)會(huì)發(fā)生什么:
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "product", orphanRemoval = true) @OrderBy("index") private Set<Image> images = new LinkedHashSet<Image>();將生成以下SQL:
Query:{[ select this_.id as id1_3_2_, this_.code as code2_3_2_, this_.company_id as company_6_3_2_, this_.importer_id as importer7_3_2_, this_.name as name3_3_2_, this_.quantity as quantity4_3_2_, this_.version as version5_3_2_, hibernatea2_.id as id1_0_0_, hibernatea2_.name as name2_0_0_, images3_.product_id as product_4_3_4_, images3_.id as id1_1_4_, images3_.id as id1_1_1_, images3_.index as index2_1_1_, images3_.name as name3_1_1_, images3_.product_id as product_4_1_1_ from Product this_ inner join Company hibernatea2_ on this_.company_id=hibernatea2_.id left outer join Image images3_ on this_.id=images3_.product_id where this_.id=? order by images3_.index][1]}Hibernate條件不會(huì)自動(dòng)將父實(shí)體列表分組。 由于存在一對(duì)多子表JOIN,因此對(duì)于每個(gè)子實(shí)體,我們將獲得一個(gè)新的父實(shí)體對(duì)象引用(在我們當(dāng)前的持久性上下文中,它們均指向同一對(duì)象):
product.setName("TV"); product.setCompany(company);Image frontImage = new Image(); frontImage.setName("front image"); frontImage.setIndex(0);Image sideImage = new Image(); sideImage.setName("side image"); sideImage.setIndex(1);product.addImage(frontImage); product.addImage(sideImage);List products = session.createCriteria(Product.class).add(Restrictions.eq("id", productId)).list(); assertEquals(2, products.size()); assertSame(products.get(0), products.get(1));因?yàn)槲覀冇袃蓚€(gè)圖像實(shí)體,所以我們將獲得兩個(gè)Product實(shí)體引用,它們均指向同一一級(jí)緩存條目。
要解決此問(wèn)題,我們需要指示Hibernate標(biāo)準(zhǔn)使用不同的根實(shí)體:
List products = session.createCriteria(Product.class).add(Restrictions.eq("id", productId)).setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY).list(); assertEquals(1, products.size());結(jié)論
EAGER的獲取策略是一種代碼味道。 通常,它是出于簡(jiǎn)化的目的而使用的,而沒(méi)有考慮長(zhǎng)期的性能損失。 提取策略絕不應(yīng)成為實(shí)體映射的責(zé)任。 每個(gè)業(yè)務(wù)用例具有不同的實(shí)體負(fù)載要求,因此,應(yīng)將獲取策略委托給每個(gè)單獨(dú)的查詢。
全局獲取計(jì)劃應(yīng)僅定義LAZY關(guān)聯(lián),這些關(guān)聯(lián)是在每個(gè)查詢的基礎(chǔ)上獲取的。 結(jié)合始終檢查生成的查詢策略 ,基于查詢的獲取計(jì)劃可以提高應(yīng)用程序性能并降低維護(hù)成本。
- Hibernate和JPA可用的代碼。
翻譯自: https://www.javacodegeeks.com/2014/12/eager-fetching-is-a-code-smell.html
eager
總結(jié)
以上是生活随笔為你收集整理的eager_EAGER的获取是代码的味道的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 关闭ie安全设置提示(关闭ie的安全警告
- 下一篇: 框架中建立浮动框架_建立代理,而不是框架