【问题标题】:ConcurrentModificationException upon committing transaction with Hibernate使用 Hibernate 提交事务时出现 ConcurrentModificationException
【发布时间】:2016-08-29 09:02:20
【问题描述】:

在我们的应用程序中,我们已从 Hibernate 3.5.6-final 升级到 4.2.21.Final,现在我们在提交数据库事务时收到 ConcurrentModificationException

java.util.ConcurrentModificationException: null
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:386)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:304)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:349)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1195)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)

这是 Hibernate 4.2 的一个已知问题吗?

【问题讨论】:

  • 你有没有使用实体事件监听器做一些自定义工作(pre persit 或 post persist)

标签: java hibernate


【解决方案1】:

这个异常原来是由我们使用的 Hibernate 自定义约束验证器的问题引起的。验证器的 isValid 正在运行 Hibernate 条件查询。该查询触发了 Hibernate 会话刷新,导致 ConcurrentModificationException。我们通过在 isValid 方法中暂时禁用自动刷新来解决此问题:

@Override
public boolean isValid(Object object, final ConstraintValidatorContext c) {
   try {
      sessionFactory.getCurrentSession().setFlushMode(FlushMode.MANUAL);
      ...
   } finally {
      sessionFactory.getCurrentSession().setFlushMode(FlushMode.AUTO);
   }
}

问题也可能表现为StackOverflowError

【讨论】:

    【解决方案2】:

    我在 hibernate 5.0.11 中遇到了这个问题,并在 5.2.5 中验证了它也发生了。我的解决方案是注释自定义验证器以打开新事务。

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    

    我想在自定义约束验证器易于设置和使用之前,hibernate 还有一段路要走,因为这花费了我更多的时间。

    编辑:问题相关。我认为使用相同的事务违反了 jpa2.1 规范https://hibernate.atlassian.net/browse/HHH-7537

    【讨论】:

      【解决方案3】:

      在实现 ConstraintValidator 的类中,我们需要一个 EntityManager 的实例,但我们不在 Spring 上下文中,以便自动实例化带有注释 @Autowired 的 EntityManager 对象。因此,在配置包中,我们可以编写一个工厂,它允许拥有一个 Application 实例,以便在我们不在 Spring 上下文中时实例化 bean。

      @Configuration
      public class ApplicationContextConf {
      
          @Bean
          public static ApplicationContextProvider contextProvider() {
              return new ApplicationContextProvider();
          }
      
      }
      


      @Component
      public class ApplicationContextProvider implements ApplicationContextAware {
      
          private static ApplicationContext context;
      
          public ApplicationContext getApplicationContext() {
              return context;
          }
      
          @Override
          public void setApplicationContext(final ApplicationContext ctx) {
              context = ctx;
          }
      }
      


      在实现 ConstraintValidator 的类中,由于之前在方法 initialize 中创建的工厂,我们实例化了 EntityManager。

      在调用调用存储库的方法之前,我们将 Hibernate 当前会话的刷新模式更改为 FlushMode.MANUAL 以避免在调用存储库后自动刷新,同时保持默认刷新模式。在finally块中,我们恢复之前保留的flush模式的默认值。

      private EntityManager entityManager;
      
      @Override
      public void initialize(final Object object ) {
          // ...
      
          try {
              this.entityManager = ApplicationContextConf
                      .contextProvider()
                          .getApplicationContext()
                          .getBean(EntityManager.class);
          }
          catch (final BeansException ex) {
          // ...
          }
      }
      
      @Override
      public boolean isValid(final Object object, final ConstraintValidatorContext context) {
          Session hibernateSession = null;
          FlushMode originalFlushMode = null;
      
          try {
              hibernateSession = this.entityManager.unwrap(Session.class);
              originalFlushMode = hibernateSession.getFlushMode();
              hibernateSession.setFlushMode(FlushMode.MANUAL);
      
              // ...
          }
          finally {
              if (hibernateSession != null) {
                  hibernateSession.setFlushMode(originalFlushMode);
              }
          }
      }
      

      【讨论】:

      • 你需要缩进你的代码(至少)4个空格来格式化它。
      • 请在您的回答中添加一些解释。
      猜你喜欢
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      • 1970-01-01
      • 2014-06-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多