I get java.util.ConcurrentModificationException due to nested lazy batch fetching:
Analysis of Problem:
It's not a multi-threading issue.
1. MyDataObject2.toString() results in lazy loading of MyDataObject2
2. during lazy loading of MyDataObject2, a org.hibernate.util.EqualsHelper.equals() is triggering lazy loading of MyDataObject1 while still inside of loop iterating over org.hibernate.engine.BatchFetchQueue.batchLoadableEntityKeys in org.hibernate.engine.BatchFetchQueue.getEntityBatch()
3. during lazy loading of MyDataObject1, org.hibernate.engine.BatchFetchQueue.removeBatchLoadableEntityKey modifies org.hibernate.engine.BatchFetchQueue.batchLoadableEntityKeys, will result in ConcurrentModificationException as soon as loop in org.hibernate.engine.BatchFetchQueue.getEntityBatch is continued
How can I prevent that and still use lazy / batch fetching?
Hibernate 3.2.4.sp1 (coming with jboss-4.2.3.GA)
Relevant mapping used (legacy DB + code to be accomodated):
Code:
<class name="MyDataObject1" mutable="false">
<composite-id name="MyDataObject1PK">
<key-property name="MyDataObject1Nr">
</key-property>
</composite-id>
...
<list name="MyDataObject2s">
<key>
<column name="MyDataObject1Nr" not-null="true"></column>
</key>
<list-index base="1">
<column name="number" not-null="true"></column>
</list-index>
<one-to-many entity-name="MyDataObject2"/>
</list>
...
</class>
<class name="MyDataObject2" mutable="false">
<composite-id mapped="true">
<key-many-to-one name="myDataObject1">
<column name="MyDataObject1Nr" not-null="true"></column>
</key-many-to-one>
<key-property name="number">
</key-property>
</composite-id>
...
</class>
Stacktrace of modCount modification responsible for ConcurrentModificationException later on:
Code:
Daemon Thread [Thread-1581] (Suspended (modification of field modCount in org.apache.commons.collections.SequencedHashMap))
org.apache.commons.collections.SequencedHashMap.removeImpl(java.lang.Object) line: 472
org.apache.commons.collections.SequencedHashMap.remove(java.lang.Object) line: 460
org.hibernate.engine.BatchFetchQueue.removeBatchLoadableEntityKey(org.hibernate.engine.EntityKey) line: 130
org.hibernate.engine.StatefulPersistenceContext.addEntity(org.hibernate.engine.EntityKey, java.lang.Object) line: 314
org.hibernate.engine.StatefulPersistenceContext.addEntity(java.lang.Object, org.hibernate.engine.Status, java.lang.Object[], org.hibernate.engine.EntityKey, java.lang.Object, org.hibernate.LockMode, boolean, org.hibernate.persister.entity.EntityPersister, boolean, boolean) line: 408
org.hibernate.engine.TwoPhaseLoad.addUninitializedEntity(org.hibernate.engine.EntityKey, java.lang.Object, org.hibernate.persister.entity.EntityPersister, org.hibernate.LockMode, boolean, org.hibernate.engine.SessionImplementor) line: 240
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).loadFromResultSet(java.sql.ResultSet, int, java.lang.Object, java.lang.String, org.hibernate.engine.EntityKey, java.lang.String, org.hibernate.LockMode, org.hibernate.persister.entity.Loadable, org.hibernate.engine.SessionImplementor) line: 1366
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).instanceNotYetLoaded(java.sql.ResultSet, int, org.hibernate.persister.entity.Loadable, java.lang.String, org.hibernate.engine.EntityKey, org.hibernate.LockMode, org.hibernate.engine.EntityKey, java.lang.Object, java.util.List, org.hibernate.engine.SessionImplementor) line: 1308
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).getRow(java.sql.ResultSet, org.hibernate.persister.entity.Loadable[], org.hibernate.engine.EntityKey[], java.lang.Object, org.hibernate.engine.EntityKey, org.hibernate.LockMode[], java.util.List, org.hibernate.engine.SessionImplementor) line: 1206
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).getRowFromResultSet(java.sql.ResultSet, org.hibernate.engine.SessionImplementor, org.hibernate.engine.QueryParameters, org.hibernate.LockMode[], org.hibernate.engine.EntityKey, java.util.List, org.hibernate.engine.EntityKey[], boolean) line: 580
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).doQuery(org.hibernate.engine.SessionImplementor, org.hibernate.engine.QueryParameters, boolean) line: 701
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).doQueryAndInitializeNonLazyCollections(org.hibernate.engine.SessionImplementor, org.hibernate.engine.QueryParameters, boolean) line: 236
org.hibernate.loader.entity.EntityLoader(org.hibernate.loader.Loader).loadEntityBatch(org.hibernate.engine.SessionImplementor, java.io.Serializable[], org.hibernate.type.Type, java.lang.Object, java.lang.String, java.io.Serializable, org.hibernate.persister.entity.EntityPersister) line: 1955
org.hibernate.loader.entity.BatchingEntityLoader.load(java.io.Serializable, java.lang.Object, org.hibernate.engine.SessionImplementor) line: 69
org.hibernate.persister.entity.SingleTableEntityPersister(org.hibernate.persister.entity.AbstractEntityPersister).load(java.io.Serializable, java.lang.Object, org.hibernate.LockMode, org.hibernate.engine.SessionImplementor) line: 3044
org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 395
org.hibernate.event.def.DefaultLoadEventListener.doLoad(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 375
org.hibernate.event.def.DefaultLoadEventListener.load(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 139
org.hibernate.event.def.DefaultLoadEventListener.onLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 98
org.hibernate.impl.SessionImpl.fireLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 878
org.hibernate.impl.SessionImpl.immediateLoad(java.lang.String, java.io.Serializable) line: 836
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).initialize() line: 66
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).getImplementation() line: 111
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[]) line: 166
MyDataObject1_$$_javassist_153.equals(java.lang.Object) line: not available
org.hibernate.util.EqualsHelper.equals(java.lang.Object, java.lang.Object) line: 10
org.hibernate.type.ManyToOneType(org.hibernate.type.AbstractType).isEqual(java.lang.Object, java.lang.Object, org.hibernate.EntityMode) line: 108
org.hibernate.type.EmbeddedComponentType(org.hibernate.type.ComponentType).isEqual(java.lang.Object, java.lang.Object, org.hibernate.EntityMode) line: 125
org.hibernate.engine.BatchFetchQueue.getEntityBatch(org.hibernate.persister.entity.EntityPersister, java.io.Serializable, int, org.hibernate.EntityMode) line: 226
org.hibernate.loader.entity.BatchingEntityLoader.load(java.io.Serializable, java.lang.Object, org.hibernate.engine.SessionImplementor) line: 60
org.hibernate.persister.entity.SingleTableEntityPersister(org.hibernate.persister.entity.AbstractEntityPersister).load(java.io.Serializable, java.lang.Object, org.hibernate.LockMode, org.hibernate.engine.SessionImplementor) line: 3044
org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 395
org.hibernate.event.def.DefaultLoadEventListener.doLoad(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 375
org.hibernate.event.def.DefaultLoadEventListener.load(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 139
org.hibernate.event.def.DefaultLoadEventListener.onLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 98
org.hibernate.impl.SessionImpl.fireLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 878
org.hibernate.impl.SessionImpl.immediateLoad(java.lang.String, java.io.Serializable) line: 836
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).initialize() line: 66
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).getImplementation() line: 111
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[]) line: 166
MyDataObject2_$$_javassist_146.toString() line: not available
java.lang.String.valueOf(java.lang.Object) line: 2615
java.lang.StringBuilder.append(java.lang.Object) line: 116
MyDataObject2.toString() line: 592
java.lang.String.valueOf(java.lang.Object) line: 2615
java.lang.StringBuilder.append(java.lang.Object) line: 116
A few steps later:
Code:
this: org.hibernate.engine.BatchFetchQueue.batchLoadableEntityKeys of currents session batch fetch queue
this SequencedHashMap$OrderedIterator (id=1753)
expectedModCount 27970
pos SequencedHashMap$Entry (id=1755)
returnType 0
this$0 SequencedHashMap (id=1278)
entries HashMap<K,V> (id=1286)
modCount 27975
sentinel SequencedHashMap$Entry (id=1301)
Daemon Thread [Thread-1581] (Suspended (exception java.util.ConcurrentModificationException))
org.apache.commons.collections.SequencedHashMap$OrderedIterator.next() line: 757
org.hibernate.engine.BatchFetchQueue.getEntityBatch(org.hibernate.persister.entity.EntityPersister, java.io.Serializable, int, org.hibernate.EntityMode) line: 220
org.hibernate.loader.entity.BatchingEntityLoader.load(java.io.Serializable, java.lang.Object, org.hibernate.engine.SessionImplementor) line: 60
org.hibernate.persister.entity.SingleTableEntityPersister(org.hibernate.persister.entity.AbstractEntityPersister).load(java.io.Serializable, java.lang.Object, org.hibernate.LockMode, org.hibernate.engine.SessionImplementor) line: 3044
org.hibernate.event.def.DefaultLoadEventListener.loadFromDatasource(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 395
org.hibernate.event.def.DefaultLoadEventListener.doLoad(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 375
org.hibernate.event.def.DefaultLoadEventListener.load(org.hibernate.event.LoadEvent, org.hibernate.persister.entity.EntityPersister, org.hibernate.engine.EntityKey, org.hibernate.event.LoadEventListener$LoadType) line: 139
org.hibernate.event.def.DefaultLoadEventListener.onLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 98
org.hibernate.impl.SessionImpl.fireLoad(org.hibernate.event.LoadEvent, org.hibernate.event.LoadEventListener$LoadType) line: 878
org.hibernate.impl.SessionImpl.immediateLoad(java.lang.String, java.io.Serializable) line: 836
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).initialize() line: 66
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer(org.hibernate.proxy.AbstractLazyInitializer).getImplementation() line: 111
org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(java.lang.Object, java.lang.reflect.Method, java.lang.reflect.Method, java.lang.Object[]) line: 166
MyDataObject2_$$_javassist_146.toString() line: not available
java.lang.String.valueOf(java.lang.Object) line: 2615
java.lang.StringBuilder.append(java.lang.Object) line: 116
MyDataObject2.toString() line: 592