JPA 2 | 获取联接以及我们是否应该使用它们

介绍

最近,我一直在与JPA 2中的FETCH JOINS一起使用,以期从数据库中急切地获取数据,并且我学到了很多关于为什么在日常操作中应避免使用Fetch Joins的知识。

今天的博客文章谈论了我在Fetch上的经历和学习(主要基于当我在查询中有很多FETCH JOINS时获得的评论)。

问题陈述

在我们的项目中,有一种情况是从定义了许多集合值关联的数据库(OneToMany,ManyToMany,也称为ToMany关联)中获取实体。 这是实体外观的概念图(为清楚起见,省略了getter和setter)。 这是实体的极其简化的示例。 在实际情况下,我们大约有11个关联。

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

关于上述实体,有几件事需要注意:

  • 它具有3个收藏珍贵的协会。
  • 所有这些关联都被延迟获取,因为JPA中“集合有价值的关联”的默认获取策略是“惰性”。

在我们的业务实现中,我们有一个转换器,该转换器将DAO层返回的值转换为Business DTO。

因此,我们的业务方法的算法如下:

@TransactionAttribute
public List searchItemRecords (SearchCriteria sc) {List ir = itemRecordDao.search(sc);List convertedData = recordConverter.convert(ir);return convertedData;
}

请注意,整个方法都在Transaction内部运行。

每当我们从数据库中获取数据时,就不会急切地获取与成本,统计信息等相关的数据。 但是我们的ItemInformation DTO期望所有数据。 因此,当首次调用getCosts或getStatistics时,持久性提供程序(在本例中为Hibernate)向数据库激发查询以获取指定的数据。 这为我们创建了N + 1个选择查询问题。 如果您不熟悉N + 1选择或需要刷新,可以在DZone上查看此文章 。

我们大多数人,包括我在内,都会选择N + 1选择问题的最快,最简单的解决方案,即使用Fetch Join。 在Internet上发布的不同博客/文章中也有很多建议。

因此,我也采用了相同的方法。 就我而言,这至少是一种糟糕的方法。

首先让我们看看如何使用FETCH JOIN。

使用Fetch Join之前的查询如下:

SELECT item FROM ItemRecord item WHERE author=:author;

请注意,查询采用JPA形式。

该查询未获取集合值。 结果,在翻译器中,当我们对每个ItemRecord执行getCosts时,将触发类似于以下的查询:

SELECT cost FROM Cost where itemId = :itemId

因此,如果我们有3个ItemRecords,则激发到数据库的SELECT查询总数为:

  • 1用于获取所有ItemRecords
  • 3个用于获取每个ItemRecord的成本
  • 3用于获取每个ItemRecord的注释
  • 3个用于获取每个ItemRecord的统计信息

即(3乘3)+ 1

当将其转换为N乘以M +1时,

哪里,

  • N是找到的主要实体记录的数量
  • M是主要实体中Collection值关联的数量
  • 1个查询,用于获取所有主要实体

在实际情况下,我们有11个关联。 因此,对于每个主要ItemRecord实体,我们将触发11个SELECT查询。 激发的查询数量乘以找到的每个ItemRecord。

使得集合值关联成为EAGER是不可行的,因为在实体上运行的许多其他查询仅需要选定的数据。

这不是最佳解决方案。 必须做一些事情,很多互联网文章建议使用FETCH JOINS,因为它们最容易实现并解决了(N Time M +1)个查询问题。

因此,我决定在查询中使用FETCH Joins来针对给定场景急切地获取所有数据。

该查询类似于:

SELECT item FROM ItemRecordJOIN FETCH item.costs,
JOIN FETCH item.notes,
JOIN FETCHitem.stats;

跨栏1

我很快意识到,该查询在我的情况下将不起作用,因为我可以拥有一个没有任何与之关联的统计信息的ItemRecord,在这种情况下,上述查询不会向我返回那个ItemRecord(由于ORM的工作方式)因此我将获得不完整的数据。

因此,我接下来转到LEFT JOIN FETCH,即使某些关联关系为空,它也将给我ItemRecord实体返回。 查询如下所示:

SELECT item FROM ItemRecordLEFT JOIN FETCH item.costs,
LEFT JOIN FETCH item.notes,
LEFT JOIN FETCH item.stats;

跨栏2

当我运行单元测试以测试上述查询时,出现了异常:

javax.persistence.PersistenceException: org.hibernate.HibernateException: cannot simultaneously fetch multiple bags

问题是什么?

问题是我在实体中使用列表作为集合类型。 使用列表会混淆JPA /休眠。 这篇文章很好地记录了这种困惑

为了解决此问题,我选择使用Set而不是List,主要是因为它是上述博客文章提供的三种解决方案中最简单的方法,并且也很有意义(至少在我实现时)。

