HHH-16491 Special handling for generic component properties

This commit is contained in:
Marco Belladelli 2023-04-20 17:58:10 +02:00
parent 5c2657d27c
commit 725c292227
10 changed files with 162 additions and 44 deletions

View File

@ -141,6 +141,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final Map<String,PersistentClass> entityBindingMap = new HashMap<>(); private final Map<String,PersistentClass> entityBindingMap = new HashMap<>();
private final List<Component> composites = new ArrayList<>(); private final List<Component> composites = new ArrayList<>();
private final Map<Class<?>, Component> genericComponentsMap = new HashMap<>();
private final Map<String,Collection> collectionBindingMap = new HashMap<>(); private final Map<String,Collection> collectionBindingMap = new HashMap<>();
private final Map<String, FilterDefinition> filterDefinitionMap = new HashMap<>(); private final Map<String, FilterDefinition> filterDefinitionMap = new HashMap<>();
@ -282,6 +283,16 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
composites.forEach( consumer ); composites.forEach( consumer );
} }
@Override
public void registerGenericComponent(Component component) {
genericComponentsMap.put( component.getComponentClass(), component );
}
@Override
public Component getGenericComponent(Class<?> componentClass) {
return genericComponentsMap.get( componentClass );
}
@Override @Override
public SessionFactoryBuilder getSessionFactoryBuilder() { public SessionFactoryBuilder getSessionFactoryBuilder() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
@ -2314,6 +2325,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
options, options,
entityBindingMap, entityBindingMap,
composites, composites,
genericComponentsMap,
mappedSuperClasses, mappedSuperClasses,
collectionBindingMap, collectionBindingMap,
typeDefRegistry.copyRegistrationMap(), typeDefRegistry.copyRegistrationMap(),

View File

@ -90,6 +90,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private final Map<String,PersistentClass> entityBindingMap; private final Map<String,PersistentClass> entityBindingMap;
private final List<Component> composites; private final List<Component> composites;
private final Map<Class<?>, Component> genericComponentsMap;
private final Map<Class<?>, MappedSuperclass> mappedSuperclassMap; private final Map<Class<?>, MappedSuperclass> mappedSuperclassMap;
private final Map<String,Collection> collectionBindingMap; private final Map<String,Collection> collectionBindingMap;
private final Map<String, TypeDefinition> typeDefinitionMap; private final Map<String, TypeDefinition> typeDefinitionMap;
@ -110,6 +111,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
MetadataBuildingOptions metadataBuildingOptions, MetadataBuildingOptions metadataBuildingOptions,
Map<String, PersistentClass> entityBindingMap, Map<String, PersistentClass> entityBindingMap,
List<Component> composites, List<Component> composites,
Map<Class<?>, Component> genericComponentsMap,
Map<Class<?>, MappedSuperclass> mappedSuperclassMap, Map<Class<?>, MappedSuperclass> mappedSuperclassMap,
Map<String, Collection> collectionBindingMap, Map<String, Collection> collectionBindingMap,
Map<String, TypeDefinition> typeDefinitionMap, Map<String, TypeDefinition> typeDefinitionMap,
@ -129,6 +131,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
this.metadataBuildingOptions = metadataBuildingOptions; this.metadataBuildingOptions = metadataBuildingOptions;
this.entityBindingMap = entityBindingMap; this.entityBindingMap = entityBindingMap;
this.composites = composites; this.composites = composites;
this.genericComponentsMap = genericComponentsMap;
this.mappedSuperclassMap = mappedSuperclassMap; this.mappedSuperclassMap = mappedSuperclassMap;
this.collectionBindingMap = collectionBindingMap; this.collectionBindingMap = collectionBindingMap;
this.typeDefinitionMap = typeDefinitionMap; this.typeDefinitionMap = typeDefinitionMap;
@ -570,6 +573,11 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
composites.forEach( consumer ); composites.forEach( consumer );
} }
@Override
public Component getGenericComponent(Class<?> componentClass) {
return genericComponentsMap.get( componentClass );
}
@Override @Override
public org.hibernate.type.Type getIdentifierType(String entityName) throws MappingException { public org.hibernate.type.Type getIdentifierType(String entityName) throws MappingException {
final PersistentClass pc = entityBindingMap.get( entityName ); final PersistentClass pc = entityBindingMap.get( entityName );

View File

@ -229,7 +229,39 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
return join; return join;
} }
/**
* Embeddable classes can be defined using generics. For this reason, we must check
* every property value and specially handle generic components by setting the property
* as generic, to later be able to resolve its concrete type, and creating a new component
* with correctly typed sub-properties for the metamodel.
*/
public static void handleGenericComponentProperty(Property property, MetadataBuildingContext context) {
final Value value = property.getValue();
if ( value instanceof Component ) {
final Component component = (Component) value;
if ( component.isGeneric() && context.getMetadataCollector()
.getGenericComponent( component.getComponentClass() ) == null ) {
// If we didn't already, register the generic component to use it later
// as the metamodel type for generic embeddable attributes
final Component copy = component.copy();
copy.setGeneric( false );
copy.getProperties().clear();
for ( Property prop : component.getProperties() ) {
prepareActualProperty(
prop,
component.getComponentClass(),
true,
context,
copy::addProperty
);
}
context.getMetadataCollector().registerGenericComponent( copy );
}
}
}
private void addPropertyToPersistentClass(Property property, XClass declaringClass) { private void addPropertyToPersistentClass(Property property, XClass declaringClass) {
handleGenericComponentProperty( property, getContext() );
if ( declaringClass != null ) { if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass ); final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
if ( inheritanceState == null ) { if ( inheritanceState == null ) {
@ -253,10 +285,10 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
private void addPropertyToMappedSuperclass(Property prop, XClass declaringClass) { private void addPropertyToMappedSuperclass(Property prop, XClass declaringClass) {
final Class<?> type = getContext().getBootstrapContext().getReflectionManager().toClass( declaringClass ); final Class<?> type = getContext().getBootstrapContext().getReflectionManager().toClass( declaringClass );
final MappedSuperclass superclass = getContext().getMetadataCollector().getMappedSuperclass( type ); final MappedSuperclass superclass = getContext().getMetadataCollector().getMappedSuperclass( type );
prepareActualPropertyForSuperclass( prop, type, true, getContext(), superclass::addDeclaredProperty ); prepareActualProperty( prop, type, true, getContext(), superclass::addDeclaredProperty );
} }
static void prepareActualPropertyForSuperclass( static void prepareActualProperty(
Property prop, Property prop,
Class<?> type, Class<?> type,
boolean allowCollections, boolean allowCollections,
@ -322,14 +354,20 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
} }
if ( value instanceof Component ) { if ( value instanceof Component ) {
final Component component = ( (Component) value ); final Component component = ( (Component) value );
final Iterator<Property> propertyIterator = component.getPropertyIterator(); final Class<?> componentClass = component.getComponentClass();
while ( propertyIterator.hasNext() ) { if ( component.isGeneric() ) {
Property property = propertyIterator.next(); actualProperty.setValue( context.getMetadataCollector().getGenericComponent( componentClass ) );
try { }
property.getGetter( component.getComponentClass() ); else {
} final Iterator<Property> propertyIterator = component.getPropertyIterator();
catch (PropertyNotFoundException e) { while ( propertyIterator.hasNext() ) {
propertyIterator.remove(); Property property = propertyIterator.next();
try {
property.getGetter( componentClass );
}
catch (PropertyNotFoundException e) {
propertyIterator.remove();
}
} }
} }
} }
@ -366,9 +404,8 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
} }
else if ( value instanceof Component ) { else if ( value instanceof Component ) {
final Component component = (Component) value; final Component component = (Component) value;
// Avoid setting component class name to java.lang.Object // Avoid setting type name for generic components
// for embeddable types with generic type parameters if ( !component.isGeneric() ) {
if ( !typeName.equals( Object.class.getName() ) ) {
component.setComponentClassName( typeName ); component.setComponentClassName( typeName );
} }
if ( component.getTypeName() != null ) { if ( component.getTypeName() != null ) {

View File

@ -8,7 +8,6 @@ package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -29,7 +28,6 @@ import jakarta.persistence.Version;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.annotations.Any; import org.hibernate.annotations.Any;
import org.hibernate.annotations.AttributeBinderType; import org.hibernate.annotations.AttributeBinderType;
import org.hibernate.annotations.CompositeType; import org.hibernate.annotations.CompositeType;
@ -71,6 +69,8 @@ import static jakarta.persistence.FetchType.LAZY;
import static org.hibernate.boot.model.internal.AnyBinder.bindAny; import static org.hibernate.boot.model.internal.AnyBinder.bindAny;
import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId; import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal; import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.handleGenericComponentProperty;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.prepareActualProperty;
import static org.hibernate.boot.model.internal.CollectionBinder.bindCollection; import static org.hibernate.boot.model.internal.CollectionBinder.bindCollection;
import static org.hibernate.boot.model.internal.GeneratorBinder.createForeignGenerator; import static org.hibernate.boot.model.internal.GeneratorBinder.createForeignGenerator;
import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGenerator; import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGenerator;
@ -339,18 +339,13 @@ public class PropertyBinder {
} }
private void setDeclaredIdentifier(RootClass rootClass, MappedSuperclass superclass, Property prop) { private void setDeclaredIdentifier(RootClass rootClass, MappedSuperclass superclass, Property prop) {
handleGenericComponentProperty( prop, buildingContext );
if ( superclass == null ) { if ( superclass == null ) {
rootClass.setDeclaredIdentifierProperty( prop ); rootClass.setDeclaredIdentifierProperty( prop );
return; return;
} }
final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager().toClass( declaringClass ); final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager().toClass( declaringClass );
ClassPropertyHolder.prepareActualPropertyForSuperclass( prepareActualProperty( prop, type, false, buildingContext, superclass::setDeclaredIdentifierProperty );
prop,
type,
false,
buildingContext,
superclass::setDeclaredIdentifierProperty
);
} }
private Component getOrCreateCompositeId(RootClass rootClass) { private Component getOrCreateCompositeId(RootClass rootClass) {

View File

@ -244,6 +244,10 @@ public abstract class AbstractDelegatingMetadata implements MetadataImplementor
delegate().visitRegisteredComponents( consumer ); delegate().visitRegisteredComponents( consumer );
} }
@Override
public Component getGenericComponent(Class<?> componentClass) {
return delegate().getGenericComponent( componentClass );
}
@Override @Override
public NamedObjectRepository buildNamedQueryRepository(SessionFactoryImplementor sessionFactory) { public NamedObjectRepository buildNamedQueryRepository(SessionFactoryImplementor sessionFactory) {

View File

@ -83,6 +83,8 @@ public interface InFlightMetadataCollector extends MetadataImplementor {
void registerComponent(Component component); void registerComponent(Component component);
void registerGenericComponent(Component component);
/** /**
* Adds an import (for use in HQL). * Adds an import (for use in HQL).
* *

View File

@ -56,4 +56,6 @@ public interface MetadataImplementor extends Metadata {
void initSessionFactory(SessionFactoryImplementor sessionFactoryImplementor); void initSessionFactory(SessionFactoryImplementor sessionFactoryImplementor);
void visitRegisteredComponents(Consumer<Component> consumer); void visitRegisteredComponents(Consumer<Component> consumer);
Component getGenericComponent(Class<?> componentClass);
} }

View File

@ -67,6 +67,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
private PersistentClass owner; private PersistentClass owner;
private boolean dynamic; private boolean dynamic;
private boolean isKey; private boolean isKey;
private Boolean isGeneric;
private String roleName; private String roleName;
private final ArrayList<Property> properties = new ArrayList<>(); private final ArrayList<Property> properties = new ArrayList<>();
@ -123,6 +124,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.parentProperty = original.parentProperty; this.parentProperty = original.parentProperty;
this.owner = original.owner; this.owner = original.owner;
this.dynamic = original.dynamic; this.dynamic = original.dynamic;
this.isGeneric = original.isGeneric;
this.metaAttributes = original.metaAttributes == null ? null : new HashMap<>( original.metaAttributes ); this.metaAttributes = original.metaAttributes == null ? null : new HashMap<>( original.metaAttributes );
this.isKey = original.isKey; this.isKey = original.isKey;
this.roleName = original.roleName; this.roleName = original.roleName;
@ -819,4 +821,15 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
public void setStructColumnNames(String[] structColumnNames) { public void setStructColumnNames(String[] structColumnNames) {
this.structColumnNames = structColumnNames; this.structColumnNames = structColumnNames;
} }
public boolean isGeneric() {
if ( isGeneric == null ) {
isGeneric = getComponentClassName() != null && getComponentClass().getTypeParameters().length != 0;
}
return isGeneric;
}
public void setGeneric(boolean generic) {
isGeneric = generic;
}
} }

View File

@ -300,9 +300,11 @@ public class AttributeFactory {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Class<Y> embeddableClass = (Class<Y>) component.getComponentClass(); final Class<Y> embeddableClass = (Class<Y>) component.getComponentClass();
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component ); if ( !component.isGeneric() ) {
if ( cached != null ) { final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component );
return cached; if ( cached != null ) {
return cached;
}
} }
final JavaTypeRegistry registry = context.getTypeConfiguration() final JavaTypeRegistry registry = context.getTypeConfiguration()

View File

@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.BiFunction;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.Internal; import org.hibernate.Internal;
@ -27,6 +28,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass; import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType; import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
@ -251,6 +253,33 @@ public class MetadataContext {
return Collections.unmodifiableMap( identifiableTypesByName ); return Collections.unmodifiableMap( identifiableTypesByName );
} }
private <X> PersistentAttribute<X, ?> buildAttribute(
Property property,
IdentifiableDomainType<X> entityType,
BiFunction<IdentifiableDomainType<X>, Property, PersistentAttribute<X, ?>> factoryFunction) {
final PersistentAttribute<X, ?> attribute;
final Component component = property.getValue() instanceof Component ? (Component) property.getValue() : null;
if ( component != null && component.isGeneric() ) {
// This is an embeddable property that uses generics, we have to retrieve the generic
// component previously registered and create the concrete attribute
final Component genericComponent = runtimeModelCreationContext.getMetadata()
.getGenericComponent( component.getComponentClass() );
final Property genericProperty = property.copy();
genericProperty.setValue( genericComponent );
genericProperty.setGeneric( true );
attribute = factoryFunction.apply( entityType, genericProperty );
if ( !property.isGeneric() ) {
final PersistentAttribute<X, ?> concreteAttribute = factoryFunction.apply( entityType, property );
//noinspection unchecked
( (AttributeContainer<X>) entityType ).getInFlightAccess().addConcreteGenericAttribute( concreteAttribute );
}
}
else {
attribute = factoryFunction.apply( entityType, property );
}
return attribute;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void wrapUp() { public void wrapUp() {
if ( LOG.isTraceEnabled() ) { if ( LOG.isTraceEnabled() ) {
@ -286,9 +315,10 @@ public class MetadataContext {
// skip the version property, it was already handled previously. // skip the version property, it was already handled previously.
continue; continue;
} }
final PersistentAttribute<Object, ?> attribute = attributeFactory.buildAttribute( final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaMapping, jpaMapping,
property attributeFactory::buildAttribute
); );
if ( attribute != null ) { if ( attribute != null ) {
addAttribute( jpaMapping, attribute ); addAttribute( jpaMapping, attribute );
@ -329,7 +359,11 @@ public class MetadataContext {
// skip the version property, it was already handled previously. // skip the version property, it was already handled previously.
continue; continue;
} }
final PersistentAttribute<Object, ?> attribute = attributeFactory.buildAttribute( jpaType, property ); final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaType,
attributeFactory::buildAttribute
);
if ( attribute != null ) { if ( attribute != null ) {
addAttribute( jpaType, attribute ); addAttribute( jpaType, attribute );
if ( property.isNaturalIdentifier() ) { if ( property.isNaturalIdentifier() ) {
@ -376,8 +410,9 @@ public class MetadataContext {
} }
( ( AttributeContainer<?>) embeddable ).getInFlightAccess().finishUp(); ( ( AttributeContainer<?>) embeddable ).getInFlightAccess().finishUp();
// Do not process embeddables for entity types i.e. id-classes // Do not process embeddables for entity types i.e. id-classes or
if ( !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) { // generic component embeddables used just for concrete type resolution
if ( !component.isGeneric() && !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
embeddables.put( embeddable.getJavaType(), embeddable ); embeddables.put( embeddable.getJavaType(), embeddable );
if ( staticMetamodelScanEnabled ) { if ( staticMetamodelScanEnabled ) {
@ -423,22 +458,26 @@ public class MetadataContext {
//noinspection rawtypes //noinspection rawtypes
final AttributeContainer attributeContainer = (AttributeContainer) identifiableType; final AttributeContainer attributeContainer = (AttributeContainer) identifiableType;
if ( declaredIdentifierProperty != null ) { if ( declaredIdentifierProperty != null ) {
final SingularPersistentAttribute<?, Object> idAttribute = attributeFactory.buildIdAttribute( //noinspection unchecked
final SingularPersistentAttribute<?, Object> idAttribute = (SingularPersistentAttribute<?, Object>) buildAttribute(
declaredIdentifierProperty,
identifiableType, identifiableType,
declaredIdentifierProperty attributeFactory::buildIdAttribute
); );
//noinspection unchecked //noinspection unchecked
attributeContainer.getInFlightAccess().applyIdAttribute( idAttribute ); attributeContainer.getInFlightAccess().applyIdAttribute( idAttribute );
} }
final Property superclassIdentifier = getMappedSuperclassIdentifier( persistentClass ); else {
if ( superclassIdentifier != null && superclassIdentifier.isGeneric() ) { final Property superclassIdentifier = getMappedSuperclassIdentifier( persistentClass );
// If the superclass identifier is generic we have to build the attribute to register the concrete type if ( superclassIdentifier != null && superclassIdentifier.isGeneric() ) {
final SingularPersistentAttribute<?, Object> concreteIdentifier = attributeFactory.buildIdAttribute( // If the superclass identifier is generic we have to build the attribute to register the concrete type
identifiableType, final SingularPersistentAttribute<?, Object> concreteIdentifier = attributeFactory.buildIdAttribute(
persistentClass.getIdentifierProperty() identifiableType,
); persistentClass.getIdentifierProperty()
//noinspection unchecked );
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier ); //noinspection unchecked
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier );
}
} }
} }
else { else {
@ -519,10 +558,14 @@ public class MetadataContext {
if ( mappingType.hasIdentifierProperty() ) { if ( mappingType.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty(); final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) { if ( declaredIdentifierProperty != null ) {
final SingularPersistentAttribute<X, Object> attribute =
attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty );
//noinspection unchecked //noinspection unchecked
( ( AttributeContainer) jpaMappingType ).getInFlightAccess().applyIdAttribute( attribute ); final SingularPersistentAttribute<X, Object> attribute = (SingularPersistentAttribute<X, Object>) buildAttribute(
declaredIdentifierProperty,
jpaMappingType,
attributeFactory::buildIdAttribute
);
//noinspection unchecked
( (AttributeContainer<X>) jpaMappingType ).getInFlightAccess().applyIdAttribute( attribute );
} }
} }
//a MappedSuperclass can have no identifier if the id is set below in the hierarchy //a MappedSuperclass can have no identifier if the id is set below in the hierarchy