Spring Boot 项目中常用的 ORM 框架 (JPA/Hibernate) 在性能方面有哪些需要注意的点?
在 Spring Boot 项目中使用 JPA (Java Persistence API) / Hibernate (作为 JPA 的默认实现) 时,性能是一个非常关键的考量点。虽然 ORM 极大地简化了数据库交互,但如果不注意,很容易引入性能瓶颈。以下是一些关键的性能注意事项:
-
N+1 查询问题 (N+1 Select Problem)
- 问题描述: 这是最常见也是最严重的性能问题之一。当你查询一个实体列表(1次查询),然后在循环中访问每个实体的延迟加载(Lazy Loaded)关联对象时,会为每个实体额外触发一次(或多次)查询(N次查询)。
- 解决方案:
- Fetch Joins (JPQL/HQL): 在 JPQL 查询中使用
JOIN FETCH
来显式指定在初始查询中就加载关联对象。SELECT DISTINCT e FROM Employee e JOIN FETCH e.department
- Entity Graphs (
@EntityGraph
): 使用 JPA 2.1 引入的@EntityGraph
注解(或动态创建 EntityGraph),可以在运行时或编译时定义需要一起加载的关联属性图。这比JOIN FETCH
更灵活,尤其是在CrudRepository
接口方法上使用时。 - Batch Fetching (
@BatchSize
): 在关联属性或实体类上使用 Hibernate 的@BatchSize(size=N)
注解。这不会消除 N 次查询,但会将它们分批执行(例如,一次查询加载 N 个关联对象),显著减少查询次数。可以在application.properties
/yml
中全局配置spring.jpa.properties.hibernate.default_batch_fetch_size=...
。 - Subselect Fetching (
@Fetch(FetchMode.SUBSELECT)
): 对于集合关联,使用 Hibernate 的@Fetch(FetchMode.SUBSELECT)
。它会在加载主实体列表后,通过一个子查询(WHERE main_entity_id IN (...)
)一次性加载所有关联的集合。
- Fetch Joins (JPQL/HQL): 在 JPQL 查询中使用
-
加载策略 (Fetching Strategies: Eager vs. Lazy)
- 问题描述:
- Eager Loading (急加载): 如果关联设置为
FetchType.EAGER
(@ManyToOne
,@OneToOne
的默认值),关联对象会随主实体一起加载,即使你当前不需要它们。这可能导致查询加载过多不必要的数据,拖慢速度并消耗更多内存。 - Lazy Loading (懒加载): 如果设置为
FetchType.LAZY
(@OneToMany
,@ManyToMany
的默认值),关联对象只在首次访问时才加载。这通常更好,但需要注意 N+1 问题,并且如果在事务关闭后访问懒加载属性,会抛出LazyInitializationException
。
- Eager Loading (急加载): 如果关联设置为
- 建议:
- 优先使用 Lazy Loading: 对于绝大多数关联,尤其是集合关联,坚持使用
FetchType.LAZY
。 - 按需加载: 结合前面提到的 Fetch Joins, Entity Graphs 或 Batch Fetching 来解决特定场景下需要预先加载数据的问题。
- 理解默认值: 注意
@ManyToOne
和@OneToOne
默认是 Eager,通常建议显式改为fetch = FetchType.LAZY
。
- 优先使用 Lazy Loading: 对于绝大多数关联,尤其是集合关联,坚持使用
- 问题描述:
-
投影 (Projections)
- 问题描述: 经常只需要查询实体的一部分字段,但默认情况下
findById
,findAll
等方法会加载整个实体及其所有(非懒加载)属性,这可能涉及很多不必要的列和数据传输。 - 解决方案:
- DTO 投影 (JPQL/HQL): 使用 JPQL 的构造函数表达式
SELECT new com.example.MyDTO(e.id, e.name) FROM Employee e WHERE ...
直接将查询结果映射到 DTO。 - 接口投影 (Spring Data JPA): 定义一个只包含所需 getter 方法的接口,Spring Data JPA 会自动实现它,只查询对应的列。
- Specification / Criteria API: 使用 JPA Criteria API 或 Spring Data JPA Specifications 构建查询时,可以指定只选择特定的列。
- Native Queries: 如果需要非常精细的控制或复杂的 SQL,可以使用原生 SQL 查询并映射结果。
- DTO 投影 (JPQL/HQL): 使用 JPQL 的构造函数表达式
- 问题描述: 经常只需要查询实体的一部分字段,但默认情况下
-
缓存 (Caching)
- 一级缓存 (Session Cache / Persistence Context Cache):
- 作用: 在同一个 Hibernate Session(通常对应一个事务)内有效。对于通过 ID 加载的实体,如果 Session 缓存中已存在,则直接返回缓存中的对象,避免重复查询数据库。对实体的修改也会在缓存中进行,最后通过 Flush 操作同步到数据库。
- 注意: 它的生命周期与 Session/Transaction 绑定,无法跨事务共享。需要理解其工作原理,避免因 Session 过大导致内存问题(见下一条)。
- 二级缓存 (Second-Level Cache / Shared Cache):
- 作用: 跨 Session/Transaction 共享的缓存,可以缓存实体数据、集合 ID 等。对于经常读取且不经常修改的数据(如配置信息、基础数据)非常有效,可以显著减少数据库负载。
- 配置: 需要显式启用和配置(选择缓存提供商如 EhCache, Caffeine, Redis 等),并在实体或属性上使用
@Cacheable
等注解。 - 注意: 会增加应用复杂性(缓存同步、失效策略),可能遇到脏数据问题。需要仔细评估是否需要以及如何配置。
- 查询缓存 (Query Cache):
- 作用: 缓存 JPQL/HQL 查询的结果集。当执行相同的查询(包括参数)时,可以直接从缓存返回结果。
- 配置: 需要显式启用,并为需要缓存的查询设置
query.setHint("org.hibernate.cacheable", true);
。 - 注意: 查询缓存的失效比较复杂,当涉及的任何表发生更改时,相关的查询缓存项通常会失效。适用于结果集相对稳定且查询开销大的场景。
- 一级缓存 (Session Cache / Persistence Context Cache):
-
会话管理 (Session Management)
- 问题描述: 在单个事务(或 Session)中加载和管理过多的实体对象会消耗大量内存,并且在事务提交(Flush)时,Hibernate 需要对所有受管(Managed)状态的实体进行脏检查(Dirty Checking),这可能非常耗时。
- 解决方案:
- 保持事务简短: 尽量让事务覆盖最小必要的操作范围。
- 分页查询: 对于大量数据的列表,务必使用分页(Spring Data JPA 的
Pageable
)。 - 定期 Flush 和 Clear: 在处理大量数据的批处理任务中,可以手动调用
entityManager.flush()
将变更同步到数据库,然后调用entityManager.clear()
清除持久化上下文(一级缓存),释放内存,让后续加载的对象重新被管理。 - 只读事务: 对于纯读取操作,使用
@Transactional(readOnly = true)
。这可以给数据库和 Hibernate 一些优化提示(例如,Hibernate 可能禁用脏检查,数据库可能使用更优的锁策略)。 - Stateless Session (Hibernate Specific): 对于纯粹的、无状态的批量插入/更新/删除操作,可以考虑使用 Hibernate 的
StatelessSession
,它没有一级缓存和脏检查,性能更高,但功能受限。
-
批量操作 (Batch Operations)
- 问题描述: 逐条插入、更新或删除大量数据会导致大量的数据库交互和网络往返,效率低下。
- 解决方案:
- JDBC Batching: 配置 Hibernate 启用 JDBC 批处理。在
application.properties
/yml
中设置spring.jpa.properties.hibernate.jdbc.batch_size=...
(例如 20-50)。Hibernate 会将相同类型的 DML 语句分组,一次性发送给数据库。 - 设置
order_inserts
和order_updates
: 设置spring.jpa.properties.hibernate.order_inserts=true
和spring.jpa.properties.hibernate.order_updates=true
可以让 Hibernate 对 DML 语句按表排序后再进行批处理,进一步提高效率(尤其是在有外键约束时)。 - 对于非常大的批量操作: 可能需要考虑使用 JPA 本身不太擅长的更底层技术,如直接使用
JdbcTemplate
的批处理,或者数据库特定的批量加载工具。
- JDBC Batching: 配置 Hibernate 启用 JDBC 批处理。在
-
查询优化与索引
- 分析生成的 SQL: 开启 Hibernate 的 SQL 日志 (
spring.jpa.show-sql=true
,spring.jpa.properties.hibernate.format_sql=true
) 或使用 p6spy 等工具,检查 ORM 生成的 SQL 是否符合预期,是否高效。 - 数据库索引: 确保数据库表有合适的索引,特别是针对查询条件(WHERE 子句)、连接条件(JOIN ON)和排序字段(ORDER BY)。这是数据库层面的优化,但对 ORM 性能至关重要。
- 避免笛卡尔积: 在关联查询中要小心,确保使用了正确的连接条件,避免产生不必要的笛卡尔积。
- 优化 JPQL/Criteria 查询: 编写高效的 JPQL 或 Criteria API 查询,避免不必要的子查询或复杂的逻辑。有时原生 SQL (Native Query) 可能更优,但会牺牲可移植性。
- 分析生成的 SQL: 开启 Hibernate 的 SQL 日志 (
-
映射设计
- 选择合适的继承策略:
@Inheritance
策略(SINGLE_TABLE
,JOINED
,TABLE_PER_CLASS
)对性能有不同影响。SINGLE_TABLE
查询快但可能浪费空间且不利于非空约束;JOINED
规范但查询涉及连接;TABLE_PER_CLASS
查询复杂(UNION ALL)。根据实际情况权衡。 - 避免不必要的 LOB 加载: 懒加载 LOB (
@Lob
,@Basic(fetch = FetchType.LAZY)
) 类型字段,除非确实需要。 - 集合映射: 使用
@OrderColumn
来维护 List 顺序会带来额外的更新开销。如果不需要严格的数据库层面排序,可考虑使用@OrderBy
(在加载时排序)或在 Java 代码中排序。
- 选择合适的继承策略:
-
配置调优
- 连接池: Spring Boot 默认使用 HikariCP,通常性能很好。根据应用负载调整连接池大小(
spring.datasource.hikari.maximum-pool-size
等)。 - JDBC Fetch Size:
spring.jpa.properties.hibernate.jdbc.fetch_size
可以控制ResultSet
一次从数据库获取多少行数据到 JDBC Driver,适当调整可能对大数据量查询有帮助。
- 连接池: Spring Boot 默认使用 HikariCP,通常性能很好。根据应用负载调整连接池大小(
-
监控与分析
- 使用监控工具: 利用 Spring Boot Actuator 的 metrics 端点、JMX、或者 APM 工具(如 SkyWalking, Pinpoint, Dynatrace, New Relic)来监控数据库交互时间、查询频率、缓存命中率等。
- Hibernate Statistics: 启用 Hibernate 统计信息 (
spring.jpa.properties.hibernate.generate_statistics=true
) 可以提供关于 Session、缓存、查询等方面的详细性能数据,有助于定位瓶颈。
总结:
JPA/Hibernate 提供了强大的功能,但也隐藏了许多性能陷阱。关键在于理解其工作原理,特别是懒加载、N+1 问题、缓存机制和会话管理。通过合理的配置、优化的查询编写(JPQL/EntityGraph/Projections)、有效的缓存策略以及必要的监控和分析,可以在享受 ORM 便利的同时,构建出高性能的 Spring Boot 应用。我们要明白一点,没有银弹,需要根据具体的业务场景和数据特点进行权衡和优化。
相关文章:
Spring Boot 项目中常用的 ORM 框架 (JPA/Hibernate) 在性能方面有哪些需要注意的点?
在 Spring Boot 项目中使用 JPA (Java Persistence API) / Hibernate (作为 JPA 的默认实现) 时,性能是一个非常关键的考量点。虽然 ORM 极大地简化了数据库交互,但如果不注意,很容易引入性能瓶颈。以下是一些关键的性能注意事项:…...
基于大模型的大肠癌全流程预测与诊疗方案研究报告
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、大模型技术概述 2.1 大模型原理与架构 2.2 大模型在医疗领域的应用现状 三、术前风险预测与准备 3.1 术前风险预测指标 3.2 大模型预测方法与结果 3.3 基于预测结果的术前准备方案 四、术中风险预测与应…...
解决DeepSeek部署难题:提升效率与稳定性的关键策略
DeepSeek 部署中常见问题及对应解决方案 随着大模型技术的快速发展,DeepSeek 作为国内领先的大语言模型之一,广泛应用于自然语言处理、智能客服、内容生成等多个领域。 然而,在实际部署过程中,许多开发者和企业会遇到一系列挑战&a…...

AI进行提问、改写、生图、联网搜索资料,嘎嘎方便!
极客侧边栏-AI板块 目前插件内已接入DeepSeek-R1满血版、Qwen3满血版 、豆包/智谱最新发布的推理模型以及各种顶尖AI大模型,并且目前全都可以免费不限次数使用,秒回不卡顿,联网效果超好! 相比于市面上很多AI产品,极客…...

GStreamer开发笔记(四):ubuntu搭建GStreamer基础开发环境以及基础Demo
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/147714800 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、O…...

2021年认证杯SPSSPRO杯数学建模A题(第二阶段)医学图像的配准全过程文档及程序
2021年认证杯SPSSPRO杯数学建模 A题 医学图像的配准 原题再现: 图像的配准是图像处理领域中的一个典型问题和技术难点,其目的在于比较或融合同一对象在不同条件下获取的图像。例如为了更好地综合多种信息来辨识不同组织或病变,医生可能使用…...

CV中常用Backbone-3:Clip/SAM原理以及代码操作
前面已经介绍了简单的视觉编码器,这里主要介绍多模态中使用比较多的两种backbone:1、Clip;2、SAM。对于这两个backbone简单介绍基本原理,主要是讨论使用这个backbone。 1、CV中常用Backbone-2:ConvNeXt模型详解 2、CV中…...

RPC 协议详解、案例分析与应用场景
一、RPC 协议原理详解 RPC 协议的核心目标是让开发者像调用本地函数一样调用远程服务,其实现过程涉及多个关键组件与流程。 (一)核心组件 客户端(Client):发起远程过程调用的一方,它并不关心调…...

dify-plugin-daemon的.env配置文件
源码位置:dify-plugin-daemon\.env 本文使用dify-plugin-daemon v0.1.0版本,主要总结了dify-plugin-daemon\.env配置文件。为了本地调试方便,采用本地运行时环境WSL2Ubuntu22.04方式运行dify-plugin-daemon服务。 一.服务器基本配置 服务器…...
【Python】开发工具uv
文章目录 1. uv install1.1 下载安装脚本来安装1.2 使用pipx安装uv1.3 补充 2. 考虑在离线系统上安装uv2.1 下载并上传安装包2.2 用户级安装uv(~/.local/bin/)2.3 补充 3. uv 管理Python解释器4. uv 管理依赖5. uv运行代码5.1 uv不在项目下执行脚本5.2 u…...
《技术择时,价值择股》速读笔记
文章目录 书籍信息概览技术择时价值择股投资策略投资心态 书籍信息 书名:《技术择时,价值择股:A股投资实战笔记》 作者:二十八画生 概览 技术择时 三种简单方法,教你买在起涨点 趋势行情中的“买点”判断ÿ…...
Python可视化设计原则
在数据驱动的时代,可视化不仅是结果的呈现方式,更是数据故事的核心载体。Python凭借其丰富的生态库(Matplotlib/Seaborn/Plotly等),已成为数据可视化领域的主力工具。但工具只是起点,真正让图表产生价值的&…...
SAP重塑云ERP应用套件
在2025年Sapphire大会上,SAP正式发布了其云ERP产品的重塑计划,推出全新“Business Suite”应用套件,并对供应链相关应用进行AI增强升级。这一变革旨在简化新客户进入SAP生态系统的流程,同时为现有客户提供更加统一、智能和高效的业…...
2025.5.25总结
今天早上刷了会手机,然后下午去刷了一道科目一,限时训练3.5h。遗憾的是,这周只刷了一道题,并没有达成每周两道的目标。 其次,一天下来跟平时的节假日一样,有些小压抑。我也察觉到了自己的情绪。烦心事无非…...

(九)PMSM驱动控制学习---无感控制之高阶滑膜观测器
在之前的文章中,我们介绍了永磁同步电机无感控制中的滑模观测器,但是同时我们也认识到了他的缺点:因符号函数带来的高频切换分量,使用低通滤波器引发相位延迟;在本篇文章,我们将会介绍高阶滑模观测器的无感…...
6个跨境电商独立站平台
1. WP最主题(WPZUI) 官网:http://www.wpzui.com 简介: WP最主题专注于专业WordPress主题开发定制,致力于为用户提供高质量、高性能的WordPress主题。其主题设计注重用户体验和SEO优化,适用于多种网站类型,包括企业站…...
电子电路:电学都有哪些核心概念?
电子是基本粒子,带负电荷。电荷是物质的一种属性,电子带有负电荷,而质子带有正电荷。电荷的单位是库仑。 电流呢,应该是指电荷的流动,单位是安培,也就是库仑每秒。所以电流其实就是电荷在导体中的移动形成的。比如,当电子在导线中流动时,就形成了电流。不过要注意,传…...
SQL进阶之旅 Day 2:基础查询优化技巧
【SQL进阶之旅 Day 2】基础查询优化技巧 开篇:为什么需要基础查询优化? 在SQL学习的旅程中,掌握基础查询优化是迈向专业数据库开发的关键一步。随着数据量的爆炸式增长,简单的SELECT语句已经无法满足现代应用对性能的要求。今天…...
时序数据库 TDengine × Superset:一键构建你的可视化分析系统
如果你正在用 TDengine 管理时序数据,写 SQL 查询没问题,但一到展示环节就犯难——图表太基础,交互不够,甚至连团队都看不懂你辛苦分析的数据成果?别担心,今天要介绍的这个组合,正是为你量身打造…...
一键化部署
好的,我明白了。你希望脚本变得更简洁,主要负责: 代码克隆:从 GitHub 克隆你的后端和前端项目,并在克隆前确保目标目录为空。文件复制:将你预先准备好的 Dockerfile (后端和前端各一个)、前端的 nginx.con…...
Win 系统 conda 如何配置镜像源
通过命令添加镜像源(推荐) 以 清华源 为例,依次执行以下命令: # 添加主镜像源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main # 添加免费开源镜像源 conda config --add channels http…...

Devicenet主转Profinet网关助力改造焊接机器人系统智能升级
某汽车零部件焊接车间原有6台焊接机器人(采用Devicenet协议)需与新增的西门子S7-1200 PLC(Profinet协议)组网。若更换所有机器人控制器或上位机系统,成本过高且停产周期长。 《解决方案》 工程师选择稳联技术转换网关…...

《STL--list的使用及其底层实现》
引言: 上次我们学习了容器vector的使用及其底层实现,今天我们再来学习一个容器list, 这里的list可以参考我们之前实现的单链表,但是这里的list是双向循环带头链表,下面我们就开始list的学习了。 一:list的…...
whisper相关的开源项目 (asr)
基于 Whisper(OpenAI 的开源语音识别模型)的开源项目有很多,涵盖了不同应用场景和优化方向。以下是一些值得关注的项目: 1. 核心工具 & 增强版 Whisper OpenAI Whisper 由 OpenAI 开源的通用语音识别模型,支持多语…...

python的pip怎么配置的国内镜像
以下是配置pip国内镜像源的详细方法: 常用国内镜像源列表 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple阿里云:https://mirrors.aliyun.com/pypi/simple中科大:https://pypi.mirrors.ustc.edu.cn/simple华为云࿱…...

PCB 通孔是电容性的,但不一定是电容器
哼?……这是什么意思?…… 多年来,流行的观点是 PCB 通孔本质上是电容性的,因此可以用集总电容器进行建模。虽然当信号的上升时间大于或等于过孔不连续性延迟的 3 倍时,这可能是正确的,但我将向您展示为什…...
领域驱动设计与COLA框架:从理论到实践的落地之路
目录 引言 DDD核心概念 什么是领域驱动设计 DDD的核心概念 1. 统一语言(Ubiquitous Language) 2. 限界上下文(Bounded Context) 3. 实体(Entity)与值对象(Value Object) 4. 聚…...

公有云AWS基础架构与核心服务:从概念到实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 (初学者技术专栏) 一、基础概念 定义:AWS(Amazon Web Services)是亚马逊提供的云计算服务&a…...

Python60日基础学习打卡D35
import torch import torch.nn as nn import torch.optim as optim from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler import time import matplotlib.pyplot as plt# 设置GPU设…...
Python经典算法实战
在编程的世界里,算法是解决问题的灵魂,而Python以其简洁优雅的语法成为实现算法的理想语言。无论你是初学者还是有一定经验的开发者,《Python经典算法实战》都能带你深入算法的殿堂,从理论到实践,一步步构建起扎实的编…...