因此,我将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;}

我再次运行测试用例,并且测试查询的测试用例成功。 耶皮 但是,我的另一个测试用例的测试用例失败了。 看一下测试用例,我意识到我需要按照特定的顺序输入数据,并且使用的是未排序的HashSet。 解决方案很简单。 使用LinkedHashSet维护元素的顺序。

使用LinkedHashSet可以解决问题,并且我的测试用例通过了。

我很高兴,但我的幸福短暂。

跨栏3

我有另一个测试用例,它预期给定ItemRecord的3个成本对象。 一旦我转到“设置实现”,测试就开始失败。 原来我的哈希码不正确,并且我的“成本实体”的实现等于“实现”,“成本实体”为两个不同的实体返回相同的哈希码,结果,由于Set不允许重复值,因此仅持久保留了一个实体。

因此,我接下来要做的就是为我的所有实体使用适当的HashCode和Equals实现。

最终关卡

最终,当我所有的测试用例开始通过时,我进行了代码审查,并将其发送给团队。

第一个吓到我的是我的技术主管。 :)

他只是生气地看着FETCH JOINS。 原因? 原因是LEFT FETCH JOINs返回所有数据的笛卡尔积。 鉴于我们生产中的数据量很大,甚至在ItemRecord上支持多个选择也将成为一场噩梦。 整个问题可以在此博客文章中轻松理解


因此,我试图解决性能问题,事实证明我实际上是在制造更大的性能问题。 :)

删除了转移到FETCH JOIN的整个解决方案,因此决定进一步研究为什么我们需要UI上的全部数据,以及为什么不能将获取数据分成较小的专用事务。

摘要:

使用Fetch Joins的整个过程使我很好地了解了Joins的总体工作原理以及使用它们时的期望。

希望您喜欢这篇博客文章。 如果您想继续阅读有趣的帖子,可以关注我的博客。

参考: JPA 2 | 从JavaWorld Blog博客中获取Fetch Joins,以及是否应从 JCG合作伙伴 Anuj Kumar 使用它们 。

翻译自: https://www.javacodegeeks.com/2013/07/jpa-2-fetch-joins-and-whether-we-should-use-them.html

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

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

相关文章

mysql yintint类型_MySQL服务器2 被嫌弃的胖子

