From 2b92c3dbe51f6ca6b588c2dd76cfea04e882bda3 Mon Sep 17 00:00:00 2001 From: Sanne Grinovero Date: Fri, 19 May 2023 22:43:48 +0100 Subject: [PATCH] HHH-16728 Optimise iteration of AssociationType properties within a Persister --- .../entity/AbstractEntityPersister.java | 28 ++++++++++++ .../persister/entity/EntityPersister.java | 7 +++ .../persister/entity/UniqueKeyEntry.java | 40 +++++++++++++++++ .../entity/AbstractEntityInitializer.java | 44 +++++++++---------- .../GoofyPersisterClassProvider.java | 6 +++ .../PersisterClassProviderTest.java | 6 +++ .../orm/test/legacy/CustomPersister.java | 6 +++ 7 files changed, 113 insertions(+), 24 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/persister/entity/UniqueKeyEntry.java diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 97a7e5593b..c3a2168725 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -120,6 +120,7 @@ import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.LazyValue; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.LockModeEnumMap; import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.TooManyRowsAffectedException; @@ -279,6 +280,7 @@ import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.AnyType; +import org.hibernate.type.AssociationType; import org.hibernate.type.BasicType; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; @@ -464,6 +466,8 @@ public abstract class AbstractEntityPersister private final boolean implementsLifecycle; + private List uniqueKeyEntries = null; //lazily initialized + @Deprecated(since = "6.0") public AbstractEntityPersister( final PersistentClass persistentClass, @@ -1166,6 +1170,30 @@ public abstract class AbstractEntityPersister return useReferenceCacheEntries; } + @Override + public Iterable uniqueKeyEntries() { + if ( this.uniqueKeyEntries == null ) { + this.uniqueKeyEntries = initUniqueKeyEntries( this ); + } + return this.uniqueKeyEntries; + } + + private static List initUniqueKeyEntries(final AbstractEntityPersister aep) { + ArrayList uniqueKeys = new ArrayList(); + for ( Type propertyType : aep.getPropertyTypes() ) { + if ( propertyType instanceof AssociationType ) { + final AssociationType associationType = (AssociationType) propertyType; + final String ukName = associationType.getLHSPropertyName(); + if ( ukName != null ) { + final int index = aep.findAttributeMapping( ukName ).getStateArrayPosition(); + final Type type = aep.getPropertyTypes()[index]; + uniqueKeys.add( new UniqueKeyEntry( ukName, index, type ) ); + } + } + } + return CollectionHelper.toSmallList( uniqueKeys ); + } + protected Map getLazyLoadPlanByFetchGroup() { final BytecodeEnhancementMetadata metadata = entityMetamodel.getBytecodeEnhancementMetadata(); return metadata.isEnhancedForLazyLoading() && metadata.getLazyAttributesMetadata().hasLazyAttributes() diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 91c1b7aa86..a45e013ae4 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -12,6 +12,7 @@ import java.util.Map; import java.util.function.Consumer; import org.hibernate.HibernateException; +import org.hibernate.Incubating; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.MappingException; @@ -1075,4 +1076,10 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc @Deprecated(since = "6.2") String ENTITY_ID = "id"; + /** + * @return Metadata for each unique key defined + */ + @Incubating + Iterable uniqueKeyEntries(); + } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UniqueKeyEntry.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UniqueKeyEntry.java new file mode 100644 index 0000000000..4127cf991c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UniqueKeyEntry.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.persister.entity; + +import java.util.Objects; + +import org.hibernate.type.Type; + +/** + * Useful metadata representing a unique key within a Persister + */ +public final class UniqueKeyEntry { + + private final String uniqueKeyName; + private final int stateArrayPosition; + private final Type propertyType; + + public UniqueKeyEntry(final String uniqueKeyName, final int stateArrayPosition, final Type propertyType) { + this.uniqueKeyName = Objects.requireNonNull( uniqueKeyName ); + this.stateArrayPosition = stateArrayPosition; + this.propertyType = Objects.requireNonNull( propertyType ); + } + + public String getUniqueKeyName() { + return this.uniqueKeyName; + } + + public int getStateArrayPosition() { + return this.stateArrayPosition; + } + + public Type getPropertyType() { + return this.propertyType; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 4b124d1946..712fb731c0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -41,6 +41,7 @@ import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyEntry; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.map.MapProxy; @@ -1034,32 +1035,27 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces } } - protected void registerPossibleUniqueKeyEntries(Object toInitialize, SharedSessionContractImplementor session) { - for ( Type propertyType : concreteDescriptor.getPropertyTypes() ) { - if ( propertyType instanceof AssociationType ) { - final AssociationType associationType = (AssociationType) propertyType; - final String ukName = associationType.getLHSPropertyName(); - if ( ukName != null ) { - final int index = concreteDescriptor.findAttributeMapping( ukName ).getStateArrayPosition(); - final Type type = concreteDescriptor.getPropertyTypes()[index]; + protected void registerPossibleUniqueKeyEntries(final Object toInitialize, final SharedSessionContractImplementor session) { + for ( UniqueKeyEntry entry : concreteDescriptor.uniqueKeyEntries() ) { + final String ukName = entry.getUniqueKeyName(); + final int index = entry.getStateArrayPosition();//concreteDescriptor.findAttributeMapping( ukName ).getStateArrayPosition(); + final Type type = entry.getPropertyType();//concreteDescriptor.getPropertyTypes()[index]; - // polymorphism not really handled completely correctly, - // perhaps...well, actually its ok, assuming that the - // entity name used in the lookup is the same as the - // one used here, which it will be + // polymorphism not really handled completely correctly, + // perhaps...well, actually its ok, assuming that the + // entity name used in the lookup is the same as the + // one used here, which it will be - if ( resolvedEntityState[index] != null ) { - final EntityUniqueKey euk = new EntityUniqueKey( - concreteDescriptor.getRootEntityDescriptor().getEntityName(), - //polymorphism comment above - ukName, - resolvedEntityState[index], - type, - session.getFactory() - ); - session.getPersistenceContextInternal().addEntity( euk, toInitialize ); - } - } + if ( resolvedEntityState[index] != null ) { + final EntityUniqueKey euk = new EntityUniqueKey( + concreteDescriptor.getRootEntityDescriptor().getEntityName(), + //polymorphism comment above + ukName, + resolvedEntityState[index], + type, + session.getFactory() + ); + session.getPersistenceContextInternal().addEntity( euk, toInitialize ); } } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/persister/GoofyPersisterClassProvider.java index e95a749906..5dd86f78c6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cfg/persister/GoofyPersisterClassProvider.java @@ -60,6 +60,7 @@ import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyEntry; import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; @@ -758,6 +759,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return false; } + @Override + public Iterable uniqueKeyEntries() { + return Collections.emptyList(); + } + @Override public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) { return false; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ejb3configuration/PersisterClassProviderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ejb3configuration/PersisterClassProviderTest.java index 5c335e87f4..8647df707e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ejb3configuration/PersisterClassProviderTest.java @@ -58,6 +58,7 @@ import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.orm.test.jpa.SettingsGenerator; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyEntry; import org.hibernate.persister.internal.PersisterClassResolverInitiator; import org.hibernate.persister.spi.PersisterClassResolver; import org.hibernate.persister.spi.PersisterCreationContext; @@ -729,6 +730,11 @@ public class PersisterClassProviderTest { return false; //To change body of implemented methods use File | Settings | File Templates. } + @Override + public Iterable uniqueKeyEntries() { + return Collections.emptyList(); + } + @Override public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SharedSessionContractImplementor session) { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomPersister.java index d1797ec940..815595483c 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/legacy/CustomPersister.java @@ -57,6 +57,7 @@ import org.hibernate.metamodel.mapping.TableDetails; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.spi.EntityRepresentationStrategy; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyEntry; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; @@ -849,6 +850,11 @@ public class CustomPersister implements EntityPersister { return false; //To change body of implemented methods use File | Settings | File Templates. } + @Override + public Iterable uniqueKeyEntries() { + return Collections.emptyList(); + } + @Override public boolean isAffectedByEntityGraph(LoadQueryInfluencers loadQueryInfluencers) { return loadQueryInfluencers.getEffectiveEntityGraph().getGraph() != null;