eager_EAGER的获取是代码的味道

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

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

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

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

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

相关文章

什么时候会是用treeset?_flex:1 到底代表什么?

今天在做项目的时候遇到一个关于布局的问题, 就是 flex: 1; 我一直以为 flex: 1; 代表的是 flex: aoto; 后来发现结果并不是这样, 所以写一篇博客来讲解一下 flex: 1; 代表什么代码第一版<div class"container"><div class"div">我是一个div&l…

纠缠不清的C语言位域(位段)详解

位域是什么&#xff1f;有些数据在存储时并不需要占用一个完整的字节&#xff0c;只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态&#xff0c;用 0 和 1 表示足以&#xff0c;也就是用一个二进位。正是基于这种考虑&#xff0c;C语言又提供了一种叫做位域的…

matlab画累计直方图_科学网—matlab 绘制直方图——常用命令 - 范凯波的博文

直方图上显示数值close all ,x rand(100,1);%获得直方图的数据[n,y] hist(x);maxN max(n);%设置显示x,y长度限制axis([0 1.2 0 maxN2])%根据直方图的数据绘制出图形bar(y,n);for i 1:length(y)%直方图上面数据对不齐&#xff0c;利用水平和垂直对齐 &#xff0c;可以参考se…

django debug=false后静态文件丢失_python框架Django实战商城项目之工程搭建

项目说明 该电商项目类似于京东商城&#xff0c;主要模块有验证、用户、第三方登录、首页广告、商品、购物车、订单、支付以及后台管理系统。 项目开发模式采用前后端不分离的模式&#xff0c;为了提高搜索引擎排名&#xff0c;页面整体刷新采用jinja2模板引擎实现&#xff0c;…

mysql解压缩 1067_windows安装mysql8.0.0解压版附出现1067错误解决方法

1、自己到mysql官网下载mysql-8.0.0-dmr-winx64.zip解压缩安装包2、下载页面地址&#xff1a;https://dev.mysql.com/downloads/mysql/3、解压缩到任意目录(我自己是D:\DevTools\mysql-8.0.0)4、配置环境变量添加path路径为你的mysql8.0.0路径下面的bin目录(我的目录是D:\DevTo…

基于C语言的函数指针应用-消息命令处理框架

简述大家都知道&#xff0c;在C语音中指针的地位很重要&#xff0c;各种指针&#xff0c;功能很强大&#xff01;但是用不好&#xff0c;指针也比较容易出问题。这里介绍的是函数指针的一种应用方法&#xff0c;即使用函数指针来实现消息命令的注册与回调处理。代码测试的处理函…

easyexcel 动态列_easyexcel动态表头列导出SequenceDiagram 阅读源码事半功倍

EasyExcel简介Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&#xff0c;但POI还是有一些缺陷&#xff0c;比如07版Excel解压缩以及解压后存储都是…

python3进阶_Python3 进阶教程 2020全新版

REVENGE_7771天前如果不创建class类的话&#xff0c;直接使用dog.name是会报错的&#xff0c;因为一般数据类型不接受这种调用方式0赞 0采集潘某人永不屈服2天前class Student()定义的时候&#xff0c;需要在括号内写明继承的类Person在__init__()方法&#xff0c;需要调用sup…

C语言中的输入输出

在C语言中提供了许多内置的输入输出函数。标准文件的定义在C语言中会把所有设备当文件来处理。为了访问键盘和屏幕&#xff0c;以下三个文件会在程序执行时而打开。标准文件文件指针设备标准输入stdin键盘标准输出stdout屏幕标准错误stderr屏幕C 语言中的输入/输出通常使用内置…

lambda 延迟执行_Java Lambdas和低延迟

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

C语言“悬空指针”和“野指针”究竟是什么意思?

各位&#xff0c;提起C语言我们很自然就会想到指针二字&#xff0c;没错&#xff0c;作为C的核心和灵魂&#xff0c;它的地位咱们就不再赘述了&#xff0c;今天我们想跟大家讲的是指针中的两个特有名词&#xff1a;“悬空指针”和“野指针”。悬空指针C语言中的指针可以指向一块…

grad在python什么模块_深度学习(Deep Learning)基础概念1:神经网络基础介绍及一层神经网络的python实现...

此专栏文章随时更新编辑&#xff0c;如果你看到的文章还没写完&#xff0c;那么多半是作者正在更新或者上一次没有更新完&#xff0c;请耐心等待&#xff0c;正常的频率是每天更新一篇文章。 该文章是“深度学习&#xff08;Deep Learning&#xff09;”系列文章的第一部分&…

ubuntu终端命令停止_从命令行关闭Linux计算机的5种方法

没有操作系统是完美的。 即使相对稳定,驱动程序和应用程序也可能存在问题。 Linux也不例外。 尽管比Windows更稳定(在许多情况下,并非全部!),但可能还需要重新启动Linux计算机。 这可能是因为某些东西不起作用。 或者,您可能通过SSH连接到远程计算机或服务器,并希望它重新…

C语言编写简单朗读发音小工具!!

各位&#xff0c;今天给大家带来C语言结合VBS脚本写的一个简单的朗读小工具&#xff0c;做一个能够发音的C语言程序&#xff08;保证简单&#xff0c;人人都能学会&#xff09;。具备的知识体系&#xff1a;C语言基本框架C语言输入输出C语言文件操作C语言system函数VBS指令&…

未发现oracle(tm)客户端和网络组件_SpringColud Eureka的服务注册与发现

一、Eureka简介本文中所有代码都会上传到git上&#xff0c;请放心浏览 项目git地址&#xff1a;https://github.com/839022478/Spring-Cloud在传统应用中&#xff0c;组件之间的调用&#xff0c;通过有规范的约束的接口来实现&#xff0c;从而实现不同模块间良好的协作。但是被…

sap寄售退货单_多个退货单

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

课堂经验值管理小程序_微信小程序怎么管理门店?

微信门店小程序是一种不用注册下载就能使用的购物平台&#xff0c;近年来很是流行&#xff0c;而且它操作简单&#xff0c;能让用户快速找到自己需要的产品&#xff0c;然后进行购买&#xff0c;深得用户喜爱&#xff0c;有用户的地方就会有商家&#xff0c;商家想拥有自己的微…

C语言 | 直接插入排序

解题思路&#xff1a;直接插入排序是一种最简单的排序方法&#xff0c;其基本操作是将一条记录插入到已排好的有序表中&#xff0c;从而得到一个新的、记录数量增1的有序表。C语言源代码演示&#xff1a;#include//头文件 int main()//主函数 {void insort(int post[],int n)…

mysql+after+commit_Spring事务aftercommit原理及实践

来道题CREATE TABLE goods (id bigint(20) NOT NULL AUTO_INCREMENT,good_id varchar(20) DEFAULT NULL,num int(11) DEFAULT NULL,PRIMARY KEY (id),KEY goods_good_id_index (good_id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ciClass.forName("c…

序列化和反序列化的概念_序列化的概念

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