SpringMVC+Hibernate @ManyToMany更新时Controller丢失关联集合问题
问题根源分析
你的问题核心在于Spring MVC表单绑定过程中,原有关联的projects集合没有被保留,导致Hibernate认为你要清空所有旧关联,再添加新的。具体来说:
- 当你提交更新表单时,Spring MVC会创建一个新的
Employee实例(或根据表单数据重新绑定),这个实例里的projects是类初始化时的空HashSet(因为表单没有传递任何原有项目的信息)。 - 你在
saveOrUpdateEmployee里给这个空集合添加了一个新Project,然后调用saveOrUpdate。Hibernate对比数据库中原有Employee的关联集合,发现现在的集合和原来的完全不同(原来的有多个,现在只有新的一个),所以会先执行delete删除所有旧的中间表记录,再插入新的关联。
另外,我注意到你的Employee类里应该有一个临时的projectId字段(因为你用了theEmployee.getProjectId()),这个字段是用来接收表单选择的项目ID的,但它并没有参与多对多关联的绑定。
解决方案
最稳妥的修复方式是先从数据库加载原始的Employee对象,再修改它的关联集合,而不是直接使用表单绑定的对象。这样可以保留原有关联,避免不必要的删除操作。
步骤1:修改Controller的saveOrUpdateEmployee方法
@PostMapping("/saveEmployee") public String saveOrUpdateEmployee(@ModelAttribute("employee") Employee formEmployee) { // 1. 从数据库加载原始的Employee对象,保留原有关联 Employee existingEmployee = employeeService.getEmployee(formEmployee.getId()); // 2. 获取要添加的新项目 Project newProject = projectService.getProject(formEmployee.getProjectId()); // 3. 将新项目添加到原有集合中(避免覆盖原有关联) existingEmployee.getProjects().add(newProject); // 4. 保存更新后的对象 employeeService.saveEmployee(existingEmployee); return "redirect:/employees/employee/" + existingEmployee.getId(); }
步骤2:优化多对多关联的级联配置(可选但推荐)
你的Employee类中@ManyToMany使用了cascade=CascadeType.ALL,这意味着当你删除Employee时,会级联删除所有关联的Project,这通常不是业务上想要的行为。建议根据实际需求调整级联类型,比如只使用CascadeType.PERSIST和CascadeType.MERGE:
@ManyToMany(fetch= FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name="employee_project",joinColumns=@JoinColumn(name="employee_id"), inverseJoinColumns=@JoinColumn(name="project_id")) private Set<Project> projects = new HashSet<>();
步骤3:如果需要支持多选项目(可选)
如果你想让用户在表单中同时选择多个项目(包括保留原有项目),可以在表单中添加多选框,将原有项目的ID也提交回去:
<form:form action="/employee_manager/employees/saveEmployee" modelAttribute="employee" method="POST"> <form:hidden path="id"/> <!-- 显示所有项目的多选框,原有关联项目默认选中 --> <form:checkboxes path="projects" items="${allProjects}" itemValue="id" itemLabel="name"/> <input type="submit" value="Save" /> </div> </form:form>
为什么原来的代码会触发删除?
Hibernate的saveOrUpdate方法会对比对象的当前状态和数据库中的持久化状态:
- 当你使用表单绑定的
Employee对象时,它的projects集合是空的(除了你添加的新项目),而数据库中该Employee的projects集合有多个元素。 - Hibernate会认为你要移除所有旧的关联,只保留新添加的那个,所以会先执行
delete from employee_project where employee_id=?删除所有旧关联,再插入新的关联记录。
通过先加载原始对象,你就保留了原有关联集合的状态,只需要添加新元素,Hibernate只会插入新的中间表记录,不会删除旧的。
内容的提问来源于stack exchange,提问作者F.Burian
相关产品推荐
相关产品推荐

