解决 Hibernate N+1 问题

Tartancalib标定OAK-FFC_4p(上) 1.使用Tartancalib标定双目或多目相机前,先对单个相机标定获得内参,再获得外参。 阅读详情

问题

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

@Entity
public class PurchaseOrder {

    @Id
    private 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 {

    @Id
    private String id;

    private String bookId;
}

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

SELECT
    P
FROM
    PurchaseOrder P
WHERE
    P.customerId = :customerId

转换为 SQL 时看起来如下所示 -

select
    purchaseor0_.id as id1_1_,
    purchaseor0_.customer_id as customer2_1_ 
from
    purchase_order purchaseor0_ 
where
    purchaseor0_.customer_id = ?

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

解决方法

避免使用立即抓取(Eager Fetching)

这是问题背后的主要原因。我们应该从我们的映射中摆脱所有立即抓取。它们几乎没有任何好处可以证明它们在生产级应用中的使用。我们应该将所有关系标记为懒惰。

只抓取你真实需要的数据

有时候我们并不想在查询订单时关联出所有的订单记录,我们可以将订单记录设为懒加载,在自己真实需要时再去查询对应的数据。

在 JPQL 中使用 Fetch Join

初始化延迟关联的更好选择是使用带有抓取连接的 JPQL 查询。

Query q = this.em.createQuery("SELECT o FROM Order o JOIN FETCH o.items i WHERE o.id = :id");
q.setParameter("id", orderId);
newOrder = (Order) q.getSingleResult();

这告诉实体管理器在同一查询中加载所选实体和关系。

使用 BatchSize 批量抓取

批量抓取是惰性选择抓取策略的优化。假设该订单的商品条目有 25 个,当配置了 BatchSize 后,在请求订单时,查询将变为 3 条,每条语句使用 In 查询 5 个商品条目。

使用 @BatchSize 注解可以配置到懒加载的集合或对象上。

@Entity
@BatchSize(size=100)
class PurchaseOrderItem {
...
}

@OneToMany
@BatchSize(size = 5) /
private List<PurchaseOrderItem> purchaseOrderItems() = { ... };

实体图(Entity Graph)

实体图是特定化持久性查询或操作的模板。它们在创建**抓取方案(fetch plans)**或同时检索的持久字段组时使用。应用程序开发人员使用抓取方案将相关的持久字段组合在一起以提高运行时性能。

默认情况下,实体字段或属性是**懒抓取(lazy fetch)**的。开发人员将字段或属性指定为抓取方案的一部分,持久性 provider 将立即抓取(eager fetch)它们。

我们可以使用注解或部署描述符(比如 web.xml)静态创建实体图,也可以使用标准接口动态创建实体图。

实体图定义了在查找或查询操作期间需要立即抓取的字段。

默认,实体的所有字段都是懒抓取,除非指定了实体元数据的 fetch 属性为 javax.persistence.FetchType.EAGER。始终提取实体类的主键和版本字段,不需要将其显式添加到实体图中。

创建的实体图可以是 fetch graph(抓取图) 或 load graph(加载图)。

Fetch Graphs(抓取图)

当 javax.persistence.fetchgraph 属性用于指定实体图时,实体图的属性节点指定的属性将被视为 FetchType.EAGER,未指定的属性将被视为 FetchType.LAZY。 以下规则适用,具体取决于属性类型。

Load Graphs(加载图)

当 javax.persistence.loadgraph 属性用于指定实体图时,实体图的属性节点指定的属性将被视为 FetchType.EAGER,未指定的属性将根据其指定的或默认的FetchType 进行处理。

命名实体图(Named Entity Graph)

命名实体图是由应用于实体类的 @NamedEntityGraph 注解定义的实体图,或应用程序部署描述符中的 named-entity-graph 元素。部署描述符中定义的命名实体图覆盖任何具有相同名称的基于注解的实体图。

通过使用 javax.persistence.NamedAttributeNode 注解在 @NamedEntityGraph 的 attributeNodes 元素中指定字段,将字段添加到实体图中:

@NamedEntityGraph(name="emailEntityGraph", attributeNodes={
    @NamedAttributeNode("subject"),
    @NamedAttributeNode("sender")
})
@Entity
public class EmailMessage {
    @Id
    String messageId;
    String subject;
    String body;
    String sender;
}

通过在 @NamedEntityGraphs 注解中对多个 @NamedEntityGraph 定义进行分组,可以将多个 @NamedEntityGraph 定义应用于类。

@NamedEntityGraphs({
    @NamedEntityGraph(name="previewEmailEntityGraph", attributeNodes={
        @NamedAttributeNode("subject"),
        @NamedAttributeNode("sender"),
        @NamedAttributeNode("body")
    }),
    @NamedEntityGraph(name="fullEmailEntityGraph", attributeNodes={
        @NamedAttributeNode("sender"),
        @NamedAttributeNode("subject"),
        @NamedAttributeNode("body"),
        @NamedAttributeNode("attachments")
    })
})
@Entity
public class EmailMessage { ... }

通过为命名实体图调用 EntityManager.getEntityGraph 来获取定义的命名实体图。

EntityGraph<EmailMessage> eg = em.getEntityGraph("emailEntityGraph");

在查询操作中使用 Entity Graphs

要为有类型和无类型查询指定实体图,请在查询对象上调用 setHint 方法,并指定 javax.persistence.loadgraph 或 javax.persistence.fetchgraph 作为属性名称,并将 EntityGraph 实例指定为值:

EntityGraph<EmailMessage> eg = em.getEntityGraph("previewEmailEntityGraph");
List<EmailMessage> messages = em.createNamedQuery("findAllEmailMessages")
        .setParameter("mailbox", "inbox")
        .setHint("javax.persistence.loadgraph", eg)
        .getResultList();

有类型的查询使用相同的技术:

EntityGraph<EmailMessage> eg = em.getEntityGraph("previewEmailEntityGraph");

CriteriaQuery<EmailMessage> cq = cb.createQuery(EmailMessage.class);
Root<EmailMessage> message = cq.from(EmailMessage.class);
TypedQuery<EmailMessage> q = em.createQuery(cq);
q.setHint("javax.persistence.loadgraph", eg);
List<EmailMessage> messages = q.getResultList();

动态实体图(Dynamic Entity Graph)

创建动态实体图可以使用:EntityManager.createEntityGraph

动态实体图 类似于命名的 entity graph。唯一的区别是,entity graph 是通过 Java API 定义的。

EntityGraph graph = this.em.createEntityGraph(Order.class);
Subgraph itemGraph = graph.addSubgraph("items");
    
Map hints = new HashMap();
hints.put("javax.persistence.loadgraph", graph);
  
Order order = this.em.find(Order.class, orderId, hints);

使用代码动态创建 entity graph 允许我们可以不使用实体上的注解。因此,如果您需要创建一个不会重复使用的特定于用例的图表,我建议使用动态实体图。如果要重用实体图,则更容易注释命名实体图。

Spring Data JPA 中使用 Entity Graph

在 Spring Data JPA 中,我们可以通过在查询接口方法上使用注解 org.springframework.data.jpa.repository.EntityGraph 来定义命名实体图或动态实体图:

