使用Hibernate提交事务时出现ConcurrentModificationException异常

4

在我们的应用程序中,我们已经从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已知的问题?

3
你使用过实体事件监听器来执行某些自定义工作(在持久化前或持久化后)吗? - hunter
3个回答

8

异常原因是由我们使用的一个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

3

我曾在使用hibernate 5.0.11时遇到过这个问题,并且验证了它也会在5.2.5版本中发生。我的解决方案是对自定义验证器进行注释,以打开新的事务。

@Transactional(propagation=Propagation.REQUIRES_NEW)

我想Hibernate在设置和使用自定义约束验证器方面还有一些进步空间,因为这比应该花费的时间更多。

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


1
在实现ConstraintValidator的类中,我们需要一个EntityManager实例,但是我们不在Spring上下文中,无法使用@Autowired注释自动实例化EntityManager对象。因此,在配置包中,我们可以编写一个工厂,允许在不在Spring上下文中时获取Application实例以实例化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块中,我们恢复之前保留的默认刷新模式的值。
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);
        }
    }
}

1
你需要至少缩进4个空格来格式化你的代码。 - Impulse The Fox
请在您的答案中添加一些解释。 - BlackBeard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接