java - 在 Hibernate 中保存大型集合时出现 ConcurrentModificationException

标签 java multithreading hibernate

我有以下问题:

当实体应保存在数据库中时,我有一个应用程序将 DatabaseJob 发送到单独线程中的队列。当队列中有作业时,该工作线程会从队列中获取第一个作业并使用 Hibernate 来保存它。一般来说,这工作得很好。

我遇到的问题是在尝试保存大型集合(1000+)条目时出现的。当 Hibernate 尝试将集合刷新到数据库时,可能会发生集合被修改的情况,从而导致以下 ConcurrentModificationException

2014-09-01 10:37:57,301 ERROR [DatabaseManager] [Thread: database-message-queue] Hibernate - Unhandled Exception during saving
java.util.ConcurrentModificationException
    at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:711)
    at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:734)
    at org.hibernate.collection.internal.PersistentSet.getSnapshot(PersistentSet.java:96)
    at org.hibernate.engine.spi.CollectionEntry.afterAction(CollectionEntry.java:246)
    at org.hibernate.action.internal.CollectionUpdateAction.execute(CollectionUpdateAction.java:105)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:349)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177)

通过使用Collections.synchronizeXXX,我使用的所有集合都可以安全地进行并发访问,但对于迭代,必须手动同步。

有没有办法分别使用线程安全迭代器来同步 Hibernate 的集合访问?我已经尝试使用 org.hibernate.usertype.UserCollectionType ,但也不起作用。

否则我想我可能必须在将实体添加到数据库队列之前复制它们,但我希望有一种更简单的方法来做到这一点。

提前非常感谢, 本杰明·高尼茨

更新 1 -> 相关代码块(为了简单起见,删除了异常处理)

private void processHibernateSaves() {
    if (!savesHibernate.isEmpty()) {            
        Iterator<DatabaseJobEvent> jobIter = savesHibernate.iterator();
        while (jobIter.hasNext()) {
            DatabaseJobEvent job = jobIter.next();
            Session session = HibernateUtil.currentSession();
            tx = session.beginTransaction();
            for (PersistentObject obj : job.getObjects()) {
                if (obj.isSavedInDatabase()) {
                    session.update(obj);
                } else {
                    session.save(obj);
                }
            }
            tx.commit(); //<-- Error is thrown at this point
            session.clear();
            for (PersistentObject obj : job.getObjects()) {
                synchronized (obj) {
                    obj.setSavedInDatabase(true);
                }
            }
            job.setExecuted(true);
            jobIter.remove();
        }
    }
}

PersistentObject 不是集合本身,它包含作为字段的集合。

最佳答案

Java 不提供 CopyOnWriteHashMap 实现,但看看是否可以使用 jersey impl

https://java.net/projects/jersey/sources/svn/content/trunk/jersey/jersey-client/src/main/java/com/sun/jersey/client/impl/CopyOnWriteHashMap.java

您需要将其克隆传递给 hibernate API

或者编写您自己的实现 http://concurrently.blogspot.in/2007/07/implementing-copy-on-write-hashmap-in.html

关于java - 在 Hibernate 中保存大型集合时出现 ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25602557/

相关文章:

java - ESC命令将条码打印到热敏打印机?

java - 为什么在Java中必须使用paintComponent方法来绘制?

java - Spring Boot 中 OffsetDateTime 的 jackson 日期格式

c - 使用 pthread_create 时如何判断是什么导致了段错误?

java - h2 上的 hibernate ID 生成器 AUTO_INCREMENT 和集群中的 MySQL

java - 无法使用带有/自定义拦截器的 spring resttemplate 发布大文件

java - Hibernate 多对多映射未保存在数据透视表中

.net - SwitchToThread/Thread.Yield 与 Thread.Sleep(0) 与 Thread.Sleep(1)

java - 如何解决 Spring Security 配置问题

java - 在 hibernate 中合并具有复合主键的实体时出现异常