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

Spring Boot 3.4升级后调用DeleteAll触发TransientObjectException问题

Spring Boot 3.4多对多关联删除循环级联问题分析与解决

问题背景

  • 升级Spring Boot 3.4后,集成测试因多对多关联删除逻辑失败,相同代码在Spring Boot 3.3环境下可正常运行
  • 模型包含DocumentationUnitDTO与ProcedureDTO,二者为双向多对多关联,关联表documentation_unit_procedure中存储DocumentationUnitDTO的排序字段
  • ProcedureDTO中对DocumentationUnitDTO的引用使用FetchType.EAGER加载,删除操作时触发了DocumentationUnitDTO -> ProcedureDTO -> DocumentationUnitDTO的循环级联调用,导致异常

已验证的临时缓解方法

  • 给ProcedureDTO中的多对多属性标记@Transient,删除操作恢复正常
  • 切换至Hibernate 6.6版本运行代码,删除逻辑可正常执行

问题根因

Spring Boot 3.4默认集成的Hibernate 6.5.x系列版本,在处理双向多对多关联+EAGER加载的删除逻辑时存在bug:删除DocumentationUnitDTO时,EAGER加载会立即初始化关联的ProcedureDTO实例,而ProcedureDTO的反向关联又会触发DocumentationUnitDTO的EAGER加载,形成循环依赖;同时Hibernate的级联删除逻辑在这种循环状态下出现了状态判断错误,导致删除操作失败。

而Hibernate 6.6版本已修复该双向多对多EAGER加载的循环处理bug,因此切换版本后问题消失。

正式解决方案

方案1:升级Hibernate至6.6+

直接指定Hibernate版本为6.6及以上,覆盖Spring Boot 3.4的默认依赖版本:

<!-- Maven 配置 -->
<dependency>
    <groupId>org.hibernate.orm</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>6.6.0.Final</version>
</dependency>
// Gradle 配置
implementation 'org.hibernate.orm:hibernate-core:6.6.0.Final'

此方案直接修复底层bug,无需修改业务代码,优先推荐。

方案2:调整关联加载策略为LAZY

将ProcedureDTO中的多对多关联加载类型从FetchType.EAGER改为FetchType.LAZY,避免删除时立即触发反向关联的加载,打破循环依赖:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
    name = "documentation_unit_procedure",
    inverseJoinColumns = @JoinColumn(name = "documentation_unit_id"),
    joinColumns = @JoinColumn(name = "procedure_id"))

注意:如果业务场景必须使用EAGER加载,此方案不适用

方案3:拆分双向多对多为显式中间表关联

由于关联表中存在自定义排序字段,推荐将双向多对多拆分为两个一对多关联,显式映射中间表实体,从根源避免循环加载问题:

  1. 创建中间表实体:
@Entity
@Table(name = "documentation_unit_procedure")
public class DocumentationUnitProcedureDTO {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "documentation_unit_id")
    private DocumentationUnitDTO documentationUnit;

    @ManyToOne
    @JoinColumn(name = "procedure_id")
    private ProcedureDTO procedure;

    private Integer sortOrder; // 排序字段

    // 省略getter、setter及构造方法
}
  1. 修改DocumentationUnitDTO的关联:
@OneToMany(mappedBy = "documentationUnit", cascade = CascadeType.ALL, orphanRemoval = true)
private List<DocumentationUnitProcedureDTO> procedureAssociations;
  1. 修改ProcedureDTO的关联:
@OneToMany(mappedBy = "procedure")
private List<DocumentationUnitProcedureDTO> documentationUnitAssociations;

这种方式不仅解决了循环级联问题,还能直接操作中间表的排序字段,更适配复杂关联场景的业务需求。

内容的提问来源于stack exchange,提问作者Ralph von Petersdorff-Campen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:27:08