HHH-16491 Special handling for generic component properties
This commit is contained in:
parent
a73db16523
commit
0f64b13ea3
|
@ -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(
|
||||
|
@ -2313,6 +2324,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
|||
options,
|
||||
entityBindingMap,
|
||||
composites,
|
||||
genericComponentsMap,
|
||||
mappedSuperClasses,
|
||||
collectionBindingMap,
|
||||
typeDefRegistry.copyRegistrationMap(),
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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,17 +354,23 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
}
|
||||
if ( value instanceof Component ) {
|
||||
final Component component = ( (Component) value );
|
||||
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( component.getComponentClass() );
|
||||
property.getGetter( componentClass );
|
||||
}
|
||||
catch (PropertyNotFoundException e) {
|
||||
propertyIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
actualProperty.setValue( value );
|
||||
propertyConsumer.accept( actualProperty );
|
||||
break;
|
||||
|
@ -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 ) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -84,6 +84,8 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
|
|||
|
||||
void registerComponent(Component component);
|
||||
|
||||
void registerGenericComponent(Component component);
|
||||
|
||||
/**
|
||||
* Adds an import (for use in HQL).
|
||||
*
|
||||
|
|
|
@ -56,4 +56,6 @@ public interface MetadataImplementor extends Metadata {
|
|||
void initSessionFactory(SessionFactoryImplementor sessionFactoryImplementor);
|
||||
|
||||
void visitRegisteredComponents(Consumer<Component> consumer);
|
||||
|
||||
Component getGenericComponent(Class<?> componentClass);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,10 +300,12 @@ public class AttributeFactory {
|
|||
@SuppressWarnings("unchecked")
|
||||
final Class<Y> embeddableClass = (Class<Y>) component.getComponentClass();
|
||||
|
||||
if ( !component.isGeneric() ) {
|
||||
final EmbeddableDomainType<Y> cached = context.locateEmbeddable( embeddableClass, component );
|
||||
if ( cached != null ) {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
final JavaTypeRegistry registry = context.getTypeConfiguration()
|
||||
.getJavaTypeRegistry();
|
||||
|
|
|
@ -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,13 +458,16 @@ 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 );
|
||||
}
|
||||
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
|
||||
|
@ -441,6 +479,7 @@ public class MetadataContext {
|
|||
attributeContainer.getInFlightAccess().addConcreteGenericAttribute( concreteIdentifier );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// we have a non-aggregated composite-id
|
||||
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue