HHH-16728 Optimise iteration of AssociationType properties within a Persister

This commit is contained in:
Sanne Grinovero 2023-05-19 22:43:48 +01:00 committed by Christian Beikov
parent 0d58a2410c
commit 2b92c3dbe5
7 changed files with 113 additions and 24 deletions

View File

@ -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<UniqueKeyEntry> 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<UniqueKeyEntry> uniqueKeyEntries() {
if ( this.uniqueKeyEntries == null ) {
this.uniqueKeyEntries = initUniqueKeyEntries( this );
}
return this.uniqueKeyEntries;
}
private static List<UniqueKeyEntry> initUniqueKeyEntries(final AbstractEntityPersister aep) {
ArrayList<UniqueKeyEntry> 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<String, SingleIdArrayLoadPlan> getLazyLoadPlanByFetchGroup() {
final BytecodeEnhancementMetadata metadata = entityMetamodel.getBytecodeEnhancementMetadata();
return metadata.isEnhancedForLazyLoading() && metadata.getLazyAttributesMetadata().hasLazyAttributes()

View File

@ -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<UniqueKeyEntry> uniqueKeyEntries();
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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;
}
}

View File

@ -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 );
}
}
}

View File

@ -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<UniqueKeyEntry> uniqueKeyEntries() {
return Collections.emptyList();
}
@Override
public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) {
return false;

View File

@ -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<UniqueKeyEntry> uniqueKeyEntries() {
return Collections.emptyList();
}
@Override
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SharedSessionContractImplementor session) {
return null;

View File

@ -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<UniqueKeyEntry> uniqueKeyEntries() {
return Collections.emptyList();
}
@Override
public boolean isAffectedByEntityGraph(LoadQueryInfluencers loadQueryInfluencers) {
return loadQueryInfluencers.getEffectiveEntityGraph().getGraph() != null;