  • 通过指定 value 属性指定命名实体图

  • 通过指定 attributePaths 属性动态定义实体图

    该属性为数组类型,我们可以定义多个 attribute,也可以通过 property.nestedProperty 形式来定义实体对象字段嵌套的属性

    @EntityGraph(attributePaths = {"questions", "questions.questionOptions", "questions.answers"})
        Optional<Questionnaire> findOneByProject_Id(Long id);
    

原文链接:解决 Hibernate N+1 问题

FCKeditor 2.6.4 php版本任意文件上传——漏洞复现 一、漏洞描述 FCKeditor是目前最优秀的可见即可得网页编辑器之一,它采用JavaScript编写。具备功能强大、配置容易、跨浏览器、支持多种编程语言、开源等特点。它非常流行,互联网上很容易找到相关技术文档,国内许多Web项目和大型网站均采用了FCKeditor。 Fckeditor 低于 2.6.4 php 版本任意文件上传漏洞,当您的PHP环境配置为 GPC = Off 时可绕过currentfolder参数过滤上传脚本木马控制网站服务器 二、漏洞原理 漏洞版本:<=2.6.4 三、环境搭建 阅读详情

相关推荐

《遥感大模型生产级实战:从数据到决策的端到端AI工程》系列第一部分:AI遥感工程师的基础设施与工具链-文章 4:传统遥感分析与机器学习基线

本章将系统性地介绍经典遥感分析工作流,从数据预处理到多维度特征工程,再到如何利用经典的机器学习模型(如随机森林、支持向量机)进行监督与非监督学习。我们将通过一个完整的“智慧农业作物分类”项目实战,展示如何利用我们之前构建的数据湖和工具链,从原始数据到最终的分析结果,构建一个性能可靠、可解释性强的基线系统。更重要的是,我们将深入剖析这种传统方法的局限性,从而为后续章节引入遥感大模型的必要性提供强有力的论证。

panshengwu的博客 704

解决Hibernate N+1问题的3种方法

如果仔细观察 IN 条件,您会发现子查询中的代码几乎重复了第一个查询。为了加快这种情况,我们可以通过第一个查询过滤和页面作者检索他们的 ID。如果您执行查询以选择 n 个实体,并且如果您尝试调用实体的惰性关联的任何访问方法,Hibernate 将执行 n 个附加查询来加载延迟获取的对象。除了第一个查询之外,当我们打印书籍数量时,将为每个作者调用 n 次此查询。此查询将称为 N/M 次,其中 N 是作者的数量,M 是指定的批大小。此查询工作正常,但它有一个问题:它不允许我们使用分页,因为限制不会应用于作者。

allway2的博客 1204

营销反作弊之 异常行为分析模型(一),附带python代码

通过对用户行为进行深入的分析,识别出与常规模式不符的异常行为,可以有效检测并阻止虚假广告点击、刷单、点击欺诈等欺诈行为。本文将详细探讨营销反欺诈中异常行为分析的方法,介绍如何通过数据采集、清洗、行为建模与异常检测,准确识别潜在的欺诈行为,并提供 Python 实现代码示例。**监督学习 (Supervised Learning):**在标注数据的帮助下,训练机器学习模型使其能预测未知数据的结果。营销反欺诈异常行为分析的整体流程涉及多个环节,确保通过对用户行为的深入分析,及时识别潜在的欺诈行为。

viviwiky的博客 1230

JPA 查询性能优化 ------EntityGraph篇

)@Entity@Id在上述代码中,@EntityGraph注解指定了要使用的命名实体图"customer-with-orders",指定加载关联实体orders。这样就可以在查询客户列表时一次性获取关联的订单信息,避免N+1问题。也可以配置视图的子视图,如下所示,Order类中关联了Orderdetail实体。@Entity},)}@Id上述代码中,@NamedAttributeNode指定要加载的关联属性orders,和orders的关联实体orderDetail。

