HHH-15569 Replace list and map with array to improve state extraction for entity initializing

This commit is contained in:
Christian Beikov 2022-10-25 15:06:23 +02:00
parent 1d5f6b5c13
commit 99f9ccdd11
4 changed files with 126 additions and 68 deletions

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.mapping;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -23,6 +24,7 @@ import org.hibernate.loader.ast.spi.NaturalIdLoader;
import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
@ -167,6 +169,10 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Inheritance
default int getSubclassId() {
return getEntityPersister().getEntityMetamodel().getSubclassId();
}
default boolean hasSubclasses() {
return getEntityPersister().getEntityMetamodel().hasSubclasses();
}
@ -198,6 +204,16 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
return null;
}
default Collection<EntityMappingType> getSubMappingTypes() {
final MappingMetamodelImplementor mappingMetamodel = getEntityPersister().getFactory().getMappingMetamodel();
final Set<String> subclassEntityNames = getSubclassEntityNames();
final List<EntityMappingType> mappingTypes = new ArrayList<>( subclassEntityNames.size() );
for ( String subclassEntityName : subclassEntityNames ) {
mappingTypes.add( mappingMetamodel.getEntityDescriptor( subclassEntityName ) );
}
return mappingTypes;
}
default boolean isTypeOrSuperType(EntityMappingType targetType) {
return targetType == this;
}
@ -314,6 +330,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
// Customer <- DomesticCustomer <- OtherCustomer
@Deprecated(forRemoval = true)
default Object[] extractConcreteTypeStateValues(
Map<AttributeMapping, DomainResultAssembler> assemblerMapping,
RowProcessingState rowProcessingState) {

View File

@ -1221,7 +1221,7 @@ public abstract class AbstractEntityPersister
// of this class, not its subclasses, but since we
// are reusing code used for sequential selects, we
// use the subclass closure
partsToSelect.add( getAttributeMappings().get( getSubclassPropertyIndex( lazyAttributeDescriptor.getName() ) ) );
partsToSelect.add( getAttributeMapping( getSubclassPropertyIndex( lazyAttributeDescriptor.getName() ) ) );
}
if ( partsToSelect.isEmpty() ) {
@ -2915,8 +2915,8 @@ public abstract class AbstractEntityPersister
final Update update = createUpdate().setTableName( getTableName( j ) );
boolean hasColumns = false;
for ( int index = 0; index < attributeMappings.size(); index++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( index );
for ( int index = 0; index < attributeMappings.length; index++ ) {
final AttributeMapping attributeMapping = attributeMappings[index];
if ( isPropertyOfTable( index, j ) ) {
// `attributeMapping` is an attribute of the table we are updating
@ -3039,8 +3039,8 @@ public abstract class AbstractEntityPersister
final Insert insert = createInsert().setTableName( getTableName( j ) );
for ( int index = 0; index < attributeMappings.size(); index++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( index );
for ( int index = 0; index < attributeMappings.length; index++ ) {
final AttributeMapping attributeMapping = attributeMappings[index];
if ( isPropertyOfTable( index, j ) ) {
// `attributeMapping` is an attribute of the table we are updating
@ -5117,10 +5117,10 @@ public abstract class AbstractEntityPersister
}
else {
if ( hasSubclasses() ) {
for ( int i = 0; i < attributeMappings.size(); i++ ) {
for ( int i = 0; i < attributeMappings.length; i++ ) {
final Object value = values[i];
if ( value != UNFETCHED_PROPERTY ) {
final Setter setter = attributeMappings.get( i ).getPropertyAccess().getSetter();
final Setter setter = attributeMappings[i].getPropertyAccess().getSetter();
setter.set( object, value );
}
}
@ -5153,8 +5153,8 @@ public abstract class AbstractEntityPersister
final BytecodeEnhancementMetadata enhancementMetadata = entityMetamodel.getBytecodeEnhancementMetadata();
final LazyAttributesMetadata lazyAttributesMetadata = enhancementMetadata.getLazyAttributesMetadata();
final Object[] values = new Object[ getNumberOfAttributeMappings() ];
for ( int i = 0; i < attributeMappings.size(); i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
for ( int i = 0; i < attributeMappings.length; i++ ) {
final AttributeMapping attributeMapping = attributeMappings[i];
final AttributeMetadataAccess attributeMetadataAccess = attributeMapping.getAttributeMetadataAccess();
if ( ! lazyAttributesMetadata.isLazyAttribute( attributeMapping.getAttributeName() )
|| enhancementMetadata.isAttributeLoaded( object, attributeMapping.getAttributeName() ) ) {
@ -5175,7 +5175,7 @@ public abstract class AbstractEntityPersister
@Override
public Object getPropertyValue(Object object, int i) {
return attributeMappings.get( i ).getAttributeMetadataAccess()
return attributeMappings[i].getAttributeMetadataAccess()
.resolveAttributeMetadata( this )
.getPropertyAccess()
.getGetter()
@ -5370,9 +5370,9 @@ public abstract class AbstractEntityPersister
return accessOptimizer.getPropertyValues( entity );
}
final Object[] result = new Object[this.attributeMappings.size()];
for ( int i = 0; i < this.attributeMappings.size(); i++ ) {
result[i] = this.attributeMappings.get( i ).getPropertyAccess().getGetter().getForInsert(
final Object[] result = new Object[this.attributeMappings.length];
for ( int i = 0; i < this.attributeMappings.length; i++ ) {
result[i] = this.attributeMappings[i].getPropertyAccess().getGetter().getForInsert(
entity,
mergeMap,
session
@ -5668,7 +5668,7 @@ public abstract class AbstractEntityPersister
private EntityRowIdMapping rowIdMapping;
private EntityDiscriminatorMapping discriminatorMapping;
private List<AttributeMapping> attributeMappings;
private AttributeMapping[] attributeMappings;
protected Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>();
protected List<Fetchable> staticFetchableList;
@ -5676,13 +5676,15 @@ public abstract class AbstractEntityPersister
@Override
public void visitAttributeMappings(Consumer<? super AttributeMapping> action) {
attributeMappings.forEach( action );
for ( AttributeMapping attributeMapping : attributeMappings ) {
action.accept( attributeMapping );
}
}
@Override
public void forEachAttributeMapping(IndexedConsumer<AttributeMapping> consumer) {
for ( int i = 0; i < attributeMappings.size(); i++ ) {
consumer.accept( i, attributeMappings.get( i ) );
for ( int i = 0; i < attributeMappings.length; i++ ) {
consumer.accept( i, attributeMappings[i] );
}
}
@ -5770,7 +5772,7 @@ public abstract class AbstractEntityPersister
if ( hasUpdateGeneratedProperties() ) {
updateGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.UPDATE );
}
staticFetchableList = new ArrayList<>( attributeMappings.size() );
staticFetchableList = new ArrayList<>( attributeMappings.length );
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) );
return true;
}
@ -5910,13 +5912,11 @@ public abstract class AbstractEntityPersister
// in the collected names. iterate here because it is already alphabetical
final List<SingularAttributeMapping> collectedAttrMappings = new ArrayList<>();
this.attributeMappings.forEach(
(attributeMapping) -> {
if ( attributeNames.contains( attributeMapping.getAttributeName() ) ) {
collectedAttrMappings.add( (SingularAttributeMapping) attributeMapping );
}
}
);
for ( AttributeMapping attributeMapping : attributeMappings ) {
if ( attributeNames.contains( attributeMapping.getAttributeName() ) ) {
collectedAttrMappings.add( (SingularAttributeMapping) attributeMapping );
}
}
if ( collectedAttrMappings.size() <= 1 ) {
throw new MappingException( "Expected multiple natural-id attributes, but found only one: " + getEntityName() );
@ -6106,12 +6106,12 @@ public abstract class AbstractEntityPersister
// force calculation of `attributeMappings`
getAttributeMappings();
}
return attributeMappings.size();
return attributeMappings.length;
}
@Override
public AttributeMapping getAttributeMapping(int position) {
return attributeMappings.get( position );
return attributeMappings[position];
}
@Override
@ -6134,6 +6134,14 @@ public abstract class AbstractEntityPersister
return superMappingType;
}
@Override
public Collection<EntityMappingType> getSubMappingTypes() {
if ( subclassMappingTypes == null ) {
return Collections.emptyList();
}
return subclassMappingTypes.values();
}
@Override
public boolean isTypeOrSuperType(EntityMappingType targetType) {
if ( targetType == null ) {
@ -6534,18 +6542,18 @@ public abstract class AbstractEntityPersister
@Override
public List<AttributeMapping> getAttributeMappings() {
if ( attributeMappings == null ) {
attributeMappings = new ArrayList<>();
ArrayList<AttributeMapping> attributeMappings = new ArrayList<>();
if ( superMappingType != null ) {
superMappingType.visitAttributeMappings( attributeMappings::add );
}
attributeMappings.addAll( declaredAttributeMappings.values() );
this.attributeMappings = attributeMappings.toArray(new AttributeMapping[0]);
// subclasses? it depends on the usage
}
return attributeMappings;
return Arrays.asList( attributeMappings );
}
@Override
@ -6769,7 +6777,9 @@ public abstract class AbstractEntityPersister
visitSubTypeAttributeMappings( fetchableConsumer );
}
else {
attributeMappings.forEach( fetchableConsumer );
for ( AttributeMapping attributeMapping : attributeMappings ) {
fetchableConsumer.accept( attributeMapping );
}
}
}
@ -6785,9 +6795,9 @@ public abstract class AbstractEntityPersister
return;
}
final int size = attributeMappings.size();
final int size = attributeMappings.length;
for ( int i = 0; i < size; i++ ) {
fetchableConsumer.accept( i, attributeMappings.get( i ) );
fetchableConsumer.accept( i, attributeMappings[i] );
}
if ( treatTargetType.isTypeOrSuperType( this ) ) {
if ( subclassMappingTypes != null ) {
@ -6810,7 +6820,9 @@ public abstract class AbstractEntityPersister
public void visitAttributeMappings(
Consumer<? super AttributeMapping> action,
EntityMappingType targetType) {
attributeMappings.forEach( action );
for ( AttributeMapping attributeMapping : attributeMappings ) {
action.accept( attributeMapping );
}
}
@Override
@ -6823,9 +6835,8 @@ public abstract class AbstractEntityPersister
@Override
public int forEachSelectable(int offset, SelectableConsumer selectableConsumer) {
int span = 0;
final List<AttributeMapping> mappings = getAttributeMappings();
for ( int i = 0; i < mappings.size(); i++ ) {
span += mappings.get( i ).forEachSelectable( span + offset, selectableConsumer );
for ( AttributeMapping attributeMapping : attributeMappings ) {
span += attributeMapping.forEachSelectable( span + offset, selectableConsumer );
}
return span;
}

View File

@ -6,8 +6,7 @@
*/
package org.hibernate.sql.results.graph.entity;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Collection;
import java.util.function.Consumer;
import org.hibernate.HibernateException;
@ -33,9 +32,10 @@ import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.internal.util.NullnessHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.loader.entity.CacheEntityLoaderHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
@ -64,6 +64,7 @@ import org.hibernate.type.Type;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable;
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
import static org.hibernate.internal.log.LoggingHelper.toLoggableString;
/**
@ -90,7 +91,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private final DomainResultAssembler versionAssembler;
private final DomainResultAssembler<Object> rowIdAssembler;
private final Map<AttributeMapping, DomainResultAssembler> assemblerMap;
private final DomainResultAssembler[][] assemblers;
// per-row state
private EntityPersister concreteDescriptor;
@ -167,17 +168,21 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
this.rowIdAssembler = null;
}
final Collection<EntityMappingType> subMappingTypes = rootEntityDescriptor.getSubMappingTypes();
assemblers = new DomainResultAssembler[subMappingTypes.size() + 1][];
assemblers[rootEntityDescriptor.getSubclassId()] = new DomainResultAssembler[rootEntityDescriptor.getNumberOfFetchables()];
for ( EntityMappingType subMappingType : subMappingTypes ) {
assemblers[subMappingType.getSubclassId()] = new DomainResultAssembler[subMappingType.getNumberOfFetchables()];
}
final int size = entityDescriptor.getNumberOfFetchables();
assemblerMap = new IdentityHashMap<>( CollectionHelper.determineProperSizing( size ) );
for ( int i = 0; i < size; i++ ) {
final AttributeMapping attributeMapping = (AttributeMapping) entityDescriptor.getFetchable( i );
// todo (6.0) : somehow we need to track whether all state is loaded/resolved
// note that lazy proxies or uninitialized collections count against
// that in the affirmative
final Fetch fetch = resultDescriptor.findFetch( attributeMapping );
final DomainResultAssembler<?> stateAssembler;
if ( fetch == null ) {
stateAssembler = new NullValueAssembler<>(
@ -188,7 +193,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
stateAssembler = fetch.createAssembler( this, creationState );
}
assemblerMap.put( attributeMapping, stateAssembler );
final int stateArrayPosition = attributeMapping.getStateArrayPosition();
final EntityMappingType declaringType = (EntityMappingType) attributeMapping.getDeclaringType();
assemblers[declaringType.getSubclassId()][stateArrayPosition] = stateAssembler;
for ( EntityMappingType subMappingType : declaringType.getSubMappingTypes() ) {
assemblers[subMappingType.getSubclassId()][stateArrayPosition] = stateAssembler;
}
}
}
@ -197,25 +207,24 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
Object[] source,
Object[] target,
EntityPersister concreteDescriptor) {
containerDescriptor.visitAttributeMappings(
attributeMapping -> {
if ( attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable() ) {
final int position = attributeMapping.getStateArrayPosition();
Object result;
if ( source[position] == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| source[position] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
result = source[position];
}
else {
result = attributeMapping.getAttributeMetadataAccess()
.resolveAttributeMetadata(null)
.getMutabilityPlan()
.deepCopy( source[position] );
}
target[position] = result;
}
final int numberOfAttributeMappings = containerDescriptor.getNumberOfAttributeMappings();
for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping = containerDescriptor.getAttributeMapping( i );
final AttributeMetadata attributeMetadata = attributeMapping.getAttributeMetadataAccess()
.resolveAttributeMetadata( concreteDescriptor );
if ( attributeMetadata.isUpdatable() ) {
final int position = attributeMapping.getStateArrayPosition();
Object result;
if ( source[position] == LazyPropertyInitializer.UNFETCHED_PROPERTY
|| source[position] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
result = source[position];
}
);
else {
result = attributeMetadata.getMutabilityPlan().deepCopy( source[position] );
}
target[position] = result;
}
}
}
@Override
@ -702,10 +711,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
entityDescriptor.setIdentifier( toInitialize, entityIdentifier, session );
resolvedEntityState = concreteDescriptor.extractConcreteTypeStateValues(
assemblerMap,
rowProcessingState
);
resolvedEntityState = extractConcreteTypeStateValues( rowProcessingState );
if ( isPersistentAttributeInterceptable( toInitialize ) ) {
PersistentAttributeInterceptor persistentAttributeInterceptor = asPersistentAttributeInterceptable( toInitialize ).$$_hibernate_getInterceptor();
@ -900,6 +906,24 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
}
private Object[] extractConcreteTypeStateValues(RowProcessingState rowProcessingState) {
final Object[] values = new Object[concreteDescriptor.getNumberOfAttributeMappings()];
final DomainResultAssembler[] concreteAssemblers = assemblers[concreteDescriptor.getSubclassId()];
for ( int i = 0; i < values.length; i++ ) {
final DomainResultAssembler assembler = concreteAssemblers[i];
final Object value;
if ( assembler == null ) {
value = UNFETCHED_PROPERTY;
}
else {
value = assembler.assemble( rowProcessingState );
}
values[i] = value;
}
return values;
}
private boolean skipInitialization(
Object toInitialize,
RowProcessingState rowProcessingState,

View File

@ -74,6 +74,7 @@ public class EntityMetamodel implements Serializable {
private final String rootName;
private EntityType entityType;
private final int subclassId;
private final IdentifierProperty identifierAttribute;
private final boolean versioned;
@ -151,6 +152,7 @@ public class EntityMetamodel implements Serializable {
// Improves performance of EntityKey#equals by avoiding content check in String#equals
name = persistentClass.getEntityName().intern();
rootName = persistentClass.getRootClass().getEntityName().intern();
subclassId = persistentClass.getSubclassId();
identifierAttribute = PropertyFactory.buildIdentifierAttribute(
persistentClass,
@ -892,6 +894,10 @@ public class EntityMetamodel implements Serializable {
return rootName;
}
public int getSubclassId() {
return subclassId;
}
public EntityType getEntityType() {
if ( entityType == null ) {
entityType = new ManyToOneType( name, getSessionFactory().getTypeConfiguration() );