When a entity who has any map associations where the elements have composite keys are merged, an null pointer exception is raised, below is the stack trace.
Code:
java.lang.NullPointerException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:169)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:199)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3605)
at org.hibernate.type.EntityType.isEqual(EntityType.java:330)
at org.hibernate.type.ComponentType.isEqual(ComponentType.java:166)
at org.hibernate.engine.EntityKey.equals(EntityKey.java:119)
at java.util.HashMap.get(HashMap.java:305)
at org.hibernate.engine.StatefulPersistenceContext.getEntity(StatefulPersistenceContext.java:345)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:185)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:314)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:282)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:397)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:234)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:715)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:697)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:268)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:292)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:240)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:320)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:266)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:243)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:193)
at org.hibernate.engine.Cascade.cascade(Cascade.java:154)
at org.hibernate.event.def.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:563)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:423)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:234)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:705)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:689)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:693)
at org.hibernate.test.collection.map.CompositeIDPersistentMapTest.testMergedPutWithCompositeID(CompositeIDPersistentMapTest.java:38)
The bug's is caused by the code snippet below (this snippet is taken from DefaultMergeEventListener:~291)
Code:
final Serializalizable id = persister.hasIdentifierProperty() ? persister.getIdentifier( entity, source.getEntityMode() ) : null
When you map an entity with a composite id you might not have an identifier property, in this scenario, the entity became it's own identifier. The code snippet above assumes that if you dont have an identifier property, the id must be null.
I'm proposing these code changes :
Code:
// If there isn't an identifier property, the entity was mapped with a composite-id therefore it must be Serializable
final Serializable id = persister.hasIdentifierProperty() ? persister.getIdentifier( entity, source.getEntityMode() ) :((Serializable)(entity));
If is required, I'll fill an JIRA issue with an patch and a underlying test case.