hua501014的博客 2763

千兆以太网PHY设计:PCB原理图完整示例

深入解析千兆以太网PHY电路设计要点,提供完整的PCB原理图设计实例,帮助工程师掌握高速信号布局与电源去耦等关键技术,提升设计稳定性与信号完整性。

weixin_30633869的博客 628

Linux 桌面虚拟化技术 KVM

Linux 桌面虚拟化技术 KVM

高飞的博客 36万+

性能优化(1+N,list与iterator,缓存,事务)

1、注意session.clear()的运用,尤其是不断分页循环的时候 A 在一个大集合中进行遍历,取出其中含有敏感字的对象 B 另一种形式的内存泄露. 2、1+N问题 问题描述:如@ManyToOne时,两个类分别是User与Group,取User时,本想发一条SQL语句,结果顺带发了N条语句,将每个User对应的Group也查询了。 解决方法有三种:   (1)设为@Man...

weixin_30914981的博客 174

ModbusRTU串口读写报文解析

解析MODBUS报文的格式,讨论异常状态、读多个寄存器与写多个寄存器

Serendipitor的博客 9208

二十二、 1+N 问题

问题的来源: 在@ManyToOne中,由于默认的fetch=FetchType.EAGER,所以如下的代码 List&lt;Topic&gt; topics = session.createQuery("from Topic").list(); 本来只发一条sql就能查询出所以的Topic对象,结果Topic所关联的Catagory对象也会马上查询出来,而且如果有N个Topic对象,就会...

