HHH-16683 Micro optimisations for MappingMetamodelImpl.getEntityDescriptor

This commit is contained in:
Sanne Grinovero 2023-05-24 10:00:41 +01:00 committed by Sanne Grinovero
parent 1d30b0161f
commit 5c60b632a1
3 changed files with 115 additions and 8 deletions

View File

@ -16,6 +16,7 @@ import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonTransientException;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.model.domain.internal.EntityPersisterConcurrentMap;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
@ -33,7 +34,7 @@ public class MappingModelCreationProcess {
* Triggers creation of the mapping model
*/
public static void process(
Map<String,EntityPersister> entityPersisterMap,
EntityPersisterConcurrentMap entityPersisterMap,
RuntimeModelCreationContext creationContext) {
final MappingModelCreationProcess process = new MappingModelCreationProcess(
entityPersisterMap,
@ -42,14 +43,14 @@ public class MappingModelCreationProcess {
process.execute();
}
private final Map<String,EntityPersister> entityPersisterMap;
private final EntityPersisterConcurrentMap entityPersisterMap;
private final RuntimeModelCreationContext creationContext;
private String currentlyProcessingRole;
private List<PostInitCallbackEntry> postInitCallbacks;
private MappingModelCreationProcess(
Map<String, EntityPersister> entityPersisterMap,
EntityPersisterConcurrentMap entityPersisterMap,
RuntimeModelCreationContext creationContext) {
this.entityPersisterMap = entityPersisterMap;
this.creationContext = creationContext;

View File

@ -0,0 +1,104 @@
/*
* 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.metamodel.model.domain.internal;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.hibernate.persister.entity.EntityPersister;
/**
* Concurrent Map implementation of mappings entity name -> EntityPersister.
* Concurrency is optimised for read operations; write operations will
* acquire a lock and are relatively costly: only use for long living,
* read-mostly use cases.
* This implementation attempts to avoid type pollution problems.
*/
public final class EntityPersisterConcurrentMap {
private final ConcurrentHashMap<String,EntityPersisterHolder> map = new ConcurrentHashMap<>();
private volatile EntityPersister[] values = new EntityPersister[0];
private volatile String[] keys = new String[0];
public EntityPersister get(final String name) {
final EntityPersisterHolder entityPersisterHolder = map.get( name );
if ( entityPersisterHolder != null ) {
return entityPersisterHolder.entityPersister;
}
return null;
}
public EntityPersister[] values() {
return values;
}
public synchronized void put(final String name, final EntityPersister entityPersister) {
map.put( name, new EntityPersisterHolder( entityPersister ) );
recomputeValues();
}
public synchronized void putIfAbsent(final String name, final EntityPersister entityPersister) {
map.putIfAbsent( name, new EntityPersisterHolder( entityPersister ) );
recomputeValues();
}
public boolean containsKey(final String name) {
return map.containsKey( name );
}
public String[] keys() {
return keys;
}
private void recomputeValues() {
//Assumption: the write lock is being held (synchronize on this)
final int size = map.size();
final EntityPersister[] newValues = new EntityPersister[size];
final String[] newKeys = new String[size];
int i = 0;
for ( Map.Entry<String, EntityPersisterHolder> e : map.entrySet() ) {
newValues[i] = e.getValue().entityPersister;
newKeys[i] = e.getKey();
i++;
}
this.values = newValues;
this.keys = newKeys;
}
/**
* @deprecated Higly inefficient - do not use; this exists
* to support other deprecated methods and will be removed.
*/
@Deprecated(forRemoval = true)
public Map<String, EntityPersister> convertToMap() {
return map.entrySet().stream().collect( Collectors.toUnmodifiableMap(
Map.Entry::getKey,
e -> e.getValue().entityPersister
) );
}
/**
* Implementation note: since EntityPersister is an highly used
* interface, we intentionally avoid using a generic Map referring
* to it to avoid type pollution.
* Using a concrete holder class bypasses the problem, at a minimal
* tradeoff of memory.
*/
private final static class EntityPersisterHolder {
private final EntityPersister entityPersister;
EntityPersisterHolder(final EntityPersister entityPersister) {
Objects.requireNonNull( entityPersister );
this.entityPersister = entityPersister;
}
}
}

View File

@ -115,7 +115,7 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RuntimeModel
private final Map<String, EntityPersister> entityPersisterMap = new ConcurrentHashMap<>();
private final EntityPersisterConcurrentMap entityPersisterMap = new EntityPersisterConcurrentMap();
private final Map<String, CollectionPersister> collectionPersisterMap = new ConcurrentHashMap<>();
private final Map<String, Set<String>> collectionRolesByEntityParticipant = new ConcurrentHashMap<>();
@ -375,12 +375,14 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
@Override
public void forEachEntityDescriptor(Consumer<EntityPersister> action) {
entityPersisterMap.values().forEach( action );
for ( EntityPersister value : entityPersisterMap.values() ) {
action.accept( value );
}
}
@Override
public Stream<EntityPersister> streamEntityDescriptors() {
return entityPersisterMap.values().stream();
return Arrays.stream( entityPersisterMap.values() );
}
@Override
@ -547,7 +549,7 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
@Override @SuppressWarnings("deprecation")
public Map<String, EntityPersister> entityPersisters() {
return entityPersisterMap;
return entityPersisterMap.convertToMap();
}
@Override @SuppressWarnings("deprecation")
@ -634,7 +636,7 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
@Override
public String[] getAllEntityNames() {
return ArrayHelper.toStringArray( entityPersisterMap.keySet() );
return entityPersisterMap.keys();
}
@Override