Hibernate maintains a cache in the session for id<->naturalId correspondances (pkToNaturalIdMap and naturalIdToPkMap maps in org.hibernate.engine.internal.NaturalIdResolutionsImpl.EntityResolutions#cache). This cache is updated after creations and loads (at least, I did not test other use-cases) . In the maps, the natural id is encapsulated in a ResolutionImpl instance and the hashcode is computed in the constructor :
final int prime = 31;
int hashCodeCalculation = 1;
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.hashCode();
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue, persistenceContext.getSession() );
For entities with a simple natural id, entityDescriptor.getNaturalIdMapping() is an instance of SimpleNaturalIdMapping with this calculateHashCode method :
@Override
public int calculateHashCode(Object value) {
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode( value );
}
For entities with a compound natural id, entityDescriptor.getNaturalIdMapping() is an instance of CompoundNaturalIdMapping with this calculateHashCode method :
@Override
public int calculateHashCode(Object value) {
return 0;
}
As a consequence, for a given entity with a compound natural id, the hashcodes of all ResolutionImpl objects used as keys in naturalIdToPkMap are the same, which creates collisions and consumes a lot of cpu cycles. The calculateHashCode method in CompoundNaturalIdMapping should be
@Override
public int calculateHashCode(Object value) {
return Arrays.hashCode((Object[]) value);
}
to align with the areEqual method. The attached test case creates 20000 objects in database then re-reads these objects. It is done for two entity classes : EntityWithSimpleNaturalId and EntityWithCompoundNaturalId. Logs before correction :
16:33:31,782 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:33:53,745 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=21957ms
16:33:53,906 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=160ms
16:33:55,140 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:34:12,915 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=17777ms
16:34:12,921 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:34:13,021 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=99ms
Logs after correction :
16:35:37,950 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:35:38,247 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=292ms
16:35:38,368 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=119ms
16:35:39,679 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:35:40,096 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=418ms
16:35:40,103 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:35:40,232 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=129ms
|