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 4e070a48f2
commit dd75b0e110
4 changed files with 120 additions and 13 deletions

View File

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

View File

@ -13,6 +13,7 @@ import jakarta.persistence.Version;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.annotations.AttributeBinderType;
import org.hibernate.annotations.Generated;
import org.hibernate.annotations.Immutable;
@ -25,13 +26,17 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.ClassPropertyHolder;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyData;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyInferredData;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
@ -47,6 +52,7 @@ import org.hibernate.tuple.ValueGenerator;
import org.jboss.logging.Logger;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.Map;
import static org.hibernate.cfg.BinderHelper.getMappedSuperclassOrNull;
@ -264,18 +270,12 @@ public class PropertyBinder {
}
else {
rootClass.setIdentifierProperty( prop );
final org.hibernate.mapping.MappedSuperclass superclass = getMappedSuperclassOrNull(
final MappedSuperclass superclass = getMappedSuperclassOrNull(
declaringClass,
inheritanceStatePerClass,
buildingContext
);
if (superclass != null) {
superclass.setDeclaredIdentifierProperty(prop);
}
else {
//we know the property is on the actual entity
rootClass.setDeclaredIdentifierProperty( prop );
}
setDeclaredIdentifier( rootClass, superclass, prop );
}
}
}
@ -288,6 +288,65 @@ public class PropertyBinder {
return prop;
}
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 Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation =
property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );

View File

@ -596,9 +596,22 @@ public class AttributeFactory {
final CompositeTypeImplementor ownerComponentType = (CompositeTypeImplementor) ownerBootDescriptor.getType();
final EmbeddableValuedModelPart ownerMappingModelDescriptor = ownerComponentType.getMappingModelPart();
final EmbeddableRepresentationStrategy ownerRepStrategy = ownerMappingModelDescriptor
.getEmbeddableTypeDescriptor()
.getRepresentationStrategy();
final EmbeddableRepresentationStrategy ownerRepStrategy;
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 ) {
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.collections.CollectionHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
@ -428,6 +429,12 @@ public class MetadataContext {
//noinspection unchecked rawtypes
( ( 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 {
// 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) {
final JavaTypeRegistry registry = getTypeConfiguration()
.getJavaTypeRegistry();