diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
index b6241cf0aa..67a2a16856 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java
@@ -1737,12 +1737,17 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
/**
* Controls how the individual Loaders for an entity are created.
*
- * When `true` (the default), only the minimal set of Loaders are
- * created. These include the handling for {@link org.hibernate.LockMode#READ}
- * and {@link org.hibernate.LockMode#NONE} as well as specialized Loaders for
- * merge and refresh handling.
+ * When `true` (the default), the loaders are only created on first
+ * access; this ensures that all access patterns which are not useful
+ * to the application are never instantiated, possibly saving a
+ * substantial amount of memory for applications having many entities.
+ * The only exception is the loader for LockMode.NONE,
+ * which will always be eagerly initialized; this is necessary to
+ * detect mapping errors.
*
- * `false` indicates that all loaders should be created up front
+ * `false` indicates that all loaders should be created up front; this
+ * will consume more memory but ensures all necessary memory is
+ * allocated right away.
*
* @since 5.3
*/
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
index 1d6081740e..19aa1b9389 100755
--- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java
@@ -13,6 +13,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -291,4 +292,73 @@ public final class CollectionHelper {
return properties;
}
+
+ /**
+ * Use to convert sets which will be retained for a long time,
+ * such as for the lifetime of the Hibernate ORM instance.
+ * The returned Set might be immutable, but there is no guarantee of this:
+ * consider it immutable but don't rely on this.
+ * The goal is to save memory.
+ * @param set
+ * @param
+ * @return
+ */
+ public static Set toSmallSet(Set set) {
+ switch ( set.size() ) {
+ case 0:
+ return Collections.EMPTY_SET;
+ case 1:
+ return Collections.singleton( set.iterator().next() );
+ default:
+ //TODO assert tests pass even if this is set to return an unmodifiable Set
+ return set;
+ }
+ }
+
+ /**
+ * Use to convert Maps which will be retained for a long time,
+ * such as for the lifetime of the Hibernate ORM instance.
+ * The returned Map might be immutable, but there is no guarantee of this:
+ * consider it immutable but don't rely on this.
+ * The goal is to save memory.
+ * @param map
+ * @param
+ * @param
+ * @return
+ */
+ public static Map toSmallMap(final Map map) {
+ switch ( map.size() ) {
+ case 0:
+ return Collections.EMPTY_MAP;
+ case 1:
+ Map.Entry entry = map.entrySet().iterator().next();
+ return Collections.singletonMap( entry.getKey(), entry.getValue() );
+ default:
+ //TODO assert tests pass even if this is set to return an unmodifiable Map
+ return map;
+ }
+ }
+
+ /**
+ * Use to convert ArrayList instances which will be retained for a long time,
+ * such as for the lifetime of the Hibernate ORM instance.
+ * The returned List might be immutable, but there is no guarantee of this:
+ * consider it immutable but don't rely on this.
+ * The goal is to save memory.
+ * @param arrayList
+ * @param
+ * @return
+ */
+ public static List toSmallList(ArrayList arrayList) {
+ switch ( arrayList.size() ) {
+ case 0:
+ return Collections.EMPTY_LIST;
+ case 1:
+ return Collections.singletonList( arrayList.get( 0 ) );
+ default:
+ arrayList.trimToSize();
+ return arrayList;
+ }
+ }
+
}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LazyIndexedMap.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LazyIndexedMap.java
new file mode 100644
index 0000000000..1c04d400f5
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LazyIndexedMap.java
@@ -0,0 +1,84 @@
+/*
+ * 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.internal.util.collections;
+
+import java.util.Arrays;
+import java.util.function.Function;
+
+/**
+ * This is an internal data structure designed for very specific needs;
+ * it will most often be used as a replacement for EnumMap, although
+ * the focus on the Enum aspect is modelled as an int primitive:
+ * think of using the ordinals of an Enum to simulate the EnumMap.
+ * Proper abstraction of the indexing strategy is left to subclasses.
+ *
+ * There are various reasons to not expose the Enum on this class API;
+ * the primary is that in some of the cases in which we need the pattern,
+ * there is the need to hold an additional couple of values beyond the
+ * ones modelled by the Enum, essentially having some extra keys in the map;
+ * this could be modelled by defining a new Enum but that's also not ideal.
+ *
+ * Another reason is that the goal of this class is to allow the host to
+ * save memory, as we typically need to keep references to many of these
+ * objects for a long time; being able to refer purely to an array is
+ * less practical but gets us benefits in memory layout and total retained
+ * memory.
+ *
+ * @param the types used to model the key
+ * @param the types used to model the value
+ */
+public abstract class LazyIndexedMap {
+
+ private volatile Object[] values;
+ private static final Object NOT_INITIALIZED = new Object();
+
+ protected LazyIndexedMap(final int size) {
+ final Object[] vs = new Object[size];
+ //could rely on null, then then we should ensure that the valueGenerator function never returns null.
+ //Seems safer to guard with a custom token: the memory impact is negligible since it's a constant,
+ //and we're not needing to create these objects frequently.
+ Arrays.fill( vs, NOT_INITIALIZED );
+ this.values = vs;
+ }
+
+ /**
+ * Both the index and the original key are requested for efficiency reasons.
+ * It is the responsibility of the caller to ensure there is a 1:1 matching relation between them.
+ * @param index storage index in the array
+ * @param originalKey the original key object, used to efficiently pass it into the valueGenerator function
+ * @param valueGenerator if no value was generated before for this index, then the valueGenerator is invoked to
+ * associate a new value and store it into the internal array at the provided index.
+ * @return the associated value to this index/key.
+ */
+ protected V computeIfAbsent(final int index, final K1 originalKey, final Function valueGenerator) {
+ final Object value = this.values[index];
+ if ( value != NOT_INITIALIZED ) {
+ return (V) value;
+ }
+ else {
+ return lockedComputeIfAbsent( index, originalKey, valueGenerator );
+ }
+ }
+
+ private synchronized V lockedComputeIfAbsent(final int index, final K1 originalKey, final Function valueGenerator) {
+ //Get a fresh copy from the volatile read, while holding the global pessimistic lock in this:
+ final Object[] values = this.values;
+ final Object value = values[index];
+ //Check again
+ if ( value != NOT_INITIALIZED ) {
+ return (V) value;
+ }
+ else {
+ //Actually need to generate the value
+ final V generated = valueGenerator.apply( originalKey );
+ values[index] = generated;
+ //re-write on the volatile reference to publish any changes to the array
+ this.values = values;
+ return generated;
+ }
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LockModeEnumMap.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LockModeEnumMap.java
new file mode 100644
index 0000000000..9cddfd292f
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/LockModeEnumMap.java
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.util.collections;
+
+import java.util.function.Function;
+
+import org.hibernate.LockMode;
+
+/**
+ * A concurrent safe EnumMap<LockMode>, suitable to
+ * lazily associate values to the enum keys.
+ * This implementation favours fast read operations
+ * and low memory consumption over other metrics.
+ *
+ * Specifically designed with specific use cases in mind:
+ * do not overly reuse without good reasons.
+ *
+ * @param the value type to be associated with each key
+ */
+public final class LockModeEnumMap extends LazyIndexedMap {
+
+ private static final int ENUM_DIMENSION = LockMode.values().length;
+
+ public LockModeEnumMap() {
+ super( ENUM_DIMENSION );
+ }
+
+ public V computeIfAbsent(LockMode key, Function valueGenerator) {
+ return super.computeIfAbsent( key.ordinal(), key, valueGenerator );
+ }
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java
index fff0d058c9..96343cdafa 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java
@@ -7,6 +7,7 @@
package org.hibernate.metamodel.model.domain;
import java.io.Serializable;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
@@ -331,13 +332,18 @@ public abstract class AbstractIdentifiableType
throw new IllegalStateException( "Non-aggregated id attributes were already set" );
}
- for ( SingularPersistentAttribute idAttribute : (Set) idAttributes ) {
- if ( AbstractIdentifiableType.this == idAttribute.getDeclaringType() ) {
- addAttribute( idAttribute );
- }
+ if ( idAttributes.isEmpty() ) {
+ AbstractIdentifiableType.this.nonAggregatedIdAttributes = Collections.EMPTY_SET;
}
+ else {
+ for ( SingularPersistentAttribute idAttribute : (Set) idAttributes ) {
+ if ( AbstractIdentifiableType.this == idAttribute.getDeclaringType() ) {
+ addAttribute( idAttribute );
+ }
+ }
- AbstractIdentifiableType.this.nonAggregatedIdAttributes = (Set) idAttributes;
+ AbstractIdentifiableType.this.nonAggregatedIdAttributes = (Set) idAttributes;
+ }
}
@Override
diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java
index 1be37e14ba..e9816dc491 100644
--- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java
+++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractManagedType.java
@@ -9,6 +9,7 @@ package org.hibernate.metamodel.model.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -28,6 +29,7 @@ import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.graph.internal.SubGraphImpl;
import org.hibernate.graph.spi.SubGraphImplementor;
+import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.model.domain.internal.AttributeContainer;
import org.hibernate.metamodel.model.domain.internal.DomainModelHelper;
@@ -47,7 +49,7 @@ public abstract class AbstractManagedType
private final RepresentationMode representationMode;
private final Map> declaredSingularAttributes = new LinkedHashMap<>();
- private final Map> declaredPluralAttributes = new LinkedHashMap<>();
+ private volatile Map> declaredPluralAttributes ;
private final List subTypes = new ArrayList<>();
@@ -118,12 +120,21 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings("unchecked")
public Set> getDeclaredAttributes() {
- if ( declaredSingularAttributes.isEmpty() && declaredPluralAttributes.isEmpty() ) {
+ final boolean isDeclaredSingularAttributesEmpty = CollectionHelper.isEmpty( declaredSingularAttributes );
+ final boolean isDeclaredPluralAttributes = CollectionHelper.isEmpty( declaredPluralAttributes );
+ if ( isDeclaredSingularAttributesEmpty && isDeclaredPluralAttributes ) {
return Collections.emptySet();
}
-
- final HashSet attributes = new LinkedHashSet( declaredSingularAttributes.values() );
- attributes.addAll( declaredPluralAttributes.values() );
+ final HashSet attributes;
+ if ( !isDeclaredSingularAttributesEmpty ) {
+ attributes = new LinkedHashSet( declaredSingularAttributes.values() );
+ if ( !isDeclaredPluralAttributes ) {
+ attributes.addAll( declaredPluralAttributes.values() );
+ }
+ }
+ else {
+ attributes = new LinkedHashSet( declaredPluralAttributes.values() );
+ }
return attributes;
}
@@ -191,6 +202,9 @@ public abstract class AbstractManagedType
}
// next plural
+ if ( declaredPluralAttributes == null ) {
+ return null;
+ }
attribute = declaredPluralAttributes.get( name );
//noinspection RedundantIfStatement
if ( attribute != null ) {
@@ -347,18 +361,17 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings("unchecked")
public Set> getPluralAttributes() {
- final Set attributes = getDeclaredPluralAttributes();
-
+ HashSet attributes = declaredPluralAttributes == null ? new HashSet>() : new HashSet>( declaredPluralAttributes.values() );
if ( getSuperType() != null ) {
attributes.addAll( getSuperType().getPluralAttributes() );
}
-
return attributes;
}
@Override
public Set> getDeclaredPluralAttributes() {
- return new HashSet<>( declaredPluralAttributes.values() );
+ return declaredPluralAttributes == null ?
+ Collections.EMPTY_SET : new HashSet<>( declaredPluralAttributes.values() );
}
@Override
@@ -380,8 +393,9 @@ public abstract class AbstractManagedType
}
@Override
+ @SuppressWarnings("unchecked")
public PluralPersistentAttribute super J, ?, ?> findDeclaredPluralAttribute(String name) {
- return declaredPluralAttributes.get( name );
+ return declaredPluralAttributes == null ? null : declaredPluralAttributes.get( name );
}
private void checkTypeForPluralAttributes(
@@ -429,7 +443,7 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings( "unchecked")
public CollectionAttribute getDeclaredCollection(String name) {
- final PluralAttribute attribute = findDeclaredPluralAttribute( name );
+ final PluralPersistentAttribute super J, ?, ?> attribute = findDeclaredPluralAttribute( name );
basicCollectionCheck( attribute, name );
return ( CollectionAttribute ) attribute;
}
@@ -476,7 +490,7 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings( "unchecked")
public SetPersistentAttribute getDeclaredSet(String name) {
- final PluralAttribute attribute = findDeclaredPluralAttribute( name );
+ final PluralPersistentAttribute super J, ?, ?> attribute = findDeclaredPluralAttribute( name );
basicSetCheck( attribute, name );
return (SetPersistentAttribute) attribute;
}
@@ -523,7 +537,7 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings("unchecked")
public ListPersistentAttribute getDeclaredList(String name) {
- final PluralAttribute attribute = findDeclaredPluralAttribute( name );
+ final PluralPersistentAttribute super J, ?, ?> attribute = findDeclaredPluralAttribute( name );
basicListCheck( attribute, name );
return (ListPersistentAttribute) attribute;
}
@@ -570,7 +584,7 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings("unchecked")
public MapPersistentAttribute getDeclaredMap(String name) {
- final PluralAttribute attribute = findDeclaredPluralAttribute( name );
+ final PluralPersistentAttribute super J, ?, ?> attribute = findDeclaredPluralAttribute( name );
basicMapCheck( attribute, name );
return (MapPersistentAttribute) attribute;
}
@@ -598,7 +612,7 @@ public abstract class AbstractManagedType
@Override
@SuppressWarnings("unchecked")
public MapAttribute getDeclaredMap(String name, Class keyType, Class valueType) {
- final PluralAttribute attribute = findDeclaredPluralAttribute( name );
+ final PluralPersistentAttribute super J, ?, ?> attribute = findDeclaredPluralAttribute( name );
checkMapValueType( attribute, name, valueType );
final MapAttribute mapAttribute = ( MapAttribute ) attribute;
checkMapKeyType( mapAttribute, name, keyType );
@@ -645,7 +659,10 @@ public abstract class AbstractManagedType
declaredSingularAttributes.put( attribute.getName(), (SingularPersistentAttribute) attribute );
}
else if ( attribute instanceof PluralPersistentAttribute ) {
- declaredPluralAttributes.put(attribute.getName(), (PluralPersistentAttribute) attribute );
+ if ( AbstractManagedType.this.declaredPluralAttributes == null ) {
+ AbstractManagedType.this.declaredPluralAttributes = new HashMap<>();
+ }
+ AbstractManagedType.this.declaredPluralAttributes.put( attribute.getName(), (PluralPersistentAttribute) attribute );
}
else {
throw new IllegalArgumentException(
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 caecf101fb..6f7103fb96 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
@@ -78,6 +78,8 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.CascadeStyle;
+import org.hibernate.engine.spi.CascadingAction;
+import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryFactory;
@@ -105,6 +107,8 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.FilterHelper;
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.Expectations;
import org.hibernate.jdbc.TooManyRowsAffectedException;
@@ -296,7 +300,7 @@ public abstract class AbstractEntityPersister
private final boolean[] propertyUniqueness;
private final boolean[] propertySelectable;
- private final List lobProperties = new ArrayList<>();
+ private final List lobProperties;
//information about lazy properties of this class
private final String[] lazyPropertyNames;
@@ -333,10 +337,9 @@ public abstract class AbstractEntityPersister
// dynamic filters attached to the class-level
private final FilterHelper filterHelper;
- private final Set affectingFetchProfileNames = new HashSet<>();
+ private volatile Set affectingFetchProfileNames;
- private final Map uniqueKeyLoaders = new HashMap();
- private final Map lockers = new HashMap();
+ private final LockModeEnumMap lockers = new LockModeEnumMap<>();
// SQL strings
private String sqlVersionSelectString;
@@ -811,6 +814,7 @@ public abstract class AbstractEntityPersister
ArrayList lazyTypes = new ArrayList();
ArrayList lazyColAliases = new ArrayList();
+ final ArrayList lobPropertiesLocalCollector = new ArrayList<>();
iter = bootDescriptor.getPropertyClosureIterator();
i = 0;
boolean foundFormula = false;
@@ -873,12 +877,13 @@ public abstract class AbstractEntityPersister
propertyUniqueness[i] = prop.getValue().isAlternateUniqueKey();
if ( prop.isLob() && dialect.forceLobAsLastValue() ) {
- lobProperties.add( i );
+ lobPropertiesLocalCollector.add( i );
}
i++;
}
+ this.lobProperties = CollectionHelper.toSmallList( lobPropertiesLocalCollector );
hasFormulaProperties = foundFormula;
lazyPropertyColumnAliases = ArrayHelper.to2DStringArray( lazyColAliases );
lazyPropertyNames = ArrayHelper.toStringArray( lazyNames );
@@ -2148,25 +2153,12 @@ public abstract class AbstractEntityPersister
}
}
- protected void initLockers() {
- lockers.put( LockMode.READ, generateLocker( LockMode.READ ) );
- lockers.put( LockMode.UPGRADE, generateLocker( LockMode.UPGRADE ) );
- lockers.put( LockMode.UPGRADE_NOWAIT, generateLocker( LockMode.UPGRADE_NOWAIT ) );
- lockers.put( LockMode.UPGRADE_SKIPLOCKED, generateLocker( LockMode.UPGRADE_SKIPLOCKED ) );
- lockers.put( LockMode.FORCE, generateLocker( LockMode.FORCE ) );
- lockers.put( LockMode.PESSIMISTIC_READ, generateLocker( LockMode.PESSIMISTIC_READ ) );
- lockers.put( LockMode.PESSIMISTIC_WRITE, generateLocker( LockMode.PESSIMISTIC_WRITE ) );
- lockers.put( LockMode.PESSIMISTIC_FORCE_INCREMENT, generateLocker( LockMode.PESSIMISTIC_FORCE_INCREMENT ) );
- lockers.put( LockMode.OPTIMISTIC, generateLocker( LockMode.OPTIMISTIC ) );
- lockers.put( LockMode.OPTIMISTIC_FORCE_INCREMENT, generateLocker( LockMode.OPTIMISTIC_FORCE_INCREMENT ) );
- }
-
protected LockingStrategy generateLocker(LockMode lockMode) {
return factory.getDialect().getLockingStrategy( this, lockMode );
}
private LockingStrategy getLocker(LockMode lockMode) {
- return (LockingStrategy) lockers.get( lockMode );
+ return lockers.computeIfAbsent( lockMode, this::generateLocker );
}
public void lock(
@@ -4576,6 +4568,9 @@ public abstract class AbstractEntityPersister
}
public void registerAffectingFetchProfile(String fetchProfileName) {
+ if ( affectingFetchProfileNames == null ) {
+ this.affectingFetchProfileNames = new HashSet<>();
+ }
affectingFetchProfileNames.add( fetchProfileName );
}
@@ -4590,9 +4585,12 @@ public abstract class AbstractEntityPersister
@Override
public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers loadQueryInfluencers) {
- for ( String s : loadQueryInfluencers.getEnabledFetchProfileNames() ) {
- if ( affectingFetchProfileNames.contains( s ) ) {
- return true;
+ final Set fetchProfileNames = this.affectingFetchProfileNames;
+ if ( fetchProfileNames != null ) {
+ for ( String s : loadQueryInfluencers.getEnabledFetchProfileNames() ) {
+ if ( fetchProfileNames.contains( s ) ) {
+ return true;
+ }
}
}
return false;
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
index f14cbcab7a..fd37e9955b 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractPropertyMapping.java
@@ -41,7 +41,11 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractPropertyMapping.class );
private final Map typesByPropertyPath = new HashMap<>();
- private final Set duplicateIncompatiblePaths = new HashSet<>();
+
+ //This field is only used during initialization, no need for threadsafety:
+ //FIXME get rid of the field, or at least clear it after boot? Not urgent as we typically won't initialize it at all.
+ private Set duplicateIncompatiblePaths = null;
+
private final Map columnsByPropertyPath = new HashMap<>();
private final Map columnReadersByPropertyPath = new HashMap<>();
private final Map columnReaderTemplatesByPropertyPath = new HashMap<>();
@@ -168,7 +172,7 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
String[] formulaTemplates,
Mapping factory) {
Type existingType = typesByPropertyPath.get( path );
- if ( existingType != null || duplicateIncompatiblePaths.contains( path ) ) {
+ if ( existingType != null || ( duplicateIncompatiblePaths != null && duplicateIncompatiblePaths.contains( path ) ) ) {
// If types match or the new type is not an association type, there is nothing for us to do
if ( type == existingType || existingType == null || !( type instanceof AssociationType ) ) {
logDuplicateRegistration( path, existingType, type );
@@ -212,6 +216,9 @@ public abstract class AbstractPropertyMapping implements PropertyMapping {
logIncompatibleRegistration( path, existingType, type );
}
if ( commonType == null ) {
+ if ( duplicateIncompatiblePaths == null ) {
+ duplicateIncompatiblePaths = new HashSet<>();
+ }
duplicateIncompatiblePaths.add( path );
typesByPropertyPath.remove( path );
// Set everything to empty to signal action has to be taken!
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
index e6bcaf1b4d..87b66c20c1 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
@@ -641,8 +641,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
subclassNamesBySubclassTable = buildSubclassNamesBySubclassTableMapping( persistentClass, factory );
- initLockers();
-
initSubclassPropertyAliasesMap( persistentClass );
postConstruct( creationContext.getMetadata() );
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
index 9cb182baa1..df09ba6224 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java
@@ -8,6 +8,7 @@ package org.hibernate.persister.entity;
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -31,6 +32,7 @@ import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.MutableInteger;
import org.hibernate.internal.util.collections.ArrayHelper;
+import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Join;
@@ -103,7 +105,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
private final int[] subclassFormulaTableNumberClosure;
// discriminator column
- private final Map