jpa避免n+1_JPA技巧:避免N + 1选择问题

jpa避免n+1

介绍

像JPA这样的ORM框架通过帮助我们在对象<->关系数据映射期间避免了很多样板代码,从而简化了我们的开发过程。 但是,它们还给表带来了一些其他问题,N + 1是其中之一。 在本文中,我们将简要介绍该问题以及避免这些问题的一些方法。

问题

作为示例,我将使用在线图书订购应用程序的简化版本。 在这样的应用程序中,我可能会创建一个如下所示的实体来代表采购订单–

@Entity
public class PurchaseOrder {@Idprivate String id;private String customerId;@OneToMany(cascade = ALL, fetch = EAGER)@JoinColumn(name = "purchase_order_id")private List<PurchaseOrderItem> purchaseOrderItems = new ArrayList<>();
}

采购订单由订单ID,客户ID和要购买的一个或多个项目组成。 PurchaseOrderItem实体可能具有以下结构–

@Entity
public class PurchaseOrderItem {@Idprivate String id;private String bookId;
}

这些实体已经简化了很多,但是出于本文的目的,这是可以做到的。

现在假设我们需要找到一个客户的订单以在他们的采购订单历史记录中显示它们。 以下查询将用于此目的–

SELECTP
FROMPurchaseOrder P
WHEREP.customerId = :customerId

转换为SQL后,其外观如下所示–

selectpurchaseor0_.id as id1_1_,purchaseor0_.customer_id as customer2_1_ 
frompurchase_order purchaseor0_ 
wherepurchaseor0_.customer_id = ?

此查询将返回客户拥有的所有采购订单。 但是,为了获取订单项,JPA将为每个单独的订单发出单独的查询。 例如,如果某个客户有5个订单,那么JPA将发出5个附加查询以获取这些订单中包含的订单项。 这基本上被称为N + 1问题-1个查询以获取所有N个采购订单,N个查询以获取所有订单商品。

当我们的数据增长时,此行为为我们带来了可伸缩性问题。 即使适量的订单和项目也会造成严重的性能问题。

解决方案

避免热切获取

这是问题背后的主要原因。 我们应该摆脱从映射中获取的所有渴望。 它们几乎没有任何好处可证明其可用于生产级应用程序。 我们应该将所有关系标记为“懒惰”。

需要注意的重要一点–将关系映射标记为“惰性”并不保证基础持久性提供程序也将其同样对待。 JPA规范不保证延迟获取。 充其量对持久提供者来说是一个提示。 但是,考虑到Hibernate,我从未见过这样做。

仅获取实际需要的数据

始终建议使用此方法,而不考虑是否要进行急切/懒惰的访存。

我记得我进行过一次N + 1优化,将REST端点的最大响应时间从17分钟提高到1.5秒 。 端点根据某些条件获取单个实体,对于我们当前的示例,该实体将遵循以下原则:

TypedQuery<PurchaseOrder> jpaQuery = entityManager.createQuery("SELECT P FROM PurchaseOrder P WHERE P.customerId = :customerId", PurchaseOrder.class);
jpaQuery.setParameter("customerId", "Sayem");
PurchaseOrder purchaseOrder = jpaQuery.getSingleResult();// after some calculation
anotherRepository.findSomeStuff(purchaseOrder.getId());

id是结果中唯一用于后续计算的数据。

有几个客户有上千个订单。 每个命令依次又有数千个其他几种不同类型的子级。 不用说,每当在此端点接收到针对这些订单的请求时,数据库中就会执行数千个查询。
为了提高性能,我所做的就是-

TypedQuery<String> jpaQuery = entityManager.createQuery("SELECT P.id FROM PurchaseOrder P WHERE P.customerId = :customerId", String.class);
jpaQuery.setParameter("customerId", "Sayem");
String orderId = jpaQuery.getSingleResult();// after some calculation
anotherRepository.findSomeStuff(orderId);

只是此更改导致680倍的改进
如果我们要获取多个属性,则可以利用JPA提供的Constructor表达式–

"SELECT " +
"NEW com.codesod.example.jpa.nplusone.dto.PurchaseOrderDTO(P.id, P.orderDate) " +
"FROM " +
"PurchaseOrder P " +
"WHERE " +
"P.customerId = :customerId",
PurchaseOrderDTO.class);
jpaQuery.setParameter("customerId", "Sayem");
List<PurchaseOrderDTO> orders = jpaQuery.getResultList();

