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

如何在Hibernate与Spring Boot中实现双向自引用实体的级联增删?

双向自引用实体级联CRUD异常

问题现象

  • 配置CascadeType.ALL时,实体可正常创建,但删除操作无效果;
  • 配置CascadeType.REMOVE时,实体可正常删除,但创建时无法级联保存子实体;
  • 必须使用FetchType.EAGER,删除前需校验所有字段,必须加载全部子字段。

使用环境

Spring 3、PostgreSQL


实体代码

@Entity
@Getter
@Setter
@Table(name = "field")
public class Field {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    @JdbcTypeCode(SqlTypes.BIGINT)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToOne
    @JoinColumn(name = "parent_field_id")
    private Field parentField;

    @OneToMany(mappedBy = "parentField", fetch = FetchType.EAGER, cascade =  CascadeType., orphanRemoval = true)
    private Set<Field> childFields = new HashSet<>();

}

测试代码

final Field firstLevel = createField("firstLevel");
final Field secondLevel = createField("secondLevel");
secondLevel.setParentField(firstLevel);
firstLevel.getChildFields().add(secondLevel);

final Field thirdLevel = createField("thirdLevel");
thirdLevel.setParentField(secondLevel);
secondLevel.getChildFields().add(thirdLevel);
fieldRepo.save(firstLevel);

// 校验子字段是否已保存
final Field filedWithChild = fieldRepo.findById(1L).get();
filedWithChild.getChildFields().forEach(field -> Assert.isTrue(field.getId() != null));

// 尝试删除二级字段
log.info("Prepare to delete");
fieldRepo.deleteById(2L);
final Optional<Field> byId = fieldRepo.findById(2L);
Assert.isTrue(byId.isEmpty(),"Second level was not removed");

删除操作日志(未生成SQL删除语句)

2023-05-19T10:56:55.109+05:00  INFO 31018 --- [  restartedMain] c.e.springbatchtest.app.SpringBatch      : Prepare to delete
2023-05-19T10:56:55.110+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select f1_0.id,c1_0.parent_field_id,c1_0.id,c1_0.name,f1_0.name,p1_0.id,p1_0.name,p1_0.parent_field_id from field f1_0 left join field c1_0 on f1_0.id=c1_0.parent_field_id left join field p1_0 on p1_0.id=f1_0.parent_field_id where f1_0.id=?
Hibernate: select f1_0.id,c1_0.parent_field_id,c1_0.id,c1_0.name,f1_0.name,p1_0.id,p1_0.name,p1_0.parent_field_id from field f1_0 left join field c1_0 on f1_0.id=c1_0.parent_field_id left join field p1_0 on p1_0.id=f1_0.parent_field_id where f1_0.id=?
2023-05-19T10:56:55.112+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
Hibernate: select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
2023-05-19T10:56:55.114+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
Hibernate: select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
2023-05-19T10:56:55.120+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select f1_0.id,c1_0.parent_field_id,c1_0.id,c1_0.name,f1_0.name,p1_0.id,p1_0.name,p1_0.parent_field_id from field f1_0 left join field c1_0 on f1_0.id=c1_0.parent_field_id left join field p1_0 on p1_0.id=f1_0.parent_field_id where f1_0.id=?
Hibernate: select f1_0.id,c1_0.parent_field_id,c1_0.id,c1_0.name,f1_0.name,p1_0.id,p1_0.name,p1_0.parent_field_id from field f1_0 left join field c1_0 on f1_0.id=c1_0.parent_field_id left join field p1_0 on p1_0.id=f1_0.parent_field_id where f1_0.id=?
2023-05-19T10:56:55.122+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
Hibernate: select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
2023-05-19T10:56:55.124+05:00 DEBUG 31018 --- [  restartedMain] org.hibernate.SQL                        : select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?
Hibernate: select c1_0.parent_field_id,c1_0.id,c1_0.name from field c1_0 where c1_0.parent_field_id=?

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 20:23:11