HHH-16070 Check for type parameters when setting declared identifier

This commit is contained in:
Marco Belladelli 2023-01-24 18:07:14 +01:00 committed by Christian Beikov
parent 3bfb2f66a1
commit 94b20bafc8
4 changed files with 115 additions and 12 deletions

View File

@ -329,7 +329,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
} }
} }
private String getTypeName(Value value) { static String getTypeName(Value value) {
if ( value instanceof Component ) { if ( value instanceof Component ) {
final Component component = (Component) value; final Component component = (Component) value;
final String typeName = component.getTypeName(); final String typeName = component.getTypeName();
@ -341,7 +341,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
return ( (SimpleValue) value ).getTypeName(); return ( (SimpleValue) value ).getTypeName();
} }
private void setTypeName(Value value, String typeName) { static void setTypeName(Value value, String typeName) {
if ( value instanceof ToOne ) { if ( value instanceof ToOne ) {
final ToOne toOne = (ToOne) value; final ToOne toOne = (ToOne) value;
toOne.setReferencedEntityName( typeName ); toOne.setReferencedEntityName( typeName );

View File

@ -8,6 +8,7 @@ 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;
@ -28,6 +29,7 @@ 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;
@ -317,13 +319,7 @@ public class PropertyBinder {
inheritanceStatePerClass, inheritanceStatePerClass,
buildingContext buildingContext
); );
if ( superclass != null ) { setDeclaredIdentifier( rootClass, superclass, property );
superclass.setDeclaredIdentifierProperty(property);
}
else {
//we know the property is on the actual entity
rootClass.setDeclaredIdentifierProperty( property );
}
} }
} }
} }
@ -342,6 +338,65 @@ public class PropertyBinder {
return property; return property;
} }
private void setDeclaredIdentifier(RootClass rootClass, MappedSuperclass superclass, Property prop) {
if ( superclass == null ) {
rootClass.setDeclaredIdentifierProperty( prop );
return;
}
// If the type has type parameters, we have to set the declared identifier property on the rootClass
// to be able to retrieve it with the correct type based on type variable assignment in the subclass
final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager().toClass( declaringClass );
if ( type.getTypeParameters().length == 0 ) {
superclass.setDeclaredIdentifierProperty( prop );
}
else {
// If the type has type parameters, we have to look up the XClass and actual property again
// because the given XClass has a TypeEnvironment based on the type variable assignments of a subclass
// and that might result in a wrong property type being used for a property which uses a type variable
final XClass actualDeclaringClass = buildingContext.getBootstrapContext().getReflectionManager().toXClass( type );
for ( XProperty declaredProperty : actualDeclaringClass.getDeclaredProperties( prop.getPropertyAccessorName() ) ) {
if ( prop.getName().equals( declaredProperty.getName() ) ) {
final PropertyData inferredData = new PropertyInferredData(
actualDeclaringClass,
declaredProperty,
null,
buildingContext.getBootstrapContext().getReflectionManager()
);
final Value originalValue = prop.getValue();
if ( originalValue instanceof SimpleValue ) {
// Avoid copying when the property doesn't depend on a type variable
if ( inferredData.getTypeName().equals( ClassPropertyHolder.getTypeName( originalValue ) ) ) {
superclass.setDeclaredIdentifierProperty( prop );
return;
}
}
// If the property depends on a type variable, we have to copy it and the Value
final Property actualProperty = prop.copy();
actualProperty.setReturnedClassName( inferredData.getTypeName() );
final Value value = actualProperty.getValue().copy();
assert !(value instanceof Collection);
ClassPropertyHolder.setTypeName( value, inferredData.getTypeName() );
if ( value instanceof Component ) {
Component component = ( (Component) value );
Iterator<Property> propertyIterator = component.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
Property property = propertyIterator.next();
try {
property.getGetter( component.getComponentClass() );
}
catch (PropertyNotFoundException e) {
propertyIterator.remove();
}
}
}
actualProperty.setValue( value );
superclass.setDeclaredIdentifierProperty( actualProperty );
break;
}
}
}
}
private Component getOrCreateCompositeId(RootClass rootClass) { private Component getOrCreateCompositeId(RootClass rootClass) {
final Component id = (Component) rootClass.getIdentifier(); final Component id = (Component) rootClass.getIdentifier();
if ( id == null ) { if ( id == null ) {

View File

@ -583,9 +583,22 @@ public class AttributeFactory {
final CompositeTypeImplementor ownerComponentType = (CompositeTypeImplementor) ownerBootDescriptor.getType(); final CompositeTypeImplementor ownerComponentType = (CompositeTypeImplementor) ownerBootDescriptor.getType();
final EmbeddableValuedModelPart ownerMappingModelDescriptor = ownerComponentType.getMappingModelPart(); final EmbeddableValuedModelPart ownerMappingModelDescriptor = ownerComponentType.getMappingModelPart();
final EmbeddableRepresentationStrategy ownerRepStrategy = ownerMappingModelDescriptor final EmbeddableRepresentationStrategy ownerRepStrategy;
.getEmbeddableTypeDescriptor()
.getRepresentationStrategy(); if ( ownerMappingModelDescriptor == null ) {
// When an entity uses a type variable, bound by a mapped superclass, for an embedded id,
// we will not create a model part for the component, but we still need the representation strategy here,
// in order to discover the property members to expose on the JPA metamodel
ownerRepStrategy = ownerBootDescriptor.getBuildingContext()
.getBootstrapContext()
.getRepresentationStrategySelector()
.resolveStrategy( ownerBootDescriptor, null, metadataContext.getRuntimeModelCreationContext() );
}
else {
ownerRepStrategy = ownerMappingModelDescriptor
.getEmbeddableTypeDescriptor()
.getRepresentationStrategy();
}
if ( ownerRepStrategy.getMode() == RepresentationMode.MAP ) { if ( ownerRepStrategy.getMode() == RepresentationMode.MAP ) {
return new MapMember( return new MapMember(

View File

@ -24,6 +24,7 @@ import org.hibernate.internal.HEMLogging;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
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;
@ -428,6 +429,12 @@ public class MetadataContext {
//noinspection unchecked rawtypes //noinspection unchecked rawtypes
( ( AttributeContainer) identifiableType ).getInFlightAccess().applyIdAttribute( idAttribute ); ( ( AttributeContainer) identifiableType ).getInFlightAccess().applyIdAttribute( idAttribute );
} }
else if ( persistentClass.getIdentifier() instanceof Component
&& persistentClass.getIdentifierProperty() != getSuperclassIdentifier( persistentClass ) ) {
// If the identifier is a generic component, we have to call buildIdAttribute anyway,
// as this will create and register the EmbeddableType for the subtype
attributeFactory.buildIdAttribute( identifiableType, persistentClass.getIdentifierProperty() );
}
} }
else { else {
// we have a non-aggregated composite-id // we have a non-aggregated composite-id
@ -476,6 +483,34 @@ public class MetadataContext {
} }
} }
private Property getSuperclassIdentifier(PersistentClass persistentClass) {
final Property declaredIdentifierProperty = persistentClass.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
return declaredIdentifierProperty;
}
if ( persistentClass.getSuperMappedSuperclass() != null ) {
return getSuperclassIdentifier( persistentClass.getSuperMappedSuperclass() );
}
else if ( persistentClass.getSuperclass() != null ) {
return getSuperclassIdentifier( persistentClass.getSuperclass() );
}
return null;
}
private Property getSuperclassIdentifier(MappedSuperclass persistentClass) {
final Property declaredIdentifierProperty = persistentClass.getDeclaredIdentifierProperty();
if ( declaredIdentifierProperty != null ) {
return declaredIdentifierProperty;
}
if ( persistentClass.getSuperMappedSuperclass() != null ) {
return getSuperclassIdentifier( persistentClass.getSuperMappedSuperclass() );
}
else if ( persistentClass.getSuperPersistentClass() != null ) {
return getSuperclassIdentifier( persistentClass.getSuperPersistentClass() );
}
return null;
}
private EmbeddableTypeImpl<?> applyIdClassMetadata(Component idClassComponent) { private EmbeddableTypeImpl<?> applyIdClassMetadata(Component idClassComponent) {
final JavaTypeRegistry registry = getTypeConfiguration() final JavaTypeRegistry registry = getTypeConfiguration()
.getJavaTypeRegistry(); .getJavaTypeRegistry();