ConcurrentModificationException при сохранении больших коллекций в Hibernate

1

У меня есть следующая проблема:

У меня есть приложение, которое отправляет 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, который тоже не работал.

В противном случае, я думаю, мне придется копировать объекты, прежде чем добавлять их в очередь базы данных, но я надеюсь, что есть более простой способ сделать это.

Большое спасибо заблаговременно, Бенджамин Гауниц

UPDATE 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 не является самой коллекцией, в которой содержится коллекция как поле.

  • 0
    Покажите нам код, где вы сохраняете коллекцию
  • 0
    Пожалуйста, отправьте соответствующий код.
Показать ещё 2 комментария
Теги:
multithreading
hibernate

1 ответ

0

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

  • 0
    В Java присутствует ConcurrentSkipListMap

Ещё вопросы

Сообщество Overcoder
Наверх
Меню