迭代Hibernate子集合触发save后抛出ConcurrentModificationException如何解决?
问题原因
你遇到的ConcurrentModificationException本质是Hibernate持久化集合的特性导致的:
- Parent的
child集合是Hibernate的AbstractPersistentCollection代理对象,它会跟踪集合的结构变化。 - 你在遍历这个集合的forEach循环里,同步触发了事件监听器,监听器中的
childRepository.save(child)会触发Hibernate持久化上下文的刷新,间接修改了这个集合的内部状态(比如更新集合版本、重新加载元素)。 - 集合的迭代器在遍历下一个元素时,检测到集合结构和迭代启动时不一致,就抛出了并发修改异常。
解决方案
方案1:将持久化集合转为普通Java集合遍历
把Hibernate代理的集合转成普通的ArrayList/HashSet,这样遍历的是脱离Hibernate控制的独立集合,后续原持久化集合的修改不会影响当前迭代:
// 先复制为普通List,避免迭代Hibernate的持久化集合 new ArrayList<>(parent.getChild()).forEach(child -> { if (child.getStatus().equals("PENDING")) { applicationEventPublisher.publishEvent(new NewEvent(child)); publishLifeCycleEvent(deal); } });
这个方案简单直接,不需要额外配置,适合需要同步执行监听器逻辑的场景。
方案2:异步执行事件监听器
让事件监听器在独立线程中执行,这样遍历过程不会等待监听器完成,也就不会在迭代时触发集合的修改:
- 先在Spring配置类上开启异步支持:
@EnableAsync @Configuration public class AsyncConfig { // 可根据需求自定义线程池配置 }
- 给事件监听器添加
@Async注解:
@Async @EventListener public void saveChildEvent(NewEvent childEvent) { Child child = childEvent.getChild(); //...do processing childRepository.save(child); }
注意:异步场景下需要确保事务正确性,可给监听器方法添加@Transactional注解,避免事务上下文丢失。
注意事项
- Hibernate持久化集合与Session绑定,迭代过程中尽量避免触发集合的结构修改或刷新操作。
- 如果使用异步方案,要考虑线程安全和事务边界问题,比如避免在异步方法中操作同一个Session实例。
内容的提问来源于stack exchange,提问作者Lolly
相关产品推荐
相关产品推荐

