HHH-18733 - Add a prepareMappingModel phase to collection persisters

Co-authored-by: Christian Beikov <christian.beikov@gmail.com>
This commit is contained in:
Steve Ebersole 2024-10-15 18:02:06 -05:00
parent 84cc5f013c
commit d647599fe9
23 changed files with 803 additions and 379 deletions

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.type.AnyDiscriminatorValueStrategy;
import org.hibernate.type.AnyType;
import org.hibernate.type.Type;
import org.hibernate.type.MappingContext;
@ -301,9 +302,13 @@ public class Any extends SimpleValue {
}
}
/**
* The discriminator {@linkplain Value}
*/
public static class MetaValue extends SimpleValue {
private String typeName;
private String columnName;
private AnyDiscriminatorValueStrategy valueStrategy;
private final Consumer<Selectable> selectableConsumer;
@ -396,6 +401,10 @@ public class Any extends SimpleValue {
return columnName != null
&& getType().getColumnSpan( mappingContext ) == 1;
}
public AnyDiscriminatorValueStrategy getValueStrategy() {
return valueStrategy;
}
}
public static class KeyValue extends SimpleValue {

View File

@ -22,6 +22,7 @@ import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.resource.beans.spi.ProvidedInstanceManagedBeanImpl;
import org.hibernate.type.AnyDiscriminatorValueStrategy;
import org.hibernate.type.AnyType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.CustomCollectionType;
@ -123,15 +124,22 @@ public final class MappingHelper {
}
public static AnyType anyMapping(
Type metaType,
Type discriminatorType,
Type identifierType,
Map<Object, String> metaValueToEntityNameMap,
Map<Object, String> explicitValeMappings,
boolean lazy,
MetadataBuildingContext buildingContext) {
if ( metaValueToEntityNameMap != null ) {
metaType = new MetaType( metaValueToEntityNameMap, metaType );
return anyMapping( discriminatorType, identifierType, AnyDiscriminatorValueStrategy.AUTO, explicitValeMappings, lazy, buildingContext );
}
public static AnyType anyMapping(
Type discriminatorType,
Type identifierType,
AnyDiscriminatorValueStrategy discriminatorValueStrategy,
Map<Object, String> explicitValeMappings,
boolean lazy,
MetadataBuildingContext buildingContext) {
final MetaType metaType = new MetaType( discriminatorType, discriminatorValueStrategy, explicitValeMappings );
return new AnyType( buildingContext.getBootstrapContext().getTypeConfiguration(), metaType, identifierType, lazy );
}

View File

@ -1,88 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.metamodel.mapping.internal.DiscriminatorValueDetailsImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Implements the default discriminator assignment strategy defined by JPA,
* that is, the discriminator value is the JPA (unqualified) entity name.
* This strategy is used when no explicit discriminator mapping is specified.
*
* @author Gavin King
*/
public class DefaultDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R> {
public static <O,R> DefaultDiscriminatorConverter<O,R> fromMappingMetamodel(
NavigableRole role,
JavaType<O> domainJavaType,
BasicType<R> underlyingJdbcMapping,
MappingMetamodelImplementor mappingMetamodel) {
return new DefaultDiscriminatorConverter<>(
role,
domainJavaType,
underlyingJdbcMapping.getJavaTypeDescriptor(),
mappingMetamodel
);
}
private final MappingMetamodelImplementor mappingMetamodel;
public DefaultDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
MappingMetamodelImplementor mappingMetamodel) {
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.mappingMetamodel = mappingMetamodel;
}
@Override
public DiscriminatorValueDetails getDetailsForRelationalForm(R relationalForm) {
return getDetailsForDiscriminatorValue( relationalForm );
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
if ( persister!= null ) {
return new DiscriminatorValueDetailsImpl( entityName, persister );
}
throw new AssertionFailure( "Unrecognized entity name: " + entityName );
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
if ( value instanceof String ) {
String entityName = mappingMetamodel.getImportedName( (String) value );
EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
if ( persister!= null ) {
return new DiscriminatorValueDetailsImpl( entityName, persister );
}
}
throw new HibernateException( "Unrecognized discriminator value: " + value );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails,X> handler) {
return null;
}
}

View File

@ -1,151 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.internal.DiscriminatorValueDetailsImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.CharacterJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.StringJavaType;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR;
import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR;
/**
* Conversion of discriminator values between the entity name/Class domain form and
* its generally CHARACTER or INTEGER based relational form
*
* @param <O> The domain type - either <ul>
* <li>
* the {@linkplain EntityMappingType#getMappedJavaType() entity Class} for unnamed entities
* </li>
* <li>
* the {@linkplain EntityMappingType#getEntityName() entity name} for named entities
* </li>
* </ul>
* @param <R> The Java type of the relational form of the discriminator
*
* @author Steve Ebersole
*/
public class MappedDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R> {
public static <O,R> MappedDiscriminatorConverter<O,R> fromValueMappings(
NavigableRole role,
JavaType<O> domainJavaType,
BasicType<R> underlyingJdbcMapping,
Map<Object,String> valueMappings,
MappingMetamodelImplementor mappingMetamodel) {
final List<DiscriminatorValueDetails> valueDetailsList = CollectionHelper.arrayList( valueMappings.size() );
valueMappings.forEach( (value, entityName) -> {
final DiscriminatorValueDetails valueDetails = new DiscriminatorValueDetailsImpl(
value,
mappingMetamodel.getEntityDescriptor( entityName )
);
valueDetailsList.add( valueDetails );
} );
return new MappedDiscriminatorConverter<>(
role,
domainJavaType,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueDetailsList
);
}
private final Map<Object, DiscriminatorValueDetails> discriminatorValueToEntityNameMap;
private final Map<String,DiscriminatorValueDetails> entityNameToDiscriminatorValueMap;
public MappedDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
List<DiscriminatorValueDetails> valueMappings) {
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.discriminatorValueToEntityNameMap = CollectionHelper.concurrentMap( valueMappings.size() );
this.entityNameToDiscriminatorValueMap = CollectionHelper.concurrentMap( valueMappings.size() );
valueMappings.forEach( (valueDetails) -> {
discriminatorValueToEntityNameMap.put( valueDetails.getValue(), valueDetails );
entityNameToDiscriminatorValueMap.put( valueDetails.getIndicatedEntityName(), valueDetails );
} );
}
@Override
public DiscriminatorValueDetails getDetailsForRelationalForm(R relationalForm) {
return getDetailsForDiscriminatorValue( relationalForm );
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
DiscriminatorValueDetails valueDetails = entityNameToDiscriminatorValueMap.get( entityName );
if ( valueDetails!= null) {
return valueDetails;
}
throw new AssertionFailure( "Unrecognized entity name: " + entityName );
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
if ( value == null ) {
return discriminatorValueToEntityNameMap.get( NULL_DISCRIMINATOR );
}
final DiscriminatorValueDetails valueMatch = discriminatorValueToEntityNameMap.get( value );
if ( valueMatch != null ) {
return valueMatch;
}
final DiscriminatorValueDetails notNullMatch = discriminatorValueToEntityNameMap.get( NOT_NULL_DISCRIMINATOR );
if ( notNullMatch != null ) {
return notNullMatch;
}
if ( value.getClass().isEnum() ) {
final Object enumValue;
if ( getRelationalJavaType() instanceof StringJavaType ) {
enumValue = ( (Enum) value ).name();
}
else if ( getRelationalJavaType() instanceof CharacterJavaType ) {
enumValue = ( (Enum) value ).name().charAt( 0 );
}
else {
enumValue = ( (Enum) value ).ordinal();
}
final DiscriminatorValueDetails enumMatch = discriminatorValueToEntityNameMap.get( enumValue );
if ( enumMatch != null ) {
return enumMatch;
}
}
throw new HibernateException( "Unrecognized discriminator value: " + value );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
discriminatorValueToEntityNameMap.forEach( (value, detail) -> consumer.accept( detail ) );
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails,X> handler) {
for ( DiscriminatorValueDetails detail : discriminatorValueToEntityNameMap.values() ) {
final X result = handler.apply( detail );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -4,23 +4,19 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.Map;
import java.util.function.BiConsumer;
import org.hibernate.AssertionFailure;
import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.metamodel.mapping.DefaultDiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappedDiscriminatorConverter;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole;
@ -39,10 +35,14 @@ import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.type.AnyDiscriminatorValueStrategy;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.ClassJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import java.util.Map;
import java.util.function.BiConsumer;
/**
* Acts as a ModelPart for the discriminator portion of an any-valued mapping
*
@ -84,6 +84,7 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
boolean partitioned,
BasicType<?> underlyingJdbcMapping,
Map<Object,String> valueToEntityNameMap,
AnyDiscriminatorValueStrategy valueStrategy,
MappingMetamodelImplementor mappingMetamodel) {
this.navigableRole = partRole;
this.declaringType = declaringType;
@ -100,20 +101,54 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
this.partitioned = partitioned;
this.underlyingJdbcMapping = underlyingJdbcMapping;
this.valueConverter = valueToEntityNameMap.isEmpty()
? DefaultDiscriminatorConverter.fromMappingMetamodel(
this.valueConverter = determineDiscriminatorConverter(
partRole,
ClassJavaType.INSTANCE,
underlyingJdbcMapping,
valueToEntityNameMap,
valueStrategy,
mappingMetamodel
)
: MappedDiscriminatorConverter.fromValueMappings(
);
}
public static DiscriminatorConverter<?, ?> determineDiscriminatorConverter(
NavigableRole partRole,
BasicType<?> underlyingJdbcMapping,
Map<Object, String> valueToEntityNameMap,
AnyDiscriminatorValueStrategy valueStrategy,
MappingMetamodelImplementor mappingMetamodel) {
if ( valueStrategy == AnyDiscriminatorValueStrategy.AUTO ) {
if ( valueToEntityNameMap == null || valueToEntityNameMap.isEmpty() ) {
valueStrategy = AnyDiscriminatorValueStrategy.IMPLICIT;
}
else {
valueStrategy = AnyDiscriminatorValueStrategy.EXPLICIT;
}
}
return switch ( valueStrategy ) {
case AUTO -> throw new AssertionFailure( "Not expecting AUTO" );
case MIXED -> new MixedDiscriminatorConverter<>(
partRole,
ClassJavaType.INSTANCE,
underlyingJdbcMapping,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueToEntityNameMap,
mappingMetamodel
);
case EXPLICIT -> new ExplicitDiscriminatorConverter<>(
partRole,
ClassJavaType.INSTANCE,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueToEntityNameMap,
mappingMetamodel
);
case IMPLICIT -> new ImplicitDiscriminatorConverter<>(
partRole,
ClassJavaType.INSTANCE,
underlyingJdbcMapping.getJavaTypeDescriptor(),
valueToEntityNameMap,
mappingMetamodel
);
};
}
public DiscriminatorConverter<?,?> getValueConverter() {

View File

@ -89,6 +89,7 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
bootValueMapping.isPartitionKey(),
(BasicType<?>) metaType.getBaseType(),
metaType.getDiscriminatorValuesToEntityNameMap(),
metaType.getValueStrategy(),
creationProcess.getCreationContext().getSessionFactory().getMappingMetamodel()
);

View File

@ -0,0 +1,130 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.descriptor.java.CharacterJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.StringJavaType;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.util.Locale.ROOT;
import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR;
import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR;
/**
* @author Steve Ebersole
*/
public class ExplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<O, R> {
private final NavigableRole discriminatorRole;
private final Map<Object, DiscriminatorValueDetails> detailsByValue;
private final Map<String,DiscriminatorValueDetails> detailsByEntityName;
public ExplicitDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
Map<Object, String> explicitValueMappings,
MappingMetamodelImplementor mappingMetamodel) {
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.discriminatorRole = discriminatorRole;
if ( CollectionHelper.isEmpty( explicitValueMappings ) ) {
throw new MappingException( String.format(
ROOT,
"No explicit ANY discriminator mappings (%s)",
discriminatorRole.getFullPath()
) );
}
this.detailsByValue = CollectionHelper.concurrentMap( explicitValueMappings.size() );
this.detailsByEntityName = CollectionHelper.concurrentMap( explicitValueMappings.size() );
explicitValueMappings.forEach( (value, entityName) -> {
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor( entityName );
final DiscriminatorValueDetails details = new DiscriminatorValueDetailsImpl( value, entityDescriptor );
detailsByValue.put( value, details );
detailsByEntityName.put( entityDescriptor.getEntityName(), details );
} );
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object relationalForm) {
if ( relationalForm == null ) {
return detailsByValue.get( NULL_DISCRIMINATOR );
}
final DiscriminatorValueDetails existing = detailsByValue.get( relationalForm );
if ( existing != null ) {
// an explicit or previously-resolved mapping
return existing;
}
final DiscriminatorValueDetails notNullMatch = detailsByValue.get( NOT_NULL_DISCRIMINATOR );
if ( notNullMatch != null ) {
return notNullMatch;
}
if ( relationalForm.getClass().isEnum() ) {
final Object enumValue;
if ( getRelationalJavaType() instanceof StringJavaType ) {
enumValue = ( (Enum) relationalForm ).name();
}
else if ( getRelationalJavaType() instanceof CharacterJavaType ) {
enumValue = ( (Enum) relationalForm ).name().charAt( 0 );
}
else {
enumValue = ( (Enum) relationalForm ).ordinal();
}
final DiscriminatorValueDetails enumMatch = detailsByValue.get( enumValue );
if ( enumMatch != null ) {
return enumMatch;
}
}
throw new HibernateException( String.format(
ROOT,
"Unknown discriminator value (%s) : %s",
discriminatorRole,
relationalForm
) );
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
final DiscriminatorValueDetails valueDetails = detailsByEntityName.get( entityName );
if ( valueDetails != null) {
return valueDetails;
}
throw new HibernateException( "Unknown entity name (" + discriminatorRole + ") : " + entityName );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
detailsByEntityName.forEach( (value, detail) -> consumer.accept( detail ) );
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails, X> handler) {
for ( DiscriminatorValueDetails detail : detailsByEntityName.values() ) {
final X result = handler.apply( detail );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -0,0 +1,109 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.descriptor.java.JavaType;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import static java.util.Locale.ROOT;
/**
* @author Steve Ebersole
*/
public class ImplicitDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R> {
private final NavigableRole discriminatorRole;
private final MappingMetamodelImplementor mappingMetamodel;
private final Map<Object, DiscriminatorValueDetails> detailsByValue;
private final Map<String,DiscriminatorValueDetails> detailsByEntityName;
public ImplicitDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
Map<Object,String> explicitValueMappings,
MappingMetamodelImplementor mappingMetamodel) {
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.discriminatorRole = discriminatorRole;
this.mappingMetamodel = mappingMetamodel;
if ( CollectionHelper.isNotEmpty( explicitValueMappings ) ) {
throw new MappingException( String.format(
ROOT,
"Encountered explicit ANY discriminator mappings (%s)",
discriminatorRole.getFullPath()
) );
}
this.detailsByValue = CollectionHelper.concurrentMap( 8 );
this.detailsByEntityName = CollectionHelper.concurrentMap( 8 );
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object value) {
if ( value instanceof String incoming ) {
final DiscriminatorValueDetails existingDetails = detailsByValue.get( incoming );
if ( existingDetails != null ) {
return existingDetails;
}
final String entityName = mappingMetamodel.getImportedName( incoming );
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
if ( persister != null ) {
assert persister.getImportedName().equals( incoming );
return register( incoming, persister );
}
}
throw new HibernateException( String.format(
ROOT,
"Unrecognized discriminator value (%s): %s",
discriminatorRole.getFullPath(),
value
) );
}
private DiscriminatorValueDetails register(Object value, EntityPersister entityDescriptor) {
final DiscriminatorValueDetails details = new DiscriminatorValueDetailsImpl( value, entityDescriptor );
detailsByValue.put( value, details );
detailsByEntityName.put( entityDescriptor.getImportedName(), details );
return details;
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
final DiscriminatorValueDetails existingDetails = detailsByEntityName.get( entityName );
if ( existingDetails != null ) {
return existingDetails;
}
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( entityName );
if ( persister!= null ) {
return register( persister.getImportedName(), persister );
}
throw new HibernateException( String.format(
ROOT,
"Unrecognized entity name (%s): %s",
discriminatorRole.getFullPath(),
entityName
) );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails,X> handler) {
return null;
}
}

View File

@ -0,0 +1,24 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping.internal;
/**
* Defines the ability to perform post-creation processing for collection mappings.
*
* @apiNote Called after all {@linkplain InFlightEntityMappingType} processing has
* occurred, allowing access to the runtime mapping model of the entities.
*
* @author Steve Ebersole
*/
public interface InFlightCollectionMapping {
/**
* After all hierarchy types have been linked, this method is called to allow the
* mapping model to be prepared which generally includes creating attribute mapping
* descriptors, identifier mapping descriptor, etc.
*/
default void prepareMappingModel(MappingModelCreationProcess creationProcess) {
// by default do nothing - support for legacy impls
}
}

View File

@ -7,6 +7,8 @@ package org.hibernate.metamodel.mapping.internal;
import org.hibernate.metamodel.mapping.EntityMappingType;
/**
* Defines the ability to perform post-creation processing for entity mappings.
*
* @author Steve Ebersole
*/
public interface InFlightEntityMappingType extends EntityMappingType {

View File

@ -16,6 +16,7 @@ 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.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
@ -32,15 +33,18 @@ public class MappingModelCreationProcess {
*/
public static void process(
EntityPersisterConcurrentMap entityPersisterMap,
Map<String, CollectionPersister> collectionPersisterMap,
RuntimeModelCreationContext creationContext) {
final MappingModelCreationProcess process = new MappingModelCreationProcess(
entityPersisterMap,
collectionPersisterMap,
creationContext
);
process.execute();
}
private final EntityPersisterConcurrentMap entityPersisterMap;
private final Map<String, CollectionPersister> collectionPersisterMap;
private final RuntimeModelCreationContext creationContext;
private String currentlyProcessingRole;
@ -48,8 +52,10 @@ public class MappingModelCreationProcess {
private MappingModelCreationProcess(
EntityPersisterConcurrentMap entityPersisterMap,
Map<String, CollectionPersister> collectionPersisterMap,
RuntimeModelCreationContext creationContext) {
this.entityPersisterMap = entityPersisterMap;
this.collectionPersisterMap = collectionPersisterMap;
this.creationContext = creationContext;
}
@ -83,6 +89,12 @@ public class MappingModelCreationProcess {
}
}
for ( CollectionPersister collectionPersister : collectionPersisterMap.values() ) {
if ( collectionPersister instanceof InFlightCollectionMapping ) {
((InFlightCollectionMapping) collectionPersister).prepareMappingModel( this );
}
}
executePostInitCallbacks();
}

View File

@ -0,0 +1,133 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.HibernateException;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.descriptor.java.CharacterJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.StringJavaType;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.hibernate.persister.entity.DiscriminatorHelper.NOT_NULL_DISCRIMINATOR;
import static org.hibernate.persister.entity.DiscriminatorHelper.NULL_DISCRIMINATOR;
/**
* DiscriminatorConverter for use with {@linkplain org.hibernate.type.AnyDiscriminatorValueStrategy#MIXED}
*
* @author Steve Ebersole
*/
public class MixedDiscriminatorConverter<O,R> extends DiscriminatorConverter<O,R> {
private final NavigableRole discriminatorRole;
private final Map<Object, DiscriminatorValueDetails> detailsByValue;
private final Map<String,DiscriminatorValueDetails> detailsByEntityName;
private final MappingMetamodelImplementor mappingMetamodel;
public MixedDiscriminatorConverter(
NavigableRole discriminatorRole,
JavaType<O> domainJavaType,
JavaType<R> relationalJavaType,
Map<Object,String> explicitValueMappings,
MappingMetamodelImplementor mappingMetamodel) {
super( discriminatorRole.getFullPath(), domainJavaType, relationalJavaType );
this.discriminatorRole = discriminatorRole;
this.mappingMetamodel = mappingMetamodel;
this.detailsByValue = CollectionHelper.concurrentMap( explicitValueMappings.size() );
this.detailsByEntityName = CollectionHelper.concurrentMap( explicitValueMappings.size() );
explicitValueMappings.forEach( (value,entityName) -> {
String importedEntityName = mappingMetamodel.getImportedName( entityName );
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor( importedEntityName );
register( value, entityDescriptor );
} );
}
private DiscriminatorValueDetails register(Object value, EntityPersister entityDescriptor) {
final DiscriminatorValueDetails details = new DiscriminatorValueDetailsImpl( value, entityDescriptor );
detailsByValue.put( value, details );
detailsByEntityName.put( entityDescriptor.getEntityName(), details );
return details;
}
@Override
public DiscriminatorValueDetails getDetailsForDiscriminatorValue(Object relationalForm) {
if ( relationalForm == null ) {
return detailsByValue.get( NULL_DISCRIMINATOR );
}
final DiscriminatorValueDetails existing = detailsByValue.get( relationalForm );
if ( existing != null ) {
// an explicit or previously-resolved mapping
return existing;
}
final DiscriminatorValueDetails notNullMatch = detailsByValue.get( NOT_NULL_DISCRIMINATOR );
if ( notNullMatch != null ) {
return notNullMatch;
}
if ( relationalForm.getClass().isEnum() ) {
final Object enumValue;
if ( getRelationalJavaType() instanceof StringJavaType ) {
enumValue = ( (Enum) relationalForm ).name();
}
else if ( getRelationalJavaType() instanceof CharacterJavaType ) {
enumValue = ( (Enum) relationalForm ).name().charAt( 0 );
}
else {
enumValue = ( (Enum) relationalForm ).ordinal();
}
final DiscriminatorValueDetails enumMatch = detailsByValue.get( enumValue );
if ( enumMatch != null ) {
return enumMatch;
}
}
if ( relationalForm instanceof String assumedEntityName ) {
// Assume the relational form is the entity name
final EntityPersister persister = mappingMetamodel.findEntityDescriptor( assumedEntityName );
if ( persister != null ) {
return register( assumedEntityName, persister );
}
}
throw new HibernateException( "Cannot interpret discriminator value (" + discriminatorRole + ") : " + relationalForm );
}
@Override
public DiscriminatorValueDetails getDetailsForEntityName(String entityName) {
final DiscriminatorValueDetails existing = detailsByEntityName.get( entityName );
if ( existing != null) {
return existing;
}
final EntityPersister entityDescriptor = mappingMetamodel.getEntityDescriptor( entityName );
return register( entityName, entityDescriptor );
}
@Override
public void forEachValueDetail(Consumer<DiscriminatorValueDetails> consumer) {
detailsByEntityName.forEach( (value, detail) -> consumer.accept( detail ) );
}
@Override
public <X> X fromValueDetails(Function<DiscriminatorValueDetails, X> handler) {
for ( DiscriminatorValueDetails detail : detailsByEntityName.values() ) {
final X result = handler.apply( detail );
if ( result != null ) {
return result;
}
}
return null;
}
}

View File

@ -6,8 +6,6 @@ package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Column;
import org.hibernate.metamodel.mapping.DefaultDiscriminatorConverter;
import org.hibernate.metamodel.mapping.MappedDiscriminatorConverter;
import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.model.domain.SimpleDomainType;
@ -15,12 +13,13 @@ import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.MetaType;
import org.hibernate.type.descriptor.java.ClassJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import java.util.List;
import static org.hibernate.metamodel.mapping.internal.AnyDiscriminatorPart.determineDiscriminatorConverter;
/**
* @author Steve Ebersole
*/
@ -42,21 +41,14 @@ public class AnyMappingDomainTypeImpl<T> implements AnyMappingDomainType<T> {
final BasicType discriminatorBaseType = (BasicType) discriminatorType.getBaseType();
final NavigableRole navigableRole = resolveNavigableRole( bootAnyMapping );
anyDiscriminatorType = new ConvertedBasicTypeImpl<>(
anyDiscriminatorType = new ConvertedBasicTypeImpl(
navigableRole.getFullPath(),
discriminatorBaseType.getJdbcType(),
bootAnyMapping.getMetaValues().isEmpty()
? DefaultDiscriminatorConverter.fromMappingMetamodel(
determineDiscriminatorConverter(
navigableRole,
ClassJavaType.INSTANCE,
discriminatorBaseType,
mappingMetamodel
)
: MappedDiscriminatorConverter.fromValueMappings(
navigableRole,
ClassJavaType.INSTANCE,
discriminatorBaseType,
bootAnyMapping.getMetaValues(),
discriminatorType.getValueStrategy(),
mappingMetamodel
)
);

View File

@ -195,7 +195,7 @@ public class MappingMetamodelImpl extends QueryParameterBindingTypeResolverImpl
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// after *all* persisters and named queries are registered
MappingModelCreationProcess.process( entityPersisterMap, context );
MappingModelCreationProcess.process( entityPersisterMap, collectionPersisterMap, context );
for ( EntityPersister persister : entityPersisterMap.values() ) {
persister.postInstantiate();

View File

@ -4,18 +4,8 @@
*/
package org.hibernate.persister.collection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import jakarta.persistence.metamodel.PluralAttribute;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.AssertionFailure;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
@ -60,7 +50,6 @@ import org.hibernate.loader.ast.internal.LoaderSqlAstCreationState;
import org.hibernate.loader.ast.spi.BatchLoaderFactory;
import org.hibernate.loader.ast.spi.CollectionLoader;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
@ -72,15 +61,21 @@ import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedCollectionPart;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.InFlightCollectionMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.PluralAttributeMappingImpl;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
@ -129,15 +124,25 @@ import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
import org.hibernate.sql.results.graph.internal.ImmutableFetchList;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.type.AnyType;
import org.hibernate.type.BasicType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.MetaType;
import org.hibernate.type.Type;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Consumer;
import static org.hibernate.internal.util.StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
@ -154,7 +159,7 @@ import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER
*/
@Internal
public abstract class AbstractCollectionPersister
implements CollectionPersister, CollectionMutationTarget, PluralAttributeMappingImpl.Aware, FetchProfileAffectee, Joinable {
implements CollectionPersister, InFlightCollectionMapping, CollectionMutationTarget, PluralAttributeMappingImpl.Aware, FetchProfileAffectee, Joinable {
private final NavigableRole navigableRole;
private final CollectionSemantics<?,?> collectionSemantics;
@ -164,13 +169,13 @@ public abstract class AbstractCollectionPersister
protected final String qualifiedTableName;
private final CollectionTableMapping tableMapping;
private final String sqlSelectSizeString;
private final String sqlDetectRowByIndexString;
private final String sqlDetectRowByElementString;
private String sqlSelectSizeString;
private String sqlDetectRowByIndexString;
private String sqlDetectRowByElementString;
protected final boolean hasWhere;
protected final String sqlWhereString;
private final String sqlWhereStringTemplate;
protected boolean hasWhere;
protected String sqlWhereString;
private String sqlWhereStringTemplate;
private final boolean hasOrder;
private final boolean hasManyToManyOrder;
@ -244,10 +249,14 @@ public abstract class AbstractCollectionPersister
private PluralAttributeMapping attributeMapping;
private volatile Set<String> affectingFetchProfiles;
private Collection collectionBootDescriptor;
public AbstractCollectionPersister(
Collection collectionBootDescriptor,
@Nullable CollectionDataAccess cacheAccessStrategy,
RuntimeModelCreationContext creationContext) throws MappingException, CacheException {
this.collectionBootDescriptor = collectionBootDescriptor;
this.factory = creationContext.getSessionFactory();
this.collectionSemantics = creationContext.getBootstrapContext()
.getMetadataBuildingOptions()
@ -295,49 +304,6 @@ public abstract class AbstractCollectionPersister
spaces[i] = tables.next();
}
String where = collectionBootDescriptor.getWhere();
/*
* Add the predicate on the role in the WHERE clause before creating the SQL queries.
*/
if ( mappedByProperty != null && elementType instanceof EntityType ) {
final String entityName = ( (EntityType) elementType ).getAssociatedEntityName();
final PersistentClass persistentClass = creationContext.getBootModel().getEntityBinding( entityName );
final Property property = persistentClass.getRecursiveProperty( mappedByProperty );
final Value propertyValue = property.getValue();
if ( propertyValue instanceof Any ) {
final Any any = (Any) propertyValue;
final BasicValue discriminatorDescriptor = any.getDiscriminatorDescriptor();
final AnyType anyType = any.getType();
final MetaType metaType = (MetaType) anyType.getDiscriminatorType();
final Object discriminatorValue = metaType.getEntityNameToDiscriminatorValueMap().get( ownerPersister.getEntityName() );
final BasicType<Object> discriminatorBaseType = (BasicType<Object>) metaType.getBaseType();
final String discriminatorLiteral = discriminatorBaseType.getJdbcLiteralFormatter().toJdbcLiteral(
discriminatorValue,
creationContext.getDialect(),
creationContext.getSessionFactory().getWrapperOptions()
);
where = getNonEmptyOrConjunctionIfBothNonEmpty(
where,
discriminatorDescriptor.getColumn().getText() + "=" + discriminatorLiteral
);
}
}
if ( StringHelper.isNotEmpty( where ) ) {
hasWhere = true;
sqlWhereString = "(" + where + ")";
sqlWhereStringTemplate = Template.renderWhereStringTemplate(
sqlWhereString,
dialect,
creationContext.getTypeConfiguration()
);
}
else {
hasWhere = false;
sqlWhereString = null;
sqlWhereStringTemplate = null;
}
hasOrphanDelete = collectionBootDescriptor.hasOrphanDelete();
batchSize = collectionBootDescriptor.getBatchSize() < 0
@ -512,12 +478,6 @@ public abstract class AbstractCollectionPersister
identifierGenerator = null;
}
// GENERATE THE SQL:
sqlSelectSizeString = generateSelectSizeString( collectionBootDescriptor.isIndexed() && !collectionBootDescriptor.isMap() );
sqlDetectRowByIndexString = generateDetectRowByIndexString();
sqlDetectRowByElementString = generateDetectRowByElementString();
isLazy = collectionBootDescriptor.isLazy();
isExtraLazy = collectionBootDescriptor.isExtraLazy();
@ -592,6 +552,119 @@ public abstract class AbstractCollectionPersister
&& creationContext.getDialect().supportsCascadeDelete();
}
@Override
public void prepareMappingModel(MappingModelCreationProcess creationProcess) {
if ( mappedByProperty != null && elementType instanceof EntityType ) {
final String entityName = ((EntityType) elementType).getAssociatedEntityName();
final PersistentClass persistentClass = creationProcess.getCreationContext().getBootModel().getEntityBinding( entityName );
final Property property = persistentClass.getRecursiveProperty( mappedByProperty );
final Value propertyValue = property.getValue();
if ( propertyValue instanceof Any ) {
// we want to delay processing of where-fragment, and therefore affected SQL, until
// all model parts are ready so that we can access details about the ANY discriminator
creationProcess.registerInitializationCallback(
"Where-fragment handling for ANY inverse mapping : " + navigableRole,
() -> {
final EntityPersister entityPersister = creationProcess.getEntityPersister( entityName );
delayedWhereFragmentProcessing( entityPersister, mappedByProperty, collectionBootDescriptor, creationProcess );
buildStaticWhereFragmentSensitiveSql();
collectionBootDescriptor = null;
return true;
}
);
return;
}
}
if ( StringHelper.isNotEmpty( collectionBootDescriptor.getWhere() ) ) {
hasWhere = true;
sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ")";
sqlWhereStringTemplate = Template.renderWhereStringTemplate(
sqlWhereString,
dialect,
creationProcess.getCreationContext().getTypeConfiguration()
);
}
buildStaticWhereFragmentSensitiveSql();
collectionBootDescriptor = null;
}
private void delayedWhereFragmentProcessing(
EntityPersister entityPersister,
String mappedByProperty,
Collection collectionBootDescriptor,
MappingModelCreationProcess creationProcess) {
String where = collectionBootDescriptor.getWhere();
final AttributeMapping mappedByAttribute = resolveMappedBy( entityPersister, mappedByProperty, creationProcess );
if ( mappedByAttribute instanceof DiscriminatedAssociationAttributeMapping ) {
final DiscriminatedAssociationAttributeMapping anyMapping = (DiscriminatedAssociationAttributeMapping) mappedByAttribute;
final DiscriminatorConverter<?, ?> valueConverter = anyMapping.getDiscriminatorMapping().getValueConverter();
final DiscriminatorValueDetails discriminatorValueDetails = valueConverter.getDetailsForEntityName( ownerPersister.getEntityName() );
//noinspection unchecked
final String discriminatorLiteral = anyMapping.getDiscriminatorMapping().getUnderlyingJdbcMapping().getJdbcLiteralFormatter().toJdbcLiteral(
discriminatorValueDetails.getValue(),
creationProcess.getCreationContext().getDialect(),
creationProcess.getCreationContext().getSessionFactory().getWrapperOptions()
);
where = getNonEmptyOrConjunctionIfBothNonEmpty(
where,
anyMapping.getDiscriminatorMapping().getSelectableName() + "=" + discriminatorLiteral
);
}
if ( StringHelper.isNotEmpty( where ) ) {
hasWhere = true;
sqlWhereString = "(" + where + ")";
sqlWhereStringTemplate = Template.renderWhereStringTemplate(
sqlWhereString,
dialect,
creationProcess.getCreationContext().getTypeConfiguration()
);
}
}
private void buildStaticWhereFragmentSensitiveSql() {
sqlSelectSizeString = generateSelectSizeString( hasIndex() && !isMap() );
sqlDetectRowByIndexString = generateDetectRowByIndexString();
sqlDetectRowByElementString = generateDetectRowByElementString();
}
private boolean isMap() {
return collectionSemantics.getCollectionClassification().toJpaClassification() == PluralAttribute.CollectionType.MAP;
}
private static AttributeMapping resolveMappedBy(EntityPersister entityPersister, String mappedByProperty, MappingModelCreationProcess creationProcess) {
final StringTokenizer propertyPathParts = new StringTokenizer( mappedByProperty, ".", false );
assert propertyPathParts.countTokens() > 0;
if ( propertyPathParts.countTokens() == 1 ) {
return entityPersister.findAttributeMapping( propertyPathParts.nextToken() );
}
ManagedMappingType source = entityPersister;
while ( propertyPathParts.hasMoreTokens() ) {
final String partName = propertyPathParts.nextToken();
final AttributeMapping namedPart = source.findAttributeMapping( partName );
if ( !propertyPathParts.hasMoreTokens() ) {
return namedPart;
}
source = (ManagedMappingType) namedPart.getPartMappingType();
}
throw new MappingException(
String.format(
Locale.ROOT,
"Unable to resolve mapped-by path : (%s) %s",
entityPersister.getEntityName(),
mappedByProperty
)
);
}
private BeforeExecutionGenerator createGenerator(RuntimeModelCreationContext context, IdentifierCollection collection) {
final Generator generator =
collection.getIdentifier()

View File

@ -165,7 +165,6 @@ import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappedDiscriminatorConverter;
import org.hibernate.metamodel.mapping.MappingModelHelper;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
@ -185,6 +184,7 @@ import org.hibernate.loader.ast.internal.EntityConcreteTypeLoader;
import org.hibernate.metamodel.mapping.internal.EntityRowIdMappingImpl;
import org.hibernate.metamodel.mapping.internal.EntityVersionMappingImpl;
import org.hibernate.metamodel.mapping.internal.ExplicitColumnDiscriminatorMappingImpl;
import org.hibernate.metamodel.mapping.internal.ExplicitDiscriminatorConverter;
import org.hibernate.metamodel.mapping.internal.GeneratedValuesProcessor;
import org.hibernate.metamodel.mapping.internal.ImmutableAttributeMappingList;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
@ -337,6 +337,7 @@ public abstract class AbstractEntityPersister
private final EntityEntryFactory entityEntryFactory;
private final String sqlAliasStem;
private final String jpaEntityName;
private SingleIdEntityLoader<?> singleIdLoader;
private MultiIdEntityLoader<?> multiIdLoader;
@ -473,6 +474,7 @@ public abstract class AbstractEntityPersister
final EntityDataAccess cacheAccessStrategy,
final NaturalIdDataAccess naturalIdRegionAccessStrategy,
final RuntimeModelCreationContext creationContext) throws HibernateException {
this.jpaEntityName = persistentClass.getJpaEntityName();
//set it here, but don't call it, since it's still uninitialized!
factory = creationContext.getSessionFactory();
@ -2279,10 +2281,10 @@ public abstract class AbstractEntityPersister
}
//noinspection rawtypes
final DiscriminatorConverter converter = MappedDiscriminatorConverter.fromValueMappings(
final DiscriminatorConverter converter = new ExplicitDiscriminatorConverter(
getNavigableRole().append( EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME ),
domainJavaType,
underlingJdbcMapping,
underlingJdbcMapping.getRelationalJavaType(),
getSubclassByDiscriminatorValue(),
factory.getMappingMetamodel()
);
@ -3776,6 +3778,11 @@ public abstract class AbstractEntityPersister
return entityMetamodel.getName();
}
@Override
public @Nullable String getJpaEntityName() {
return jpaEntityName;
}
@Override
public boolean isInherited() {
return entityMetamodel.isInherited();

View File

@ -10,6 +10,8 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import jakarta.persistence.Entity;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.LockMode;
@ -37,6 +39,7 @@ import org.hibernate.generator.values.GeneratedValues;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.TableGroupFilterAliasGenerator;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
import org.hibernate.loader.ast.spi.NaturalIdLoader;
@ -178,6 +181,16 @@ public interface EntityPersister extends EntityMappingType, EntityMutationTarget
*/
String getEntityName();
/**
* The {@linkplain Entity#name() JPA entity name}, if one, associated with the entity.
*/
@Nullable
String getJpaEntityName();
default String getImportedName() {
return getJpaEntityName() != null ? getJpaEntityName() : StringHelper.unqualifyEntityName( getEntityName() );
}
/**
* The strategy to use for SQM mutation statements where the target entity
* has multiple tables. Returns {@code null} to indicate that the entity

View File

@ -0,0 +1,55 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.type;
import org.hibernate.annotations.AnyDiscriminatorValue;
/**
* Describes how to deal with discriminator values in regard to
* a {@linkplain org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart ANY mapping}
*
* @see AnyDiscriminatorValue
*
* @author Steve Ebersole
*/
public enum AnyDiscriminatorValueStrategy {
/**
* Pick between {@link #IMPLICIT} and {@link #EXPLICIT} based on
* presence or not of {@link AnyDiscriminatorValue}. The default
* (and legacy) behavior.
*/
AUTO,
/**
* Expect explicit, complete mapping of discriminator values using
* one-or-more {@link AnyDiscriminatorValue}.
*
* @implNote With this option, it is considered an error if, at runtime,
* we encounter an entity type not explicitly mapped with a
* {@link AnyDiscriminatorValue}.
*/
EXPLICIT,
/**
* Expect no {@link AnyDiscriminatorValue}. The entity name of the associated
* entity is used as the discriminator value.
*
* @implNote With this option, it is considered illegal to specify
* any {@link AnyDiscriminatorValue} mappings.
*/
IMPLICIT,
/**
* Allows a combination of {@linkplain #EXPLICIT explicit} and {@linkplain #IMPLICIT implicit}
* discriminator value mappings. If an entity is mapped using an explicit
* {@link AnyDiscriminatorValue} mapping, the associated discriminator value is used.
* Otherwise, the entity name is used.
*
* @implNote This option is roughly the same as {@link #EXPLICIT} except
* that an implicit mapping using the entity name is used when a matching
* explicit {@link AnyDiscriminatorValue} mapping is not found.
*/
MIXED
}

View File

@ -43,16 +43,37 @@ import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
public class AnyType extends AbstractType implements CompositeType, AssociationType {
private final TypeConfiguration typeConfiguration;
private final Type identifierType;
private final Type discriminatorType;
private final MetaType discriminatorType;
private final boolean eager;
public AnyType(TypeConfiguration typeConfiguration, Type discriminatorType, Type identifierType, boolean lazy) {
public AnyType(TypeConfiguration typeConfiguration, MetaType discriminatorType, Type identifierType, boolean lazy) {
this.typeConfiguration = typeConfiguration;
this.discriminatorType = discriminatorType;
this.identifierType = identifierType;
this.eager = !lazy;
}
/**
* @deprecated Use {@linkplain AnyType#AnyType(TypeConfiguration, MetaType, Type, boolean)} instead
*/
@Deprecated
public AnyType(TypeConfiguration typeConfiguration, Type discriminatorType, Type identifierType, boolean lazy) {
this(
typeConfiguration,
wrapDiscriminatorType( discriminatorType ),
identifierType,
lazy
);
}
private static MetaType wrapDiscriminatorType(Type discriminatorType) {
if ( discriminatorType instanceof MetaType metaType ) {
return metaType;
}
return new MetaType( discriminatorType, AnyDiscriminatorValueStrategy.AUTO, null );
}
public Type getIdentifierType() {
return identifierType;
}

View File

@ -4,47 +4,68 @@
*/
package org.hibernate.type;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.persister.entity.DiscriminatorMetadata;
import org.hibernate.persister.entity.DiscriminatorType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* @author Gavin King
*
* @deprecated The functionality of MetaType, {@link DiscriminatorType} and {@link DiscriminatorMetadata} have been
* consolidated into {@link EntityDiscriminatorMapping} and {@link DiscriminatorConverter}
*/
@Deprecated( since = "6.2", forRemoval = true )
@Internal
public class MetaType extends AbstractType {
public static final String[] REGISTRATION_KEYS = ArrayHelper.EMPTY_STRING_ARRAY;
private final Type baseType;
private final Type valueType;
private final AnyDiscriminatorValueStrategy valueStrategy;
private final Map<Object,String> discriminatorValuesToEntityNameMap;
private final Map<String,Object> entityNameToDiscriminatorValueMap;
public MetaType(Map<Object,String> discriminatorValuesToEntityNameMap, Type baseType) {
this.baseType = baseType;
this.discriminatorValuesToEntityNameMap = discriminatorValuesToEntityNameMap;
public MetaType(
Type valueType,
AnyDiscriminatorValueStrategy valueStrategy,
Map<Object,String> explicitValueMappings) {
this.valueType = valueType;
if ( explicitValueMappings == null || explicitValueMappings.isEmpty() ) {
if ( valueStrategy == AnyDiscriminatorValueStrategy.AUTO ) {
valueStrategy = AnyDiscriminatorValueStrategy.IMPLICIT;
}
this.discriminatorValuesToEntityNameMap = new HashMap<>();
this.entityNameToDiscriminatorValueMap = new HashMap<>();
}
else {
if ( valueStrategy == AnyDiscriminatorValueStrategy.AUTO ) {
valueStrategy = AnyDiscriminatorValueStrategy.EXPLICIT;
}
this.discriminatorValuesToEntityNameMap = explicitValueMappings;
this.entityNameToDiscriminatorValueMap = new HashMap<>();
for ( Map.Entry<Object,String> entry : discriminatorValuesToEntityNameMap.entrySet() ) {
entityNameToDiscriminatorValueMap.put( entry.getValue(), entry.getKey() );
}
}
this.valueStrategy = valueStrategy;
}
public MetaType(Map<Object,String> discriminatorValuesToEntityNameMap, Type baseType) {
this( baseType, AnyDiscriminatorValueStrategy.AUTO, discriminatorValuesToEntityNameMap );
}
public Type getBaseType() {
return baseType;
return valueType;
}
public AnyDiscriminatorValueStrategy getValueStrategy() {
return valueStrategy;
}
public String[] getRegistrationKeys() {
@ -60,12 +81,12 @@ public class MetaType extends AbstractType {
}
public int[] getSqlTypeCodes(MappingContext mappingContext) throws MappingException {
return baseType.getSqlTypeCodes( mappingContext );
return valueType.getSqlTypeCodes( mappingContext );
}
@Override
public int getColumnSpan(MappingContext mapping) throws MappingException {
return baseType.getColumnSpan(mapping);
return valueType.getColumnSpan(mapping);
}
@Override
@ -84,7 +105,8 @@ public class MetaType extends AbstractType {
Object value,
int index,
SharedSessionContractImplementor session) throws HibernateException, SQLException {
baseType.nullSafeSet(st, value==null ? null : entityNameToDiscriminatorValueMap.get(value), index, session);
throw new UnsupportedOperationException();
// baseType.nullSafeSet(st, value==null ? null : entityNameToDiscriminatorValueMap.get(value), index, session);
}
@Override
@ -122,7 +144,7 @@ public class MetaType extends AbstractType {
@Override
public String getName() {
return baseType.getName(); //TODO!
return valueType.getName(); //TODO!
}
@Override

View File

@ -13,6 +13,7 @@ import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
@ -142,6 +143,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return null;
}
@Override
public @Nullable String getJpaEntityName() {
return null;
}
@Override
public TableDetails getMappedTableDetails() {
throw new UnsupportedOperationException();

View File

@ -13,6 +13,7 @@ import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -172,6 +173,11 @@ public class PersisterClassProviderTest {
return null;
}
@Override
public @Nullable String getJpaEntityName() {
return null;
}
@Override
public TableDetails getMappedTableDetails() {
throw new UnsupportedOperationException();

View File

@ -134,6 +134,11 @@ public class CustomPersister implements EntityPersister {
return Custom.class.getName();
}
@Override
public @Nullable String getJpaEntityName() {
return Custom.class.getSimpleName();
}
@Override
public TableDetails getMappedTableDetails() {
throw new UnsupportedOperationException();