在我们的应用程序中,我们已经从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的已知问题吗?
答案 0 :(得分:6)
异常结果是由我们使用的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
。
答案 1 :(得分:3)
我在hibernate 5.0.11中使用了这个,并且验证了它也发生在5.2.5。我的解决方案是注释自定义验证器以打开新事务。
@Transactional(propagation=Propagation.REQUIRES_NEW)
我认为在自定义约束验证器易于设置和使用之前,hibernate仍有一段路要走,因为这花费了我更多的时间。
编辑:问题相关。我认为使用相同的交易违反了jpa2.1规范https://hibernate.atlassian.net/browse/HHH-7537
答案 2 :(得分:0)
在实现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的类中,我们实现了一个EntityManager,这要归功于先前在方法初始化中创建的工厂。
在调用调用存储库的方法之前,我们将Hibernate当前会话的刷新模式更改为FlushMode.MANUAL,以避免在保持默认刷新模式的同时调用存储库后自动刷新。最后,在块中,我们恢复先前保留的刷新模式的默认值。
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);
}
}
}