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

Hibernate多对多关联删除报ConcurrentModificationException异常如何解决

问题根因

你遇到的java.util.ConcurrentModificationException异常由两个核心问题导致:

  1. 增强for循环底层依赖集合迭代器实现,你在循环遍历course.getEstudiantes()的过程中,直接调用removeStudent方法修改了原集合的结构,迭代器检测到集合修改次数和预期不一致,就会抛出并发修改异常。
  2. 你的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:24:04