Spring Boot中@OneToMany双向关联更新删除关联记录失效问题
问题根因分析
初始版本删除不生效的核心原因
- 缺少事务边界:初始Controller的
saveDoctorSpecialties方法未声明@Transactional,对关联集合的清空、实体更新操作没有在同一个事务内完成,JPA脏检查机制无法触发,自然不会生成DELETE语句。你原先写的循环调用doctorSpecialtiesService.delete的逻辑不生效,也大概率是delete方法没有加事务,操作没有被持久化到数据库。 - 未开启孤儿删除:Doctor实体的
@OneToMany注解仅配置了cascade = CascadeType.ALL,没有添加orphanRemoval = true属性,仅清空关联集合不会自动删除数据库中的关联记录,只有开启孤儿删除后,集合中移除的实体才会被JPA识别为待删除的孤儿数据。 - 中间表实体未重写equals和hashCode:你使用HashSet存储
DoctorSpecialties对象,但该实体没有重写equals和hashCode方法,默认基于对象内存地址判断相等,导致你测试时添加两个对象但仅执行一次插入的问题,HashSet认为两个对象重复被自动去重了。
后续修改后生效的原因
你在saveDelete方法上加了@Transactional注解,在事务内执行了关联集合的clear操作,结合已配置的CascadeType.ALL,事务提交时JPA检测到集合变更,就触发了关联记录的删除逻辑,同时也自动处理了关联的appointments和doctor_schedules外键置空的逻辑。
优化方案
如果要回到最初的写法让逻辑更简洁,按如下调整即可:
- 修改Doctor实体的关联配置,开启孤儿删除
@OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true) private Set<DoctorSpecialties> doctorSpecialties;
- 给
DoctorSpecialties实体重写equals和hashCode方法,避免HashSet去重异常
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DoctorSpecialties that = (DoctorSpecialties) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(id); }
- 给Controller的
saveDoctorSpecialties方法添加@Transactional注解,保证所有操作在同一个事务内,触发JPA脏检查
@PostMapping(value = "/saveSpecialties/{id}") @Transactional public String saveDoctorSpecialties(@RequestParam(required = false) String[] specialtiesId, @PathVariable Integer id, RedirectAttributes message) { // 原有逻辑不需要修改,即可正常执行关联删除和新增 }
内容的提问来源于stack exchange,提问作者DarkVaderM
相关产品推荐
相关产品推荐

