Hibernate多对多关联删除报ConcurrentModificationException异常如何解决
问题根因
你遇到的java.util.ConcurrentModificationException异常由两个核心问题导致:
- 增强for循环底层依赖集合迭代器实现,你在循环遍历
course.getEstudiantes()的过程中,直接调用removeStudent方法修改了原集合的结构,迭代器检测到集合修改次数和预期不一致,就会抛出并发修改异常。 - 你的
Courses实体是多对多关系的被维护端(由mappedBy = "courses"指定,关系由Student侧的courses属性维护),直接删除课程不会自动清理中间表course_students的关联数据,必须先手动维护两边的关联关系。
可行解决方案
方案1:遍历集合拷贝避免并发修改
最简单的修复方式是先把关联学生集合拷贝到一个新的Set中,遍历新集合操作原集合,就不会触发迭代器的并发修改校验:
@Override public boolean deleteCourse(int id) { Session currentSession = entityManager.unwrap(Session.class); // 建议用get而不是load,load返回代理对象,未访问属性时不会初始化关联集合 Courses course = currentSession.get(Courses.class, id); // 重点:拷贝原集合到新的HashSet,遍历新集合不会影响原集合的迭代校验 for(Student student : new HashSet<>(course.getEstudiantes())) { course.removeStudent(student); } currentSession.delete(course); currentSession.flush(); // 强制把修改刷入数据库 // 不要判断id是否为null,Hibernate删除后不会自动清空实体id,用contains判断更准确 return !currentSession.contains(course); }
方案2:使用迭代器的remove方法操作
直接使用迭代器的内置remove方法删除元素,迭代器会感知修改,不会触发异常:
@Override public boolean deleteCourse(int id) { Session currentSession = entityManager.unwrap(Session.class); Courses course = currentSession.get(Courses.class, id); Iterator<Student> iterator = course.getEstudiantes().iterator(); while (iterator.hasNext()) { Student student = iterator.next(); student.getCourses().remove(course); // 先移除关系维护端的关联 iterator.remove(); // 用迭代器方法移除元素,不会触发并发修改 } currentSession.delete(course); currentSession.flush(); return !currentSession.contains(course); }
方案3:批量删除中间表关联(性能最优)
如果不需要在解除关联时做额外业务逻辑,直接用SQL批量删除中间表关联记录效率更高,不需要遍历所有关联学生:
@Override public boolean deleteCourse(int id) { Session currentSession = entityManager.unwrap(Session.class); // 第一步:批量删除中间表中当前课程的所有关联记录 Query deleteRelQuery = currentSession.createNativeQuery("DELETE FROM course_students WHERE courses_id = :courseId"); deleteRelQuery.setParameter("courseId", id); deleteRelQuery.executeUpdate(); // 第二步:删除课程本身 Courses course = currentSession.get(Courses.class, id); if (course != null) { currentSession.delete(course); currentSession.flush(); return !currentSession.contains(course); } return false; }
注意事项
- 请确保
Student类正确重写了equals和hashCode方法,否则HashSet的remove操作可能无法匹配到对应元素,导致关联解除失败。 - 多对多关系慎用
CascadeType.REMOVE级联删除,否则删除课程时会把关联的学生实体也一并删除,引发数据丢失。 - 原代码中判断
course.getId() == null的逻辑无效,Hibernate执行删除操作后不会主动清空实体的id属性,用Session的contains方法判断实体是否还在持久化上下文中更准确。
内容的提问来源于stack exchange,提问作者G-programmer
相关产品推荐
相关产品推荐

