EAGER的获取是代码的味道

介绍

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

取得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查询变量的行为。 我将重用我先前在获取策略文章中介绍的实体模型:

产品2

产品实体具有以下关联:

@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越多,对我们应用程序性能的影响就越大。

休眠标准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使用。

休眠条件和多个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]}

休眠条件不会自动将父实体列表分组。 由于存在一对多子表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实体引用,它们均指向同一一级缓存条目。

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361200.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于S2SH的电子商务网站系统性能优化

对于经常逛网页的人来说最受不了的事情就是访问的网页加载太慢&#xff0c;除去网络的原因网站的系统性能对加载的快慢非常重要&#xff0c; 网上有个统计&#xff1a; 每慢500ms Google访问量降低20% 每慢400ms Yahoo 访问量降低5-9% 每慢100ms Amazon销售额降低1% 对于商务网…

ExtJs的Reader

ExtJs的Reader Reader : 主要用于将proxy数据代理读取的数据按照不同的规则进行解析,讲解析好的数据保存到Modle中 结构图 Ext.data.reader.Reader 读取器的根类 Ext.data.reader.Json JSON格式的读取器 Ext.data.reader.Array 扩展JSON的Array读取器 Ext.data.reader.Xml XML格…

Java Lambdas和低延迟

总览 有关在Java和低延迟中使用Lambda的主要问题是&#xff1a; 它们会产生垃圾吗&#xff0c;您能做些什么吗&#xff1f; 背景 我正在开发一个支持不同有线协议的库。 这样的想法是&#xff0c;您可以描述要写入/读取的数据&#xff0c;并且有线协议确定它是否使用带有JSon或…

Java中的线程本地存储

开发人员中鲜为人知的功能之一是线程本地存储。 这个想法很简单&#xff0c;并且在需要数据的情况下很有用。 如果我们有两个线程&#xff0c;则它们引用相同的全局变量&#xff0c;但我们希望它们具有彼此独立初始化的单独值。 大多数主要的编程语言都有该概念的实现。 例如&…

多个退货单

我曾经听说过&#xff0c;过去人们为使方法具有单个出口点而奋斗。 我知道这是一种过时的方法&#xff0c;从未认为它特别值得注意。 但是最近&#xff0c;我与一些仍坚持该想法的开发人员进行了联系&#xff08;最后一次是在这里 &#xff09;&#xff0c;这让我开始思考。 因…

GO 语言编程 windows 环境搭建

参考 : http://blog.csdn.net/love_se/article/details/7754274 首先是安装Go&#xff0c;这里有很详细的安装说明&#xff0c;http://code.google.com/p/golang-china/wiki/Install 或者http://golang.org/doc/install 下面我们在window下面安装&#xff0c;google有提供win安…

机打发票打印管理

最近公司也从手写发票换成了机打发票&#xff0c;便应财务的要求做了这么一个简单的发票管理及打印系统&#xff0c;程序并不复杂。 使用C#&#xff08;2.0&#xff09; Access&#xff08;97-2003版&#xff09;/WinForm形式 系统菜单中有企业基本信息设置&#xff0c;见图4…

序列化的概念

讨论了为什么Optional不可序列化以及如何处理&#xff08;即将推出&#xff09;之后&#xff0c;让我们仔细看看序列化。 总览 这篇文章介绍了序列化的一些关键概念。 它尝试精简地执行此操作&#xff0c;而不会涉及太多细节&#xff0c;包括将建议降至最低。 它没有叙述&…

Java飞行记录器(JFR)

JFR是Java分析器&#xff0c;它使您可以研究代码的运行时特征。 通常&#xff0c;您将使用探查器来确定代码的哪些部分导致大量内存分配或导致消耗过多的CPU。 有很多产品在那里。 过去&#xff0c;我使用过YourKit&#xff0c;OptimizeIt&#xff0c;JProfiler&#xff0c;Ne…

图像识别SLIC、Haralick texture features(自备)

