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 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, FilterDefinition> filterDefinitionMap = new HashMap<>();
@ -282,6 +283,16 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
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
public SessionFactoryBuilder getSessionFactoryBuilder() {
throw new UnsupportedOperationException(
@ -2314,6 +2325,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
options,
entityBindingMap,
composites,
genericComponentsMap,
mappedSuperClasses,
collectionBindingMap,
typeDefRegistry.copyRegistrationMap(),

View File

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

View File

@ -229,7 +229,39 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
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) {
handleGenericComponentProperty( property, getContext() );
if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
if ( inheritanceState == null ) {
@ -253,10 +285,10 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
private void addPropertyToMappedSuperclass(Property prop, XClass declaringClass) {
final Class<?> type = getContext().getBootstrapContext().getReflectionManager().toClass( declaringClass );
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,
Class<?> type,
boolean allowCollections,
@ -322,14 +354,20 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
if ( value instanceof Component ) {
final Component component = ( (Component) value );
final Iterator<Property> propertyIterator = component.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
Property property = propertyIterator.next();
try {
property.getGetter( component.getComponentClass() );
}
catch (PropertyNotFoundException e) {
propertyIterator.remove();
final Class<?> componentClass = component.getComponentClass();
if ( component.isGeneric() ) {
actualProperty.setValue( context.getMetadataCollector().getGenericComponent( componentClass ) );
}
else {
final Iterator<Property> propertyIterator = component.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
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 ) {
final Component component = (Component) value;
// Avoid setting component class name to java.lang.Object
// for embeddable types with generic type parameters
if ( !typeName.equals( Object.class.getName() ) ) {
// Avoid setting type name for generic components
if ( !component.isGeneric() ) {
component.setComponentClassName( typeName );
}
if ( component.getTypeName() != null ) {

View File

@ -8,7 +8,6 @@ package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -29,7 +28,6 @@ import jakarta.persistence.Version;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AttributeBinderType;
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.BinderHelper.isCompositeId;
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.GeneratorBinder.createForeignGenerator;
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) {
handleGenericComponentProperty( prop, buildingContext );
if ( superclass == null ) {
rootClass.setDeclaredIdentifierProperty( prop );
return;
}
final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager().toClass( declaringClass );
ClassPropertyHolder.prepareActualPropertyForSuperclass(
prop,
type,
false,
buildingContext,
superclass::setDeclaredIdentifierProperty
);
prepareActualProperty( prop, type, false, buildingContext, superclass::setDeclaredIdentifierProperty );
}
private Component getOrCreateCompositeId(RootClass rootClass) {

View File

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

View File

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

View File

@ -56,4 +56,6 @@ public interface MetadataImplementor extends Metadata {
void initSessionFactory(SessionFactoryImplementor sessionFactoryImplementor);
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 boolean dynamic;
private boolean isKey;
private Boolean isGeneric;
private String roleName;
private final ArrayList<Property> properties = new ArrayList<>();
@ -123,6 +124,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.parentProperty = original.parentProperty;
this.owner = original.owner;
this.dynamic = original.dynamic;
this.isGeneric = original.isGeneric;
this.metaAttributes = original.metaAttributes == null ? null : new HashMap<>( original.metaAttributes );
this.isKey = original.isKey;
this.roleName = original.roleName;
@ -819,4 +821,15 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
public void setStructColumnNames(String[] 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")
final Class<Y> embeddableClass = (Class<Y>) component.getComponentClass();
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component );
if ( cached != null ) {
return cached;
if ( !component.isGeneric() ) {
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component );
if ( cached != null ) {
return cached;
}
}
final JavaTypeRegistry registry = context.getTypeConfiguration()

View File

@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import org.hibernate.AssertionFailure;
import org.hibernate.Internal;
@ -27,6 +28,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
import org.hibernate.metamodel.model.domain.BasicDomainType;
@ -251,6 +253,33 @@ public class MetadataContext {
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")
public void wrapUp() {
if ( LOG.isTraceEnabled() ) {
@ -286,9 +315,10 @@ public class MetadataContext {
// skip the version property, it was already handled previously.
continue;
}
final PersistentAttribute<Object, ?> attribute = attributeFactory.buildAttribute(
final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaMapping,
property
attributeFactory::buildAttribute
);
if ( attribute != null ) {
addAttribute( jpaMapping, attribute );
@ -329,7 +359,11 @@ public class MetadataContext {
// skip the version property, it was already handled previously.
continue;
}
final PersistentAttribute<Object, ?> attribute = attributeFactory.buildAttribute( jpaType, property );
final PersistentAttribute<Object, ?> attribute = buildAttribute(
property,
jpaType,
attributeFactory::buildAttribute
);
if ( attribute != null ) {
addAttribute( jpaType, attribute );
if ( property.isNaturalIdentifier() ) {
@ -376,8 +410,9 @@ public class MetadataContext {
}
( ( AttributeContainer<?>) embeddable ).getInFlightAccess().finishUp();
// Do not process embeddables for entity types i.e. id-classes
if ( !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
// Do not process embeddables for entity types i.e. id-classes or
// generic component embeddables used just for concrete type resolution
if ( !component.isGeneric() && !( embeddable.getExpressibleJavaType() instanceof EntityJavaType<?> ) ) {
embeddables.put( embeddable.getJavaType(), embeddable );
if ( staticMetamodelScanEnabled ) {
@ -423,22 +458,26 @@ public class MetadataContext {
//noinspection rawtypes
final AttributeContainer attributeContainer = (AttributeContainer) identifiableType;
if ( declaredIdentifierProperty != null ) {
final SingularPersistentAttribute<?, Object> idAttribute = attributeFactory.buildIdAttribute(
//noinspection unchecked
final SingularPersistentAttribute<?, Object> idAttribute = (SingularPersistentAttribute<?, Object>) buildAttribute(
declaredIdentifierProperty,
identifiableType,
declaredIdentifierProperty
attributeFactory::buildIdAttribute
);
//noinspection unchecked
attributeContainer.getInFlightAccess().applyIdAttribute( idAttribute );
}
final Property superclassIdentifier = getMappedSuperclassIdentifier( persistentClass );
if ( superclassIdentifier != null && superclassIdentifier.isGeneric() ) {
// If the superclass identifier is generic we have to build the attribute to register the concrete type
final SingularPersistentAttribute<?, Object> concreteIdentifier = attributeFactory.buildIdAttribute(
identifiableType,
persistentClass.getIdentifierProperty()
);
//noinspection unchecked
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier );
else {
final Property superclassIdentifier = getMappedSuperclassIdentifier( persistentClass );
if ( superclassIdentifier != null && superclassIdentifier.isGeneric() ) {
// If the superclass identifier is generic we have to build the attribute to register the concrete type
final SingularPersistentAttribute<?, Object> concreteIdentifier = attributeFactory.buildIdAttribute(
identifiableType,
persistentClass.getIdentifierProperty()
);
//noinspection unchecked
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier );
}
}
}
else {
@ -519,10 +558,14 @@ public class MetadataContext {
if ( mappingType.hasIdentifierProperty() ) {
final Property declaredIdentifierProperty = mappingType.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
final SingularPersistentAttribute<X, Object> attribute =
attributeFactory.buildIdAttribute( jpaMappingType, declaredIdentifierProperty );
//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