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 6d0c9599e6
commit b8b8f011dc
5 changed files with 59 additions and 52 deletions

View File

@ -66,11 +66,8 @@ 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.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath; 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.hasToOneAnnotation;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation;
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators; import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGeneratorsFromGeneratorAnnotations;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorStrategy;
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass; 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.processElementAnnotations;
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder; import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
@ -464,7 +461,7 @@ public class EmbeddableBinder {
? Nullability.FORCED_NULL ? Nullability.FORCED_NULL
: ( isNullable ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL ), : ( isNullable ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL ),
propertyAnnotatedElement, propertyAnnotatedElement,
new HashMap<>(), Map.of(),
entityBinder, entityBinder,
isIdentifierMapper, isIdentifierMapper,
isComponentEmbedded, isComponentEmbedded,
@ -473,18 +470,27 @@ public class EmbeddableBinder {
inheritanceStatePerClass inheritanceStatePerClass
); );
final MemberDetails property = propertyAnnotatedElement.getAttributeMember(); final MemberDetails member = propertyAnnotatedElement.getAttributeMember();
if ( property.hasDirectAnnotationUsage( GeneratedValue.class ) ) { if ( isIdClass || subholder.isOrWithinEmbeddedId() ) {
if ( isIdClass || subholder.isOrWithinEmbeddedId() ) { final Property property = findProperty( component, member.getName() );
processGeneratedId( context, component, property ); if ( property != null ) {
} // Identifier properties are always simple values
else { final SimpleValue value = (SimpleValue) property.getValue();
throw new AnnotationException( createIdGeneratorsFromGeneratorAnnotations(
"Property '" + property.getName() + "' of '" subholder,
+ getPath( propertyHolder, inferredData ) propertyAnnotatedElement,
+ "' is annotated '@GeneratedValue' but is not part of an identifier" ); value,
Map.of(),
context
);
} }
} }
else if ( member.hasDirectAnnotationUsage( 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 ) { if ( compositeUserType != null ) {
@ -494,6 +500,15 @@ public class EmbeddableBinder {
return component; 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( private static CompositeUserType<?> compositeUserType(
Class<? extends CompositeUserType<?>> compositeUserTypeClass, Class<? extends CompositeUserType<?>> compositeUserTypeClass,
MetadataBuildingContext context) { MetadataBuildingContext context) {
@ -812,40 +827,6 @@ public class EmbeddableBinder {
|| property.hasDirectAnnotationUsage(ManyToMany.class); || property.hasDirectAnnotationUsage(ManyToMany.class);
} }
private static void processGeneratedId(MetadataBuildingContext context, Component component, MemberDetails property) {
final GeneratedValue generatedValue = property.getDirectAnnotationUsage( GeneratedValue.class );
final String generatorType =
generatorStrategy( generatedValue.strategy(), generatedValue.generator(), property.getType() );
final String generator = generatedValue.generator();
final SimpleValue value = (SimpleValue) component.getProperty( property.getName()).getValue();
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( property, context );
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
value,
property,
generatorType,
generator,
context
) );
// handleTypeDescriptorRegistrations( property, context );
// bindEmbeddableInstantiatorRegistrations( property, context );
// bindCompositeUserTypeRegistrations( property, context );
// handleConverterRegistrations( property, context );
}
else {
makeIdGenerator(
value,
property,
generatorType,
generator,
context,
new HashMap<>( buildGenerators( property, context ) )
);
}
}
private static void processIdClassElements( private static void processIdClassElements(
PropertyHolder propertyHolder, PropertyHolder propertyHolder,
PropertyData baseInferredData, PropertyData baseInferredData,

View File

@ -65,6 +65,7 @@ import static org.hibernate.boot.model.internal.GeneratorParameters.interpretSeq
import static org.hibernate.boot.model.internal.GeneratorParameters.interpretTableGenerator; import static org.hibernate.boot.model.internal.GeneratorParameters.interpretTableGenerator;
import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorClass; import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorClass;
import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorStrategy; import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorStrategy;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.resource.beans.internal.Helper.allowExtensionsInCdi; import static org.hibernate.resource.beans.internal.Helper.allowExtensionsInCdi;
@ -600,7 +601,7 @@ public class GeneratorBinder {
MemberDetails idMember) { MemberDetails idMember) {
//manage composite related metadata //manage composite related metadata
//guess if its a component and find id data access (property, field etc) //guess if its a component and find id data access (property, field etc)
final GeneratedValue generatedValue = idMember.getDirectAnnotationUsage( GeneratedValue.class ); final GeneratedValue generatedValue = castNonNull( idMember.getDirectAnnotationUsage( GeneratedValue.class ) );
final String generatorType = final String generatorType =
generatorStrategy( generatedValue.strategy(), generatedValue.generator(), idMember.getType() ); generatorStrategy( generatedValue.strategy(), generatedValue.generator(), idMember.getType() );
final String generatorName = generatedValue.generator(); final String generatorName = generatedValue.generator();

View File

@ -1135,11 +1135,11 @@ public class PropertyBinder {
); );
} }
else if ( propertyBinder.isId() ) { else if ( propertyBinder.isId() ) {
//components and regular basic types create SimpleValue objects
if ( isIdentifierMapper ) { if ( isIdentifierMapper ) {
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData ) throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'" ); + "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'" );
} }
//components and regular basic types create SimpleValue objects
createIdGeneratorsFromGeneratorAnnotations( createIdGeneratorsFromGeneratorAnnotations(
propertyHolder, propertyHolder,
inferredData, inferredData,

View File

@ -3,12 +3,13 @@ package org.hibernate.orm.test.tenantidpk;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import org.hibernate.annotations.TenantId; import org.hibernate.annotations.TenantId;
import java.util.UUID; import java.util.UUID;
@Entity @Entity @IdClass(TenantizedId.class)
public class Account { public class Account {
@Id @GeneratedValue Long id; @Id @GeneratedValue Long id;

View File

@ -0,0 +1,24 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.tenantidpk;
import java.util.UUID;
import jakarta.persistence.Embeddable;
@Embeddable
public class TenantizedId {
Long id;
UUID tenantId;
public TenantizedId(Long id, UUID tenantId) {
this.id = id;
this.tenantId = tenantId;
}
TenantizedId() {}
}