SLIC 简单线性迭代聚类(SLIC ),它采用k-means聚类方法来有效地生成超像素。 SLIC超像素分割详解&#xff08;一&#xff09;&#xff08;二&#xff09;&#xff08;三&#xff09;_超像素分割 样本-CSDN博客 超像素分割 & SLIC算法 & 使用示例_slic分割算法matlab-C…

浏览器中的JavaFX

浏览器中的JavaFX屏幕截图 最近&#xff0c;Carl Dea和我启动了一个新项目&#xff0c;将JavaFX 8引入浏览器。 今天&#xff0c;我想介绍我们创建的前两个概念验证&#xff0c;以查看该想法是否完全可行。 对于不耐烦的人&#xff0c;这里是到PoC的链接。 但请注意&#xff0…

MDB!= JMS,反之亦然

基本 消息驱动Bean&#xff08;又称为MDB&#xff09;只是另一个EJB&#xff0c;如无状态&#xff0c;有状态或单例。 使用MessageDriven批注指定。 MDB用于异步消息处理 它们与无状态EJB 相似 &#xff0c;因为它们都是由EJB容器池化的 但是&#xff0c;它们与无状态EJB不同…

周三的工作 张孝祖

今天的任务是主面板master嘿嘿 上头有几个按钮&#xff0c;登录登出&#xff0c;个人信息&#xff0c;注册注销等&#xff0c;他们是根据用户是否等率决定的 具体代码如下 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System…

CSS 垂直居中

1、使用绝对定位垂直居中 绝对对位原理&#xff1a;元素在过度受限情况下&#xff0c;将margin设置为auto&#xff0c;浏览器会重算margin的值&#xff0c;过度受限指的是同时设置top/bottom与height或者left/right与width。 .absolute_center{/*display:none;*/position:absol…

[ExtJs6] 环境搭建及创建项目

1. 环境搭建 sencha cmd 和 extjs6 sdk.sencha cmd: https://www.sencha.com/products/extjs/cmd-download/extjs6: https://www.sencha.com/sencha cmd 安装过程将其加入系统的环境变量path中。export PATH${PATH}:/Users/shuanggai/bin/Sencha/Cmd/6.2.2.36把改语句添加到 ~/…

通过Spring Integration和RabbitMQ获得高可用性的AMQP支持的消息通道

Spring Integration消息通道默认情况下将消息存储在内存中。 这是因为内存速度快&#xff0c;易于实现&#xff0c;并且不会增加网络成本。 但是&#xff0c;在某些情况下&#xff0c;这可能会引起问题&#xff0c;因为如果应用程序崩溃或服务器意外关闭&#xff0c;所有消息都…

vue之node.js的简单介绍

一、什么是node.js&#xff1f; 它是可以运行JavaScript的服务平台&#xff0c;可以吧它当做一门后端程序&#xff0c;只是它的开发语言是JavaScript 二、安装 1、node.js的特性&#xff1a; - 非阻塞IO模型- 时间驱动 2、运用的场景&#xff1a; - 高并发低业务- 实时场景- 聊…

jQuery之jQuery扩展和事件

一、jQuery事件 常用事件 blur([[data],fn]) 失去焦点 focus([[data],fn]) 获取焦点&#xff08; 搜索框例子&#xff09; change([[data],fn]) 当select下拉框中的元素发生改变的时候触发change事件(select例子) click([[data],fn]) 点击 dblclick([[data],fn])…

JavaFX列表示例

这是使用JavaFX构建的示例列表应用程序。 该应用程序是待办事项列表。 此应用程序具有添加&#xff0c;更新和删除列表中项目的功能。 列表数据存储在HSQLDB关系数据库中。 该应用程序使用JDBC&#xff08;Java数据库连接&#xff09;API访问数据库。 该应用程序打包为可执行JA…

flex弹性布局操练2

上一个是练习的1个内元素的&#xff0c;这次练习两个元素的。 ul.box1 {list-style:none;background-color:black;display:flex;justify-content:space-between;/*水平轴的位置,默认是flex-start就是第一种情况*/align-items:center;/*垂直轴的位置*/width:100px;height:100px;…