eager

介绍

Hibernate获取策略确实可以使几乎没有爬网的应用程序和响应Swift的应用程序有所不同。 在这篇文章中,我将解释为什么您应该选择基于查询的获取而不是全局获取计划。

取得101

Hibernate定义了四种关联检索策略 :

提取策略 描述
加入 原始SELECT语句中的关联是OUTER JOINED
选择 附加的SELECT语句用于检索关联的实体(实体)
子选择 附加的SELECT语句用于检索整个关联的集合。 此模式适用于多个关联
批量 其他数量的SELECT语句用于检索整个关联的集合。 每个其他的SELECT都会检索固定数量的关联实体。 此模式适用于多个关联

这些获取策略可能适用于以下情况:

  • 关联总是与其所有者一起初始化(例如EAGER FetchType)
  • 导航未初始化的关联(例如LAZY FetchType),因此必须使用辅助SELECT检索该关联

Hibernate映射获取信息形成了全局获取计划 。 在查询时,我们可以覆盖全局获取计划,但仅适用于LAZY关联 。 为此,我们可以使用访存HQL / JPQL / Criteria指令。 EAGER关联不能被覆盖,因此将您的应用程序与全局获取计划绑定在一起。

Hibernate 3承认LAZY应该是默认的关联获取策略:

默认情况下,Hibernate3对集合使用延迟选择获取,对单值关联使用延迟代理获取。 对于大多数应用程序中的大多数关联而言,这些默认设置有意义。

在注意到与Hibernate 2默认渴望获取有关的许多性能问题后,做出了此决定。 不幸的是,JPA采取了不同的方法,并决定对许多关联为LAZY,而渴望获得一对一的关系。

关联类型 默认提取策略
@OneTMany
@多多多
@多多 急于
@OneToOne 急于

EAGER获取不一致

尽管将关联标记为EAGER(将获取职责委托给Hibernate)可能很方便,但还是建议使用基于查询的获取计划。

始终会获取EAGER关联,并且获取策略在所有查询技术之间均不一致。

接下来,我将演示EAGER的获取对于所有Hibernate查询变量的行为。 我将重用我先前在获取策略文章中介绍的实体模型:

产品实体具有以下关联:

@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>();

公司协会被标记为EAGER,并且Hibernate将始终采用获取策略来对其及其所有者实体进行初始化。

持久性上下文加载

首先,我们将使用Persistence Context API加载实体:

Product product = entityManager.find(Product.class, productId);

它将生成以下SQL SELECT语句:

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]

使用内部联接检索了EAGER公司关联。 对于M个这样的关联,所有者实体表将被连接M次。

每个额外的联接加起来将增加总体查询复杂度和执行时间。 如果我们甚至在所有可能的业务场景中都没有使用所有这些关联,那么我们只是付出了额外的性能损失,却没有得到任何回报。

使用JPQL和条件进行获取

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语句:

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查询都默认选择获取,因此为每个单独的EAGER关联发布辅助选择。 关联数越大,单个SELECTS越多,对我们应用程序性能的影响就越大。

Hibernate标准API

JPA 2.0添加了对Criteria查询的支持,而Hibernate长期以来一直提供特定的动态查询实现 。

如果EntityManager实现委托方法调用旧版Session API,则JPA Criteria实现是从头开始编写的。 这就是为什么Hibernate和JPA Criteria API在类似的查询方案中表现不同的原因。

前面的示例Hibernate Criteria等效项如下所示:

Product product = (Product) session.createCriteria(Product.class).add(Restrictions.eq("id", productId)).uniqueResult();

关联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]}

此查询使用连接抓取策略,而不是选择抓取,通过JPQL / HQL和标准的API使用。

Hibernate条件和多个EAGER集合

让我们看看将图像收集获取策略设置为EAGER时会发生什么:

@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条件不会自动将父实体列表分组。 由于存在一对多子表JOIN,因此对于每个子实体,我们将获得一个新的父实体对象引用(在我们当前的持久性上下文中,它们均指向同一对象):

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));

因为我们有两个图像实体,所以我们将获得两个Product实体引用,它们均指向同一一级缓存条目。

要解决此问题,我们需要指示Hibernate标准使用不同的根实体:

List products = session.createCriteria(Product.class).add(Restrictions.eq("id", productId)).setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY).list();
assertEquals(1, products.size());

结论

EAGER的获取策略是一种代码味道。 通常,它是出于简化的目的而使用的,而没有考虑长期的性能损失。 提取策略绝不应成为实体映射的责任。 每个业务用例具有不同的实体负载要求,因此,应将获取策略委托给每个单独的查询。

