Sylvain Dusart (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394...
) *updated* an issue
Hibernate ORM (
https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZTQ4YzI3ZDQ1...
) / Bug (
https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz...
) HHH-16218 (
https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz...
) Natural id cache is extremely slow for entities with compound natural id (
https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz...
)
Change By: Sylvain Dusart (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394...
)
Hibernate maintains a cache 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 :
{code:java}final int prime = 31;
int hashCodeCalculation = 1;
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.hashCode();
hashCodeCalculation = prime * hashCodeCalculation +
entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue,
persistenceContext.getSession() );{code}
For entities with a simple natural id, entityDescriptor.getNaturalIdMapping() is an
instance of SimpleNaturalIdMapping with this calculateHashCode method :
{code:java}@Override
public int calculateHashCode(Object value) {
//noinspection unchecked
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode(
value );
}{code}
For entities with a compound natural id, entityDescriptor.getNaturalIdMapping() is an
instance of CompoundNaturalIdMapping with this calculateHashCode method :
{code:java}@Override
public int calculateHashCode(Object value) {
return 0;
}{code}
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 something like
{code:java}@Override
public int calculateHashCode(Object value) {
return Arrays.hashCode((Object[]) value);
}{code}
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 modification :
{code:java}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{code}
Logs after correction modification :
{code:java}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{code}
(
https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey...
) Add Comment (
https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey...
)
Get Jira notifications on your phone! Download the Jira Cloud app for Android (
https://play.google.com/store/apps/details?id=com.atlassian.android.jira....
) or iOS (
https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=Em...
) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100216- sha1:64a4cf6 )