chongqingnantian的专栏 198

定位精度再突破!Inner-MPDIoU让YOLOv11边界框回归更精准

Inner-MPDIoU 凭借辅助边框与最小点距离的双机制创新,彻底解决了传统 IoU 损失在边界框回归上的精度瓶颈。本教程从原理到代码实现,再到工业场景适配,完整呈现了其在 YOLOv11 中的集成路径。只需简单修改两处核心脚本,你的目标检测模型就能在边界框定位精度上实现跨越式提升——无论是学术研究中的指标突破,还是工业场景下的精细检测需求,Inner-MPDIoU 都能成为你攻克边界框回归难题的利器。现在就动手尝试,让你的检测模型在定位精度上再上一个台阶吧。

FJN110的博客 157

HTML5+CSS3+JS小实例:扇形展开折叠菜单

你肯定见过那种圆形浮动按钮(FAB),点一下弹出几个选项。大部分实现就是菜单项从底下往上"弹"出来,排成一列——能用,但没啥记忆点。后来我看到 Path 那个经典的扇形菜单,点一下,五个按钮像花瓣一样从中心散开,每个带不同的延迟和弹性曲线。当时就觉得:这才对嘛,交互就该有点"戏剧性"。今天拆一个纯手写版本,HTML + CSS + JS,不依赖任何库。六菜单项、扇形 144° 展开、弹性过渡、点击关闭、键盘可达——该有的都有。

艾恩小灰灰的博客 164

Hibernate batch-size hibernate.jdbc.batch_size

今天花了一天的时候测试batch-size和hibernate.jdbc.batch_size这个属性.首先说说 hibernate.jdbc.batch_size 这个属性.这个属性的使用场合是批量导入数据或批量删除时使用.其实就是相当于使用PreparedStatement.executeBatch()方法..将数个sql语句一起提交获得性能上的提高. hibernate.jdbc.batch

dengyin2000的专栏 9687

AI智能体在化学中的伦理问题:7个案例,架构师如何应对?

AI在化学中的应用早已不是新鲜事:药物发现中用AI预测分子活性(比如AlphaFold预测蛋白质结构)、化工生产中用AI优化合成路线(降低成本30%+)、材料研发中用AI设计新型电池材料(缩短研发周期5年)……但随着AI从“辅助工具”升级为“自主决策智能体”(比如能自动调整反应条件的“化学机器人”),伦理风险开始从“潜在隐患”变成“真实事故”。AI在化学中会踩哪些伦理“坑”?(用案例具象化)这些“坑”的底层逻辑是什么?(从技术到价值观的链条)架构师能做什么?

AI 算法实战派 1361

SpringDataJpa解决Hibernate 1+N问题

