HHH-18524 Fix binding of meta-annotated id generators for id-class

This commit is contained in:
Marco Belladelli 2024-08-27 11:13:02 +02:00
parent 049b76c2d1
commit c85efaa7d2
6 changed files with 54 additions and 70 deletions

View File

@ -28,7 +28,6 @@ import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
@ -66,19 +65,15 @@ import static org.hibernate.boot.model.internal.BinderHelper.getPath;
import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath;
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
import static org.hibernate.boot.model.internal.GeneratorBinder.generatorType;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
import static org.hibernate.boot.model.internal.HCANNHelper.findContainingAnnotations;
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;
import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations;
import static org.hibernate.boot.model.internal.PropertyBinder.processId;
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.internal.CoreLogging.messageLogger;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.unqualify;
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
/**
* A binder responsible for interpreting {@link Embeddable} classes and producing
@ -452,18 +447,27 @@ public class EmbeddableBinder {
inheritanceStatePerClass
);
final XProperty property = propertyAnnotatedElement.getProperty();
if ( property.isAnnotationPresent( GeneratedValue.class ) ) {
if ( isIdClass || subholder.isOrWithinEmbeddedId() ) {
processGeneratedId( context, component, property );
}
else {
throw new AnnotationException(
"Property '" + property.getName() + "' of '"
+ getPath( propertyHolder, inferredData )
+ "' is annotated '@GeneratedValue' but is not part of an identifier" );
final XProperty member = propertyAnnotatedElement.getProperty();
if ( isIdClass || subholder.isOrWithinEmbeddedId() ) {
final Property property = findProperty( component, member.getName() );
if ( property != null ) {
// Identifier properties are always simple values
final SimpleValue value = (SimpleValue) property.getValue();
processId(
subholder,
propertyAnnotatedElement,
value,
Map.of(),
context
);
}
}
else if ( member.isAnnotationPresent( GeneratedValue.class ) ) {
throw new AnnotationException(
"Property '" + member.getName() + "' of '"
+ getPath( propertyHolder, inferredData )
+ "' is annotated '@GeneratedValue' but is not part of an identifier" );
}
}
if ( compositeUserType != null ) {
@ -473,6 +477,15 @@ public class EmbeddableBinder {
return component;
}
private static Property findProperty(Component component, String name) {
for ( Property property : component.getProperties() ) {
if ( property.getName().equals( name ) ) {
return property;
}
}
return null;
}
private static CompositeUserType<?> compositeUserType(
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
MetadataBuildingContext context) {
@ -782,40 +795,6 @@ public class EmbeddableBinder {
|| property.isAnnotationPresent(ManyToMany.class);
}
private static void processGeneratedId(MetadataBuildingContext context, Component component, XProperty property) {
final GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
final String generatorType = generatedValue != null
? generatorType( generatedValue, property.getType(), context )
: DEFAULT_ID_GEN_STRATEGY;
final String generator = generatedValue != null ? generatedValue.generator() : "";
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( property, context );
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
(SimpleValue) component.getProperty( property.getName() ).getValue(),
property,
generatorType,
generator,
context
) );
// handleTypeDescriptorRegistrations( property, context );
// bindEmbeddableInstantiatorRegistrations( property, context );
// bindCompositeUserTypeRegistrations( property, context );
// handleConverterRegistrations( property, context );
}
else {
makeIdGenerator(
(SimpleValue) component.getProperty( property.getName() ).getValue(),
property,
generatorType,
generator,
context,
new HashMap<>( buildGenerators( property, context ) )
);
}
}
private static void processIdClassElements(
PropertyHolder propertyHolder,
PropertyData baseInferredData,

View File

@ -59,6 +59,7 @@ import jakarta.persistence.Version;
import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.id.factory.internal.IdentifierGeneratorUtil.collectParameters;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
public class GeneratorBinder {
@ -113,16 +114,10 @@ public class GeneratorBinder {
|| identifierGeneratorStrategy.equals( "seqhilo" );
if ( generatorType == null || !avoidOverriding ) {
id.setIdentifierGeneratorStrategy( identifierGeneratorStrategy );
if ( DEFAULT_ID_GEN_STRATEGY.equals( identifierGeneratorStrategy ) ) {
id.setNullValue( "undefined" );
}
}
//checkIfMatchingGenerator(definition, generatorType, generatorName);
parameters.putAll( definition.getParameters() );
}
if ( DEFAULT_ID_GEN_STRATEGY.equals( generatorType ) ) {
id.setNullValue( "undefined" );
}
id.setIdentifierGeneratorParameters( parameters );
}
@ -564,9 +559,9 @@ public class GeneratorBinder {
XProperty idProperty) {
//manage composite related metadata
//guess if its a component and find id data access (property, field etc)
final GeneratedValue generatedValue = idProperty.getAnnotation( GeneratedValue.class );
final GeneratedValue generatedValue = castNonNull( idProperty.getAnnotation( GeneratedValue.class ) );
final String generatorType = generatorType( context, entityClass, isCompositeId( entityClass, idProperty ), generatedValue );
final String generatorName = generatedValue == null ? "" : generatedValue.generator();
final String generatorName = generatedValue.generator();
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( idProperty, context );
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(

View File

@ -15,6 +15,7 @@ import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
@ -1156,13 +1157,16 @@ public class PropertyBinder {
);
}
else if ( propertyBinder.isId() ) {
if ( isIdentifierMapper ) {
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'" );
}
//components and regular basic types create SimpleValue objects
processId(
propertyHolder,
inferredData,
(SimpleValue) propertyBinder.getValue(),
classGenerators,
isIdentifierMapper,
context
);
}
@ -1415,17 +1419,12 @@ public class PropertyBinder {
return null;
}
private static void processId(
static void processId(
PropertyHolder propertyHolder,
PropertyData inferredData,
SimpleValue idValue,
Map<String, IdentifierGeneratorDefinition> classGenerators,
boolean isIdentifierMapper,
MetadataBuildingContext context) {
if ( isIdentifierMapper ) {
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'" );
}
final XProperty idProperty = inferredData.getProperty();
final List<Annotation> idGeneratorAnnotations = findContainingAnnotations( idProperty, IdGeneratorType.class );
final List<Annotation> generatorAnnotations = findContainingAnnotations( idProperty, ValueGenerationType.class );
@ -1449,7 +1448,7 @@ public class PropertyBinder {
+ "' is annotated '" + generatorAnnotations.get(0).annotationType()
+ "' which is not an '@IdGeneratorType'" );
}
else {
else if ( idProperty.isAnnotationPresent( GeneratedValue.class ) ) {
final XClass entityClass = inferredData.getClassOrElement();
createIdGenerator( idValue, classGenerators, context, entityClass, idProperty );
if ( LOG.isTraceEnabled() ) {

View File

@ -43,4 +43,9 @@ public class Assigned implements IdentifierGenerator, StandardGenerator {
throw new MappingException("no entity name");
}
}
@Override
public boolean allowAssignedIdentifiers() {
return true;
}
}

View File

@ -76,6 +76,7 @@ import org.hibernate.integrator.spi.IntegratorService;
import org.hibernate.jpa.internal.ExceptionMapperLegacyJpaImpl;
import org.hibernate.jpa.internal.PersistenceUnitUtilImpl;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
@ -462,7 +463,8 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
bootMetamodel.getEntityBindings().stream()
.filter( model -> !model.isInherited() )
.forEach( model -> {
final Generator generator = model.getIdentifier().createGenerator(
final KeyValue id = model.getIdentifier();
final Generator generator = id.createGenerator(
bootstrapContext.getIdentifierGeneratorFactory(),
jdbcServices.getJdbcEnvironment().getDialect(),
(RootClass) model
@ -471,8 +473,11 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
final Configurable identifierGenerator = (Configurable) generator;
identifierGenerator.initialize( sqlStringGenerationContext );
}
if ( generator.allowAssignedIdentifiers() ) {
( (SimpleValue) model.getIdentifier() ).setNullValue( "undefined" );
if ( generator.allowAssignedIdentifiers() && id instanceof SimpleValue ) {
final SimpleValue simpleValue = (SimpleValue) id;
if ( simpleValue.getNullValue() == null ) {
simpleValue.setNullValue( "undefined" );
}
}
generators.put( model.getEntityName(), generator );
} );

View File

@ -719,7 +719,8 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
if ( property.getValue().isSimpleValue() ) {
final SimpleValue value = (SimpleValue) property.getValue();
if ( !DEFAULT_ID_GEN_STRATEGY.equals( value.getIdentifierGeneratorStrategy() ) ) {
if ( !DEFAULT_ID_GEN_STRATEGY.equals( value.getIdentifierGeneratorStrategy() )
|| value.getCustomIdGeneratorCreator() != null ) {
// skip any 'assigned' generators, they would have been handled by
// the StandardGenerationContextLocator
generator.addGeneratedValuePlan( new ValueGenerationPlan(