1.sql的基本语法对数据库create database db1;  创建数据库对表:create database t1(id int,name char(10));  创建表show create table t1;  查看创建的t1表show tables;  查看所有的表desc t1;  查看表的详细结构对数据:insert into t1(id,n…

Html5表单元素-搜索框和上传文件框

1、search - 搜索框element/form/input/search.html<!doctype html><html><head> <title>search</title></head><body> <!-- search - 搜索框&#xff0c;文本框形式 --> <input type"search"…

Shell 简单的java微服务jar包 -- 部署脚本

部署描述&#xff1a; 1.jenkins 通过maven编译成jar 项目包 2.shell 脚本从jenkins机器发布到&#xff1a;目标主机 注释&#xff1a;次脚本没有写jar包的备份&#xff0c;有时间加上 脚本内容&#xff1a; #!/bin/bash#线上服务器列表 HOST_LIST${:2}#项目名 REMOTE_PROJECT$…

BZOJ 4552 [Tjoi2016Heoi2016]排序 | 二分答案 线段树

题目链接 题面 题目描述 在2016年&#xff0c;佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题&#xff0c;现在他在研究一个难题&#xff0c;需要你来帮助他。这个难题是这样子的&#xff1a;给出一个1到n的全排列&#xff0c;现在对这个全排列序列进行…

python import 类 继承_python学习之类的继承

面向对象中一个重要的特性就是继承&#xff0c;继承的好处就是提高代码的重用率&#xff0c;减少不必要的代码。继承是父类与子类的关系&#xff0c;当子类继承了父类后&#xff0c;就具有了父类的所有变量和方法。在python中定义继承的语法是&#xff1a;class 派生类名(基类名…

Html5画布(canvas)实例之绘制矩形

路径方式绘制 - 矩形 | rect()canvas/shape/path/rect.html <!DOCTYPE HTML><html><head> <title>以路径的方式在 canvas 上绘制矩形的 demo</title></head><body> <canvas id"canvas" width"300" hei…

[patl1-046]整除光棍

解题关键&#xff1a;模拟除法 #include<cstdio> #include<cstring> #include<algorithm> #include<cstdlib> #include<iostream> #include<cmath> using namespace std; typedef long long ll; char ans[10002]; int main(){int n;cin>…

使用AspectJ审计Spring MVC Webapp。 第2部分

现在&#xff0c;如果您有兴趣创建一个以Aspectj的Aspect和Before批注的形式使用面向方面编程&#xff08;AOP&#xff09;的Spring MVC Webapp来审核用户对屏幕的访问&#xff0c;那么这是您想要阅读的博客。 正如我在上一个博客中所说的那样&#xff0c;审核用户对屏幕的访问…

服装店管理系统打造门店拓客、促活、存留营销方案

打造门店拓客、促活和存留营销方案对于服装店的管理系统来说是非常重要的。以下是一些可行的方案&#xff1a; 1. 会员管理系统&#xff1a;引入会员管理功能&#xff0c;建立会员档案&#xff0c;跟踪会员消费记录和偏好。通过会员系统&#xff0c;可以实施积分制度、生日礼品…

mysql添加映射模块_iis7.5中让html与shtml一样支持include功能(添加模块映射)

刚开始弄得时候&#xff0c;发现了很多错误&#xff0c;其实很简单&#xff0c;参考shtm原来的设置就可以了前提条件&#xff1a;ServerSideIncludeModule的安装&#xff1a;在安装iis的时候选择上该服务(“在服务端包含文件”&#xff0c;选项)即可&#xff0c;如下&#xff1…

全局对象与临时转换

全局对象 ECMAScript 规定全局对象叫做 global&#xff0c;但是浏览器把 window 作为全局对象 这些全局变量分为两种&#xff1a; ECMAScript 规定的global.parseIntglobal.parseFloatglobal.Numberglobal.Stringglobal.Booleanglobal.Object 浏览器自己加的属性window.alertwi…

Html5开发-使用Canvas绘制图片

呈现图片 | drawImage()canvas/media/image.html <!DOCTYPE HTML><html><head> <title>在 canvas 上呈现图片的 demo</title></head><body> <canvas id"canvas" width"800" height"600" sty…

文件上传控件bootstrap-fileinput的使用

一、准备1、插件下载地址&#xff1a;https://github.com/kartik-v/bootstrap-fileinput/ 下载后的压缩包解压文件夹内容如下&#xff1a; js&#xff1a;插件核心js代码&#xff0c;引用fileinput.min.js/fileinput.js即可&#xff0c;默认插件语言为英文&#xff0c;如需要中…

在JPA 2.1中使用@Convert正确完成映射枚举

如果您曾经在JPA中使用过Java枚举&#xff0c;那么您肯定会意识到它们的局限性和陷阱。 使用enum作为Entity的属性通常是一个很好的选择&#xff0c;但是2.1之前的JPA不能很好地处理它们。 它给了您2 1个选择&#xff1a; 托肖夫达林 Enumerated(EnumType.ORDINAL) &#xf…

python里orient_OrientDB Python连接操作

OrientDB Python连接操作Python的OrientDB驱动程序使用二进制协议。 PyOrient是git hub项目名称&#xff0c;它用于将OrientDB与Python连接起来并操作数据。 它适用于OrientDB 1.7及更高版本。以下命令用于安装PyOrient。pip install pyorient可以使用名为demo.py的脚本文件执行…

HTML5-画布(canvas)效果之-渐变色

<!DOCTYPE HTML><html><head> <title>渐变色</title></head><body> <canvas id"canvas" width"200" height"100" style"background-color: rgb(222, 222, 222)"> 您的浏…

ssh架构之hibernate(一)简单使用hibernate完成CRUD

1.Hibernate简介 Hibernate是一个开放源代码的对象关系映射(ORM)框架&#xff0c;它对JDBC进行了非常轻量级的对象封装&#xff0c;它将POJO与数据库表建立映射关系&#xff0c;是一个全自动的orm框架&#xff0c;hibernate可以自动生成SQL语句&#xff0c;自动执行&#xff0c…

使用AspectJ审计Spring MVC Webapp。 第1部分

如果您像我一样&#xff0c;那么您将拥有那种编程天&#xff0c;一切似乎都进展顺利。 您编写代码和测试&#xff0c;它就可以正常工作。 然后还有其他日子&#xff0c;非常糟糕的日子&#xff0c;在那儿&#xff0c;您知道所编写的所有内容都尽可能正确&#xff0c;并且代码拒…

GitHub注册和Git安装

一、注册GitHub GitHub官方地址&#xff1a;https://github.com。 在浏览器中打开GitHub网址&#xff0c;通过首页进行注册&#xff0c;如下图所示。 二、安装Git Git官方下载地址&#xff1a;http://git-scm.com/download/。 Git支持多平台&#xff08;Mac OS X/Windows/Linux…

django mysql迁移_关于django 数据库迁移(migrate)应该知道的一些事

命令首先数据库迁移的两大命令&#xff1a;python manage.py makemigrations & python manage.py migrate前者是将model层转为迁移文件migration&#xff0c;后者将新版本的迁移文件执行&#xff0c;更新数据库。这两中命令调用默认为全局&#xff0c;即对所有最新更改的mo…