使用构造函数表达式的一些注意事项–

  1. 目标DTO必须具有其参数列表与要选择的列匹配的构造函数
  2. 必须指定DTO类的完全限定名称

使用联接提取/实体图

每当我们需要同时获取带有所有子元素的实体时,就可以在查询中使用JOIN FETCH 。 这样可以减少数据库流量,从而提高性能。

JPA 2.1规范引入了实体图,它使我们能够创建静态/动态查询负载计划。
Thorben Janssen ( 这里和这里 )写了几篇文章,详细介绍了它们的用法,值得一看。
这篇文章的一些示例代码可以在Github上找到。

翻译自: https://www.javacodegeeks.com/2018/04/jpa-tips-avoiding-the-n-1-select-problem.html

jpa避免n+1

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

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

相关文章

Windows server 2008 基础知识

1、Windows server 2008 安装形式 (1、Virtual (2、Hyper-V (3、VMware 2、对于虚拟内存的大小&#xff0c;建议为实际内存的1.5倍 3、在Windows server 2008系统中安装活动目录的命令是DCPROMO。活动目录存放在域控制器中 4、Windows server 2008支持的两种用户账户&…

Paint X for Mac的用法

文章目录快捷键图形界面介绍快捷键 快捷键说明Command A抓手Command S另存为Command T设置字体Option Command C设置画布大小 Canvas SizeOption Command I设置图像大小 Image SizeShift Command V垂直翻转 Flip VerticalShift Command H水平翻转 Flip HorizontalC…

IntelliJ IDEA for Mac如何替换文件内容中指定的关键词

按下组合键 Shift R 打开替换对话框&#xff08;如下图所示&#xff09;&#xff0c;在对话框中有两个输入框&#xff0c;上面的输入框中输入被替换的关键词&#xff0c;会在指定的范围内查找被替换的关键词&#xff0c;接着在下面的输入框中输入替换的关键词&#xff0c;然后…

海域动态监视监测管理系统_监视和管理备份系统

海域动态监视监测管理系统上一次我们建立一个强大的备份系统时 &#xff0c;现在我们将研究如何监视备份集。 我们需要验证是否正确清理了备份集&#xff08;这称为删除策略&#xff09;&#xff0c;并且它们是一致的&#xff08;称为一致性策略&#xff09;。 备份集可以包含…

Thread 线程同步、线程状态

线程概念 线程&#xff08;英语&#xff1a;thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。 一、多线程介绍 1.1创建线程类 Java中通过继承Thread类来创建并启动多线程的步骤如下&#xff1a; 1.定义Thr…

jsp过滤器示例_Java 8过滤器,地图,收集和流示例

jsp过滤器示例大家好&#xff0c;许多读者给我发了电子邮件&#xff0c;写了一篇有关Java 8的地图和过滤器功能的文章&#xff0c;因为他们发现它们难以理解和使用。 即使我以前同时写过有关map&#xff08;&#xff09;和filter&#xff08;&#xff09;的博客&#xff0c;我仍…

IntelliJ IDEA for Mac如何使用单元测试Junit

文章目录测试分类Junit 使用步骤测试结果的判定常用的注解BeforeAfter测试分类 黑盒测试&#xff1a;不需要写代码&#xff0c;不需要关注程序执行具体逻辑和流程&#xff0c;给输入值&#xff0c;看程序是否能够输出期望的值 白盒测试&#xff1a;需要写代码&#xff0c;需要…

File类、递归、字节流

一、File类 1.1 概述 java.io.File 类是文件和目录路径名的抽象表示&#xff0c;主要用于文件和目录的创建、查找和删除等操作。 1.2 构造方法 public File(String pathname) &#xff1a;通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 public File(String …

java求期望_Java 11的期望

java求期望过去的几年对Java世界一直是动荡不安的&#xff0c;在相当多的发行版中添加了各种各样的功能。 在开发人员社区中&#xff0c;人们逐渐意识到Java的开发速度不够快。 在最初的20年中&#xff0c;Java平台和Java开发工具包&#xff08;JDK&#xff09;经历了庞大&…

IntelliJ IDEA for Mac 封装字段(添加setter/getter方法)

可以利用 IDEA 对类中的字段进行封装&#xff0c;所谓“字段封装”&#xff0c;就是指通过调用方法的方式来访问字段&#xff0c;而不是直接通过“对象.字段名”的方式去访问。 例如&#xff0c;成员变量 hairColor 是一个 public 的变量&#xff0c;没有相关的 setter/getter…

JDBC预处理对象prepareStatement

JDBC预处理对象prepareStatement概述 一、SQL注入问题 SQL注入&#xff1a;用户输入的内容作为了SQL语句语法的一部分&#xff0c;改变了原有SQL真正的意义。 假设有登录案例SQL语句如下: SELECT * FROM 用户表 WHERE NAME 用户输入的用户名 AND PASSWORD 用户输的密码; 此…

bean包、entity包、mode包、domain包的区别

文章目录beanentitymodeldomain总结bean 包含的都是 JavaBean。 JavaBean 是一种 Java 语言写成的可重用组件。为写成 JavaBean&#xff0c;类必须是具体和公共的&#xff0c;并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成属性。Jav…

java修改数据历史记录_记录更新(Java数据类)

java修改数据历史记录最近几个月&#xff0c;有一些与“ Java数据类”&#xff08;又称为“记录”&#xff09;有关的更新。 正如我在“ JavaFX&#xff0c;Valhalla&#xff0c;数据类和Java的浮点更新 ”一文中简短提到的那样&#xff0c; Brian Goetz的“ Java数据类 ”“探…

idea配置Maven的本地仓库(打开新的项目时自动更新本地仓库的位置)

配置Maven的本地仓库 一、打开目录然后选择 Other Settings → Settings for New Projects 二、配置自己的文件路径和本地仓库 三、设置每次自动更新本地仓库

java导出数据透视表_使用数据库中的Java流制作数据透视表

java导出数据透视表来自数据库行和表的原始数据不能为人类读者提供太多了解。 相反&#xff0c;如果我们对数据执行某种聚合&#xff0c;则人类更有可能看到数据模式 在向我们展示之前。 数据透视表是聚合的一种特定形式&#xff0c;我们可以在其中应用排序&#xff0c;求平均…

Java程序和MySQL数据库中关于小数的保存问题

文章目录MySQL 中的小数类型decimaldoublefloatJava 中的小数类型floatdoubleBigDecimal金额的计算MySQL 中的小数类型 decimal MySQL 使用 decimal 保存高精度的小数&#xff0c;可以设置保留的小数个数。decimal(m,d)&#xff0c;表示该值一共显示 m 位整数&#xff0c;其中…

javafx 遮罩_JavaFX技巧31:遮罩/剪切/ Alpha通道

javafx 遮罩选择条 最近&#xff0c;我不得不实现一个自定义控件&#xff0c;该控件使用户可以从项目列表中选择一个项目。 此“ SelectionStrip”控件必须水平放置项目&#xff0c;并且在项目过多的情况下&#xff0c;允许用户左右水平滚动。 该控件将在空间受限的区域中使用&…

IntelliJ IDEA for Mac如何查看某个方法的实现

如果没有子类&#xff0c;按住 Command&#xff0c;鼠标点击方法名会跳到声明定义方法的地方&#xff1b;如果是接口或者父类&#xff0c;那么只会跳至接口或者父类声明方法的地方&#xff0c;想要看子类的实现&#xff0c;可以点击鼠标右键&#xff0c;选择 【Go To】 -> 【…

日志管理工具_您需要了解的6种日志管理工具(以及如何使用它们)

日志管理工具如果没有正确的工具来汇总和解析您的日志数据&#xff0c;则几乎不可能找到并了解所需的信息。 日志有无穷的用途&#xff0c;因为日志本身是无止境的。 应用程序日志&#xff0c;安全日志&#xff0c;BI日志&#xff0c; 林肯日志 &#xff08;好吧&#xff0c;也…

Spring半注解半Xml

一、 Component 1.Component 标记了注解&#xff0c; 默认的名称是&#xff1a;简单类名&#xff0c;首字母小写UserDaoImpl -> userDaoImplUserServiceImpl -> userServiceImplComponent可以使用value属性指定对象的名称&#xff0c;相当bean标签的id属性 Component注解…