全局获取计划应仅定义LAZY关联,这些关联是在每个查询的基础上获取的。 结合始终检查生成的查询策略 ,基于查询的获取计划可以提高应用程序性能并降低维护成本。

  • Hibernate和JPA可用的代码。

翻译自: https://www.javacodegeeks.com/2014/12/eager-fetching-is-a-code-smell.html

eager

eager_EAGER的获取是代码的味道相关推荐

  1. EAGER的获取是代码的味道

    介绍 休眠获取策略确实可以使几乎没有爬网的应用程序和响应速度很快的应用程序有所不同. 在这篇文章中,我将解释为什么您应该选择基于查询的获取而不是全局获取计划. 取得101 Hibernate定义了四种 ...

  2. 25种代码坏味道总结+优化示例

    前言 什么样的代码是好代码呢?好的代码应该命名规范.可读性强.扩展性强.健壮性......而不好的代码又有哪些典型特征呢?这25种代码坏味道大家要注意啦 1. Duplicated Code (重复代 ...

  3. 代码坏味道之非必要的

    :notebook: 本文已归档到:「blog」 翻译自:https://sourcemaking.com/refactoring/smells/dispensables 非必要的(Dispensab ...

  4. 25 种代码坏味道总结+优化示例

    作者 | 捡田螺的小男孩       责编 | 欧阳姝黎 前言 什么样的代码是好代码呢?好的代码应该命名规范.可读性强.扩展性强.健壮性......而不好的代码又有哪些典型特征呢? Duplicate ...

  5. 讲点武德!避免这些代码坏味道,努力做一名优秀的程序员

    Martin Fowler:任何一个傻瓜都能写出计算机可以理解的代码.唯有写出人类容易理解的代码,才是优秀的程序员. 大家闭着眼睛想一下什么是好代码?也许你的脑海中漂浮着一堆词:干净.整洁.命名规范. ...

  6. js获取html代码中所有图片地址

    /** * JS获取html代码中所有的图片地址 * @param htmlstr * @returns imgsrcArr 数组 */ function getimgsrc(htmlstr) { v ...

  7. [PHP] debug_backtrace()可以获取到代码的调用路径追踪

    查看代码的时候,看到有使用这个函数,测试一下 1.debug_backtrace()可以获取到代码的调用追踪,以数组形式返回 2.debug_print_backtrace() - 打印一条回溯,直接 ...

  8. java滥用接口_吐槽一下项目中的代码坏味道:滥用java常量

    我们的项目中是否充斥着类似以下的代码呢?定义一个专门存放常量的java类(接口),非常多其它类依赖该常量类. public interface IConstant { int ZERO = 0; St ...

  9. C#获取动态代码的值

    通过字符串对控件属性赋值 例如:"BackColor=Color.FromArgb(100,100,100);BackGroundp_w_picpath=Image.FromFile(\&q ...

最新文章

  1. linux c 内存操作函数 简介
  2. 7个示例科普CPU Cache(转)
  3. 机器学习火热,SQL 开发人员有何用?
  4. 她说:程序员离开电脑就是 “废物” !
  5. 毕啸南专栏 | 对话旷视CEO印奇:AI产业2018年将迎来转折
  6. android 截屏 分享,Android应用内截图分享的实现记录
  7. sql in语句优化_优化SQL语句的一般步骤
  8. 用sox查看wav声音的基本信息
  9. 盘点40种常用的芯片封装技术
  10. 6678与FPGA PCIE调试
  11. 宝宝生活点滴(12.3)
  12. 使用python爬取google翻译的语音
  13. 微软 android启动器,微软启动器Mirosoft Launcher
  14. 非常漂亮的放焰火效果的Applet程序
  15. Pipeline流水线项目构建
  16. 【巴什博弈】HDOJ2188悼念512汶川大地震遇难同胞——选拔志愿者
  17. 程序员成长之旅——C语言三子棋
  18. 计算机高级语言c高起专阶段性作业1,重庆大学网络教育高起专计算机应用基础入学测试模拟题及答案2...
  19. 【Java_SSM_kuang】
  20. 虚拟机环境搭建: virtualBox6.1+window10

热门文章

  1. P2414-[NOI2011]阿狸的打字机【AC自动机,树状数组】
  2. jzoj5354-导弹拦截【dp,最大匹配,最少路径覆盖】
  3. P2055-假期的宿舍【网络流,最大流,最大匹配】
  4. ssl2346-联络员【图论,最小生成树】
  5. 【manacher】双倍回文(金牌导航 manacher-2/luogu 4287)
  6. NCPC2018 D.Delivery Delays[二分答案+DP check]
  7. mybatis源码阅读(七) ---ResultSetHandler了解一下
  8. 汇编语言(三十三)之四进制转十进制
  9. Linux查找含有某字符串的所有文件
  10. 选择大公司还是小公司