Hibernate n+1问题解决 实体类 /** * 班级实体类 */ @Data @Entity @Table(name = "class") public class StudentClass { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String className; @OneToMany(mappedBy = "stude

大榆的Hello Word 625

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Hibernate的1+N(N+1)问题与解决方法

Hibernate的1+N问题: fetch=FetchType.eager配置下,取某对象(Student)的自身属性,仍会导致其关联对象(Group)一并查询,本来只需要发1条SQL语句即可完成,结果却发了1+N条SQL语句,影响性能。  解决方法: 1.fetch = FetchType.lazy , 开始只发关于本表的SQL语句,即等用到关联对象时,再发关联表的SQL语句。 2

E-Kunt的博客 3530

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Hibernate N+1 问题及解决办法

原文链接:http://msi110.iteye.com/blog/710183 问题出现的原因: Hibernate中常会用到set,bag等集合表示1对多的关系,在获取实体的时候就能根据关系将关联的对象或者对象集取出,还可以设定cacade进行关联更新和删除。这不得不说hibernate的orm做得很好,很贴近oo的使用习惯了。 但是对数据库访问还是必须考虑性能问题的,在设定了1对多

JAVA AND GIS 1463

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

解决Hibernate中1+n的问题

一、1+n 在执行查询时,发送一条SQL语句获取N个主体对象,当需要使用这N个主体对象的关联属性时,Hibernate又发送N条SQL语句查询这个N个主体对象的关联属性,这就是1+N问题。 List list=session.createQuery("from Dept").list();  //发送一条sql语句查询主体对象 for (Dept dept2 : list) { Set

wtuxnz的博客 522

关于hibernate的n+1问题以及解决办法

关于hibernate的n+1问题以及解决办法

weixin_46246967的博客 90

Hibernate解决n+1问题

 观点:对于n+1问题的理解。   一般而言说n+1意思是,无论在一对多还是多对一当查询出n条数据之后,每条数据会关联的查询1次他的关联对象,这就叫做n+1。   但是我的理解是,本来所有信息可以一次性查询出来,也就是简单的连表查询,但是Hibernate会首先查询1次得到当前对象,然后当前对象里面的n个关联对象会再次访问数据库n次,这就是1+n问题。   他们二者之间表达的意思其...

baeryu4478的博客 89

hibernate的n+1问题及解决方案

(5)Hibernate得n+1问题 Hibernate 首先查询一次得到当前对象,然后当前对象的n个关联对象再次访问数据库n次,造成1+n问题 (6)Hibernate N+1问题解决方案 1、延迟加载 2、Fetch = “join” 做外连接,允许外键为空 3、二级缓存 ...

FightingITPanda的博客 533

怎么解决Hibernate的n+1问题文章2

原帖:http://www.javake.com.cn/web/hibernate/20130502/6422.html

奋斗不息 480

Hibernate中1+N问题以及解决方法

见:http://blog.youkuaiyun.com/eson_15/article/details/513221961. Hibernate中的1+N问题描述 在多对一关系中,当我们需要查询多的一方对应的表的记录时,可以用一条sql语句就能完成操作。然而,在多的一方的实体类中的@ManyToOne标注的fetch的默认值是fetchType.EAGER,这时,hibernate除了发...

愿我如星君如月 ... 夜夜流光相皎洁 ... 762

Hibernate 1+N问题的原理与解决

什么时候会遇到1+N的问题? 前提:Hibernate默认表与表的关联方法是fetch="select",不是fetch="join",这都是为了懒加载而准备的。 1)一对多() ,在1的这方,通过1条sql查找得到了1个对象,由于关联的存在 ,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了1 +n条 。 2)多对一  ,在多的这方,通

牛儿吃草的博客 1323

如何解决Hibernate N+1问题

问题】什么时候会遇到N+1的问题?【备注】Hibernate默认抓取策略是fetch="select",不是fetch="join",这都是为了延迟加载而准备的。【出现情况】 1)一对多(one-to-many) ,在1的这方,通过1条sql查找得到了1个对象,由于关联的存在 ,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了 1 +n条 。...

dongruan00的专栏 182
上一篇: Java Cache 入门
下一篇: Java8 Stream学习
ZeralZhang
博客等级 码龄9年 1粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值