如何在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
相关产品推荐
相关产品推荐

