You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot3升级后Hibernate6批量语句排序警告与内存溢出问题

问题描述

将Spring Boot 2升级至Spring Boot 3(内部从Hibernate 5切换到Hibernate 6)后,遇到两个问题:

  1. 持久化过程中出现从未在Hibernate 5中见过的警告:

The batch containing 4590 statements could not be sorted. This might indicate a circular entity relationship.

  1. 执行定时任务时发生Java堆内存溢出(OutOfMemoryError),目前无法复现。该定时任务包含获取数据表、检查JSON文件生成状态及EntityManager刷写操作。

实体结构为深度嵌套:

  • Datasheet包含Diagram列表,Diagram通过@OneToMany(cascade = CascadeType.ALL)和@OrderBy("index")关联Curve列表
  • Curve同样通过@OneToMany(cascade = CascadeType.ALL)和@OrderBy("index")关联Coordinate列表,所有对象在同一事务中保存
  • 单个Diagram通常包含1-10个Curve,每个Curve有5000+坐标点

内存溢出错误日志:

14:47:08 INFO  c.d.c.service.AuthenticationService - getCurrentUsername returned: Dhyani
14:47:09 INFO  ParserService - Parsing took 823 ms
14:47:09 WARN  ParserService - Input file parsed with 0 errors.
14:47:09 INFO  ParserService - Creating logs took 9 ms
14:47:09 WARN  o.hibernate.engine.spi.ActionQueue - The batch containing 3685 statements could not be sorted. This might indicate a circular entity relationship.
14:47:12 INFO  c.d.core.mail.service.MailService - Send 'Datasheet Update Notification' to Datasheet Owner.
14:47:12 WARN  o.t.s.p.AbstractStandardFragmentInsertionTagProcessor - [THYMELEAF][http-nio-8080-exec-6][mail-template] Deprecated unwrapped fragment expression "__${topContent}__" found in template mail-template, line 20, col 18. Please use the complete syntax of fragment expressions instead ("~{__${topContent}__}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
14:47:13 WARN  o.t.s.p.AbstractStandardFragmentInsertionTagProcessor - [THYMELEAF][http-nio-8080-exec-6][approval-flow-mails] Deprecated unwrapped fragment expression "this :: ds-name-and-revision" found in template approval-flow-mails, line 69, col 30. Please use the complete syntax of fragment expressions instead ("~{this :: ds-name-and-revision}"). The old, unwrapped syntax for fragment expressions will be removed in future versions of Thymeleaf.
14:47:13 INFO  c.d.core.mail.service.MailConnector - Sending Mail from R-test-ePowerDatashe@test.com to asdf@test.com; subject: ePower 2.0 Message: Your Datasheet was changed
14:47:16 INFO  c.d.core.mail.service.MailService - Mail sent!
14:47:16 INFO  c.d.c.s.s.AppUserDetailsService - numActiveUsers: 1 for username: kobayahi
14:47:16 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:16 INFO  c.d.c.service.AuthenticationService - getCurrentUsername returned: KOBAYAHI
14:47:16 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:16 INFO  c.d.c.service.AuthenticationService - Auth with currentUsername
14:47:17 INFO  c.d.c.s.s.AppUserDetailsService - numActiveUsers: 1 for username: vielemem
14:47:17 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:17 INFO  c.d.c.service.AuthenticationService - getCurrentUsername returned: VIELEMEM
14:47:17 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:17 INFO  c.d.c.service.AuthenticationService - Auth with currentUsername
14:47:18 INFO  c.d.c.s.s.AppUserDetailsService - numActiveUsers: 1 for username: torremar
14:47:18 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:18 INFO  c.d.c.service.AuthenticationService - getCurrentUsername returned: TORREMAR
14:47:18 INFO  c.d.c.service.AuthenticationService - null:productive
14:47:18 INFO  c.d.c.service.AuthenticationService - Auth with currentUsername
14:47:18 WARN  org.apache.pdfbox.cos.COSDocument - Warning: You did not close a PDF Document
14:49:39 ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task
java.lang.OutOfMemoryError: Java heap space
14:49:39 WARN  com.zaxxer.hikari.pool.HikariPool - HikariPool-2 - Thread starvation or clock leap detected (housekeeper delta=2m13s625ms839µs60ns).
14:49:39 ERROR o.a.coyote.http11.Http11NioProtocol - Error processing async timeouts
java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Java heap space
    at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
    at org.apache.coyote.AbstractProtocol.startAsyncTimeout(AbstractProtocol.java:660)
    at org.apache.coyote.AbstractProtocol.lambda$start$0(AbstractProtocol.java:646)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
    at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
    at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.base/java.util.concurrent.ConcurrentHashMap$KeySetView.iterator(ConcurrentHashMap.java:4635)
    at org.apache.coyote.AbstractProtocol.lambda$startAsyncTimeout$1(AbstractProtocol.java:667)
    at org.apache.coyote.AbstractProtocol$$Lambda$2323/0x00007f64f0f66240.run(Unknown Source)
    ... 7 common frames omitted
14:49:39 INFO  c.d.epower.service.DatasheetService - ExportJsonScheduler started

咨询点:

  • 为何Hibernate 6会记录该排序警告而Hibernate 5不会?
  • 该警告是否会引发堆内存溢出问题?

问题解答

1. 为什么Hibernate 6出现排序警告而Hibernate 5没有?

Hibernate 6对批量语句排序的逻辑做了优化和严格性提升:

  • Hibernate 5的批量语句排序逻辑相对宽松,即使存在循环依赖或者无法排序的语句,也不会主动抛出警告,只会默默跳过排序或者采用默认顺序执行。
  • Hibernate 6增强了对批量操作中语句依赖关系的检测,当它尝试对批量SQL语句进行排序以优化执行顺序(比如先插入父实体再插入子实体)时,如果发现存在无法解析的依赖循环(或者因为@OrderBy导致的排序依赖冲突),就会抛出这个警告。

你的场景中,虽然实体结构看起来是单向嵌套,但@OrderBy注解可能让Hibernate在处理批量插入时,对实体的保存顺序产生额外的依赖判断,加上批量操作规模极大(单个Curve有5000+坐标点),导致Hibernate无法完成语句排序,从而触发警告。

2. 该警告是否会引发堆内存溢出?

这个警告本身不会直接导致堆内存溢出,但它背后的批量处理逻辑变化,可能和OOM问题存在关联:

  • Hibernate 6在处理无法排序的批量语句时,可能会放弃批量优化,改为逐个执行语句,或者在内存中保留更多的语句元数据和实体状态,这会增加内存开销。
  • 你的场景中,单个事务要保存大量Coordinate实体,Hibernate 6的一级缓存会加载所有这些实体,加上批量处理效率降低,内存占用会比Hibernate 5更高,最终触发OOM。

从日志时间线来看,警告出现在持久化阶段,OOM发生在后续定时任务执行时,两者时间接近,说明批量处理的内存开销是OOM的诱因之一。

优化建议

针对你的场景,建议做以下优化来解决警告和OOM问题:

  • 拆分大事务:不要在单个事务中保存所有嵌套实体,分批次保存Curve和Coordinate,减少单次事务的内存占用。
  • 调整级联范围:如果某些子实体不需要全量级联操作,缩小CascadeType的范围,避免一次性加载所有关联实体。
  • 配置批量插入参数:设置spring.jpa.properties.hibernate.jdbc.batch_size为合适值(比如500),同时开启hibernate.order_inserts=true和hibernate.order_updates=true,帮助Hibernate更好地处理批量语句排序。
  • 采用延迟加载或DTO投影:在定时任务中,若不需要完整实体对象,使用DTO投影只加载需要的字段,减少内存占用。
  • 排查隐藏循环依赖:仔细检查实体关系,确认是否存在未配置mappedBy的双向关联,这可能导致Hibernate误判为循环依赖。

内容的提问来源于stack exchange,提问作者zFr3eak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 03:24:55