From 94d34f09abdcd77d0670322735907525cdf96811 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Thu, 19 Sep 2024 06:36:10 -0500 Subject: [PATCH 1/4] HHH-18620 - Add @NativeGenerator --- .../community/dialect/CacheDialect.java | 6 +- .../dialect/CockroachLegacyDialect.java | 5 +- .../dialect/OracleLegacyDialect.java | 5 +- .../dialect/PostgreSQLLegacyDialect.java | 5 +- .../annotations/NativeGenerator.java | 34 ++++ .../internal/GeneratorAnnotationHelper.java | 32 ++- .../boot/model/internal/GeneratorBinder.java | 2 +- .../IdGeneratorResolverSecondPass.java | 86 +++++--- .../hibernate/dialect/CockroachDialect.java | 5 +- .../java/org/hibernate/dialect/Dialect.java | 15 +- .../dialect/DialectDelegateWrapper.java | 5 +- .../org/hibernate/dialect/OracleDialect.java | 5 +- .../hibernate/dialect/PostgreSQLDialect.java | 5 +- .../org/hibernate/id/NativeGenerator.java | 188 ++++++++++++++++++ .../local/NativeGeneratorClassTest.java | 42 ++++ .../local/NativeGeneratorMemberTest.java | 42 ++++ .../pkg/NativeGeneratorPackageTest.java | 41 ++++ .../test/idgen/n_ative/pkg/package-info.java | 12 ++ .../idgen/userdefined/NativeGenerator.java | 102 ---------- .../userdefined/NativeGeneratorTest.java | 26 --- .../orm/test/idgen/userdefined/NativeId.java | 18 -- 21 files changed, 483 insertions(+), 198 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/package-info.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java index 50024b79e69e..0419ab1b13ec 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CacheDialect.java @@ -51,6 +51,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -283,10 +284,9 @@ public boolean hasSelfReferentialForeignKeyBug() { return true; } - @Override - public String getNativeIdentifierGeneratorStrategy() { - return "identity"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.IDENTITY; } // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 81ec897ce9d1..6f7c766b4639 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -75,6 +75,7 @@ import org.jboss.logging.Logger; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -654,8 +655,8 @@ public boolean requiresParensForTupleDistinctCounts() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java index 2e66da81973a..870e173c3a6e 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java @@ -100,6 +100,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static java.util.regex.Pattern.CASE_INSENSITIVE; @@ -953,8 +954,8 @@ public AggregateSupport getAggregateSupport() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } // features which change between 8i, 9i, and 10g ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 6dc5e338ff1b..3bf2fbf1b630 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -93,6 +93,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -885,8 +886,8 @@ public boolean supportsCaseInsensitiveLike() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java b/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java new file mode 100644 index 000000000000..ad3dd1d722b0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.annotations; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +import org.hibernate.dialect.Dialect; + +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.TableGenerator; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Generator that picks a strategy based on the {@linkplain Dialect#getNativeValueGenerationStrategy() dialect}. + * + * @since 7.0 + * + * @author Steve Ebersole + */ +@Target({METHOD, FIELD, TYPE, PACKAGE}) +@Retention(RUNTIME) +@IdGeneratorType(org.hibernate.id.NativeGenerator.class) +public @interface NativeGenerator { + SequenceGenerator sequenceForm() default @SequenceGenerator(); + TableGenerator tableForm() default @TableGenerator(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java index 90b952e1e002..8fa9b0d1dd6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java @@ -28,8 +28,10 @@ import org.hibernate.mapping.SimpleValue; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.ClassDetailsRegistry; import org.hibernate.models.spi.MemberDetails; import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.SourceModelContext; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.TableGenerator; @@ -92,13 +94,9 @@ public static A findLocalizedMatch( } // lastly, on the package - final String packageInfoFqn = StringHelper.qualifier( idMember.getDeclaringType().getClassName() ) + ".package-info"; - try { - final ClassDetails packageInfo = - context.getMetadataCollector() - .getSourceModelBuildingContext() - .getClassDetailsRegistry() - .resolveClassDetails( packageInfoFqn ); + final ClassDetails packageInfo = locatePackageInfoDetails( idMember.getDeclaringType(), context ); + if ( packageInfo != + null ) { for ( A generatorAnnotation : packageInfo.getRepeatedAnnotationUsages( generatorAnnotationType, sourceModelContext ) ) { if ( nameExtractor != null ) { final String registrationName = nameExtractor.apply( generatorAnnotation ); @@ -118,11 +116,27 @@ public static A findLocalizedMatch( } } } + + return possibleMatch; + } + + public static ClassDetails locatePackageInfoDetails(ClassDetails classDetails, MetadataBuildingContext buildingContext) { + return locatePackageInfoDetails( classDetails, buildingContext.getMetadataCollector().getSourceModelBuildingContext() ); + } + + public static ClassDetails locatePackageInfoDetails(ClassDetails classDetails, SourceModelContext modelContext) { + return locatePackageInfoDetails( classDetails, modelContext.getClassDetailsRegistry() ); + } + + public static ClassDetails locatePackageInfoDetails(ClassDetails classDetails, ClassDetailsRegistry classDetailsRegistry) { + final String packageInfoFqn = StringHelper.qualifier( classDetails.getName() ) + ".package-info"; + try { + return classDetailsRegistry.resolveClassDetails( packageInfoFqn ); + } catch (ClassLoadingException e) { // means there is no package-info + return null; } - - return possibleMatch; } public static void handleUuidStrategy( diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java index 630e1c929b61..72d110aeb41d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java @@ -637,7 +637,7 @@ private static G instantiateGeneratorViaDefaultConstructor } } - private static void callInitialize( + public static void callInitialize( A annotation, MemberDetails memberDetails, GeneratorCreationContext creationContext, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java index 9ae2ddf1e4f8..665bb28dab2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java @@ -34,6 +34,8 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; +import org.hibernate.models.spi.AnnotationTarget; +import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.MemberDetails; import org.hibernate.resource.beans.container.spi.BeanContainer; @@ -317,6 +319,10 @@ private void handleUnnamedAutoGenerator() { return; } + if ( handleAsMetaAnnotated() ) { + return; + } + if ( idMember.getType().isImplementor( UUID.class ) || idMember.getType().isImplementor( String.class ) ) { GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext ); @@ -326,6 +332,62 @@ private void handleUnnamedAutoGenerator() { handleSequenceGenerator( null, null ); } + private boolean handleAsMetaAnnotated() { + final Annotation fromMember = findGeneratorAnnotation( idMember ); + if ( fromMember != null ) { + handleIdGeneratorType( fromMember ); + return true; + } + + final Annotation fromClass = findGeneratorAnnotation( idMember.getDeclaringType() ); + if ( fromClass != null ) { + handleIdGeneratorType( fromClass ); + return true; + } + + final ClassDetails packageInfoDetails = GeneratorAnnotationHelper.locatePackageInfoDetails( idMember.getDeclaringType(), buildingContext ); + if ( packageInfoDetails != null ) { + final Annotation fromPackage = findGeneratorAnnotation( packageInfoDetails ); + if ( fromPackage != null ) { + handleIdGeneratorType( fromPackage ); + return true; + } + } + + return false; + } + + private Annotation findGeneratorAnnotation(AnnotationTarget annotationTarget) { + final List metaAnnotated = annotationTarget.getMetaAnnotated( IdGeneratorType.class, buildingContext.getMetadataCollector().getSourceModelBuildingContext() ); + if ( CollectionHelper.size( metaAnnotated ) > 0 ) { + return metaAnnotated.get( 0 ); + } + + return null; + } + + private void handleIdGeneratorType(Annotation generatorAnnotation) { + final IdGeneratorType markerAnnotation = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class ); + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final Generator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + markerAnnotation.value() + ); + final Map configuration = new HashMap<>(); + GeneratorParameters.collectParameters( + idValue, + buildingContext.getMetadataCollector().getDatabase().getDialect(), + entityMapping.getRootClass(), + configuration::put + ); + GeneratorBinder.callInitialize( generatorAnnotation, idMember, creationContext, identifierGenerator ); + callConfigure( creationContext, identifierGenerator, configuration, idValue ); + return identifierGenerator; + } ); + } + private void handleNamedAutoGenerator() { if ( handleAsLocalAutoGenerator() ) { return; @@ -347,29 +409,7 @@ private void handleNamedAutoGenerator() { return; } - final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); - final List metaAnnotated = - idMember.getMetaAnnotated( IdGeneratorType.class, metadataCollector.getSourceModelBuildingContext() ); - if ( CollectionHelper.size( metaAnnotated ) > 0 ) { - final Annotation generatorAnnotation = metaAnnotated.get( 0 ); - final IdGeneratorType markerAnnotation = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class ); - idValue.setCustomIdGeneratorCreator( (creationContext) -> { - - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); - final Generator identifierGenerator = instantiateGenerator( - beanContainer, - markerAnnotation.value() - ); - final Map configuration = new HashMap<>(); - GeneratorParameters.collectParameters( - idValue, - metadataCollector.getDatabase().getDialect(), - entityMapping.getRootClass(), - configuration::put - ); - callConfigure( creationContext, identifierGenerator, configuration, idValue ); - return identifierGenerator; - } ); + if ( handleAsMetaAnnotated() ) { return; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 5e7dee6227c2..cc5e836d892f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -75,6 +75,7 @@ import org.jboss.logging.Logger; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static java.lang.Integer.parseInt; @@ -620,8 +621,8 @@ public boolean requiresParensForTupleDistinctCounts() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index ad9d1f0a6cd4..d4de91e6bb1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -196,6 +196,7 @@ import org.jboss.logging.Logger; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import org.checkerframework.checker.nullness.qual.Nullable; @@ -2045,11 +2046,21 @@ public LobMergeStrategy getLobMergeStrategy() { * {@code "native"} is specified in {@code hbm.xml}. * * @return The name identifying the native generator strategy. + * + * @deprecated Use {@linkplain #getNativeValueGenerationStrategy()} instead */ + @Deprecated(since = "7.0", forRemoval = true) public String getNativeIdentifierGeneratorStrategy() { + return getNativeValueGenerationStrategy().name().toLowerCase( Locale.ROOT ); + } + + /** + * The native type of generation supported by this Dialect. + */ + public GenerationType getNativeValueGenerationStrategy() { return getIdentityColumnSupport().supportsIdentityColumns() - ? "identity" - : "sequence"; + ? GenerationType.IDENTITY + : GenerationType.SEQUENCE; } // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java index f8fbedd30163..41dc8ba8707f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DialectDelegateWrapper.java @@ -87,6 +87,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; /** @@ -349,8 +350,8 @@ public LobMergeStrategy getLobMergeStrategy() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return wrapped.getNativeIdentifierGeneratorStrategy(); + public GenerationType getNativeValueGenerationStrategy() { + return wrapped.getNativeValueGenerationStrategy(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 5574e03d4588..a5dce0bd06d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -100,6 +100,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static java.util.regex.Pattern.CASE_INSENSITIVE; @@ -1036,8 +1037,8 @@ public AggregateSupport getAggregateSupport() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } // features which change between 8i, 9i, and 10g ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index e7900ad3734e..7151ae0a9605 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -95,6 +95,7 @@ import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry; import org.hibernate.type.spi.TypeConfiguration; +import jakarta.persistence.GenerationType; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; @@ -847,8 +848,8 @@ public boolean supportsCaseInsensitiveLike() { } @Override - public String getNativeIdentifierGeneratorStrategy() { - return "sequence"; + public GenerationType getNativeValueGenerationStrategy() { + return GenerationType.SEQUENCE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java new file mode 100644 index 000000000000..dd45a1ed1ea0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java @@ -0,0 +1,188 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.id; + +import java.lang.reflect.Member; +import java.util.EnumSet; +import java.util.Map; +import java.util.Properties; + +import org.hibernate.boot.model.internal.GeneratorParameters; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.ExportableProducer; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.boot.models.annotations.internal.UuidGeneratorAnnotation; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.AnnotationBasedGenerator; +import org.hibernate.generator.BeforeExecutionGenerator; +import org.hibernate.generator.EventType; +import org.hibernate.generator.Generator; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.generator.OnExecutionGenerator; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.id.enhanced.TableGenerator; +import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; +import org.hibernate.id.uuid.UuidGenerator; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.persister.entity.EntityPersister; + +import jakarta.persistence.GenerationType; +import jakarta.persistence.SequenceGenerator; + +import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; +import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; + +/** + * Generator that picks a strategy based on the {@linkplain Dialect#getNativeValueGenerationStrategy() dialect}. + * + * @since 7.0 + * + * @author Steve Ebersole + */ +public class NativeGenerator + implements OnExecutionGenerator, BeforeExecutionGenerator, Configurable, ExportableProducer, AnnotationBasedGenerator { + private GenerationType generationType; + private org.hibernate.annotations.NativeGenerator annotation; + private Generator dialectNativeGenerator; + + public GenerationType getGenerationType() { + return generationType; + } + + @Override + public EnumSet getEventTypes() { + return dialectNativeGenerator.getEventTypes(); + } + + @Override + public boolean generatedOnExecution() { + return dialectNativeGenerator.generatedOnExecution(); + } + + @Override + public void initialize( + org.hibernate.annotations.NativeGenerator annotation, + Member member, + GeneratorCreationContext context) { + this.annotation = annotation; + + generationType = context.getDatabase() + .getDialect() + .getNativeValueGenerationStrategy(); + switch ( generationType ) { + case TABLE: { + dialectNativeGenerator = new TableGenerator(); + break; + } + case IDENTITY: { + dialectNativeGenerator = new IdentityGenerator(); + context.getProperty().getValue().getColumns().get( 0 ).setIdentity( true ); + break; + } + case UUID: { + dialectNativeGenerator = new UuidGenerator( new UuidGeneratorAnnotation( null ), member ); + break; + } + default: { + assert generationType == GenerationType.AUTO || generationType == GenerationType.SEQUENCE; + dialectNativeGenerator = new SequenceStyleGenerator(); + } + } + } + + @Override + public void configure(GeneratorCreationContext creationContext, Properties parameters) { + if ( dialectNativeGenerator instanceof SequenceStyleGenerator sequenceStyleGenerator ) { + applyProperties( parameters, annotation.sequenceForm(), creationContext ); + sequenceStyleGenerator.configure( creationContext, parameters ); + } + else if ( dialectNativeGenerator instanceof TableGenerator tableGenerator ) { + applyProperties( parameters, annotation.tableForm(), creationContext ); + tableGenerator.configure( creationContext, parameters ); + } + } + + @Override + public void registerExportables(Database database) { + if ( dialectNativeGenerator instanceof ExportableProducer exportableProducer ) { + exportableProducer.registerExportables(database); + } + } + + @Override + public void initialize(SqlStringGenerationContext context) { + if ( dialectNativeGenerator instanceof Configurable configurable ) { + configurable.initialize(context); + } + } + + @Override + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { + return ((BeforeExecutionGenerator) dialectNativeGenerator).generate(session, owner, currentValue, eventType); + } + + @Override + public boolean referenceColumnsInSql(Dialect dialect) { + return ((OnExecutionGenerator) dialectNativeGenerator).referenceColumnsInSql(dialect); + } + + @Override + public boolean writePropertyValue() { + return ((OnExecutionGenerator) dialectNativeGenerator).writePropertyValue(); + } + + @Override + public String[] getReferencedColumnValues(Dialect dialect) { + return ((OnExecutionGenerator) dialectNativeGenerator).getReferencedColumnValues(dialect); + } + + @Override + public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(EntityPersister persister) { + return ((OnExecutionGenerator) dialectNativeGenerator).getGeneratedIdentifierDelegate(persister); + } + + private void applyProperties( + Properties properties, + SequenceGenerator sequenceAnnotation, + GeneratorCreationContext creationContext) { + //noinspection unchecked,rawtypes + final Map mapRef = (Map) properties; + mapRef.put( GENERATOR_NAME, sequenceAnnotation.name() ); + applyCommonConfiguration( mapRef, creationContext ); + SequenceStyleGenerator.applyConfiguration( + sequenceAnnotation, + (SimpleValue) creationContext.getProperty().getValue(), + mapRef::put + ); + } + + private void applyProperties( + Properties properties, + jakarta.persistence.TableGenerator tableGenerator, + GeneratorCreationContext creationContext) { + //noinspection unchecked,rawtypes + final Map mapRef = (Map) properties; + mapRef.put( GENERATOR_NAME, tableGenerator.name() ); + applyCommonConfiguration( mapRef, creationContext ); + TableGenerator.applyConfiguration( + tableGenerator, + (SimpleValue) creationContext.getProperty().getValue(), + mapRef::put + ); + } + + private static void applyCommonConfiguration( + Map mapRef, + GeneratorCreationContext context) { + GeneratorParameters.collectParameters( + (SimpleValue) context.getProperty().getValue(), + context.getDatabase().getDialect(), + context.getRootClass(), + mapRef::put + ); + mapRef.put( INCREMENT_PARAM, 1 ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java new file mode 100644 index 000000000000..7bbd6a29d140 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idgen.n_ative.local; + + +import org.hibernate.generator.Generator; +import org.hibernate.id.NativeGenerator; +import org.hibernate.mapping.KeyValue; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +@SessionFactory +@DomainModel(annotatedClasses = NativeGeneratorClassTest.NativeEntity.class) +public class NativeGeneratorClassTest { + @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { + scope.inTransaction(s -> s.persist(new NativeEntity())); + + final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); + final Generator generator = identifier.createGenerator( null, null, null ); + assertThat( generator ).isInstanceOf( NativeGenerator.class ); + } + + @Entity + @org.hibernate.annotations.NativeGenerator + public static class NativeEntity { + @Id @GeneratedValue + long id; + String data; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java new file mode 100644 index 000000000000..3c291680a866 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idgen.n_ative.local; + + +import org.hibernate.generator.Generator; +import org.hibernate.id.NativeGenerator; +import org.hibernate.mapping.KeyValue; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +@SessionFactory +@DomainModel(annotatedClasses = NativeGeneratorMemberTest.NativeEntity.class) +public class NativeGeneratorMemberTest { + @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { + scope.inTransaction(s -> s.persist(new NativeEntity())); + + final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); + final Generator generator = identifier.createGenerator( null, null, null ); + assertThat( generator ).isInstanceOf( NativeGenerator.class ); + } + + @Entity + public static class NativeEntity { + @Id @GeneratedValue + @org.hibernate.annotations.NativeGenerator + long id; + String data; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java new file mode 100644 index 000000000000..da727961b57c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idgen.n_ative.pkg; + + +import org.hibernate.generator.Generator; +import org.hibernate.id.NativeGenerator; +import org.hibernate.mapping.KeyValue; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +import static org.assertj.core.api.Assertions.assertThat; + +@SessionFactory +@DomainModel(annotatedClasses = NativeGeneratorPackageTest.NativeEntity.class) +public class NativeGeneratorPackageTest { + @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { + scope.inTransaction(s -> s.persist(new NativeEntity())); + + final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); + final Generator generator = identifier.createGenerator( null, null, null ); + assertThat( generator ).isInstanceOf( NativeGenerator.class ); + } + + @Entity + public static class NativeEntity { + @Id @GeneratedValue + long id; + String data; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/package-info.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/package-info.java new file mode 100644 index 000000000000..813eef5641e7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/package-info.java @@ -0,0 +1,12 @@ +/* + * 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. + */ + +/** + * @author Steve Ebersole + */ +@org.hibernate.annotations.NativeGenerator +package org.hibernate.orm.test.idgen.n_ative.pkg; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java deleted file mode 100644 index f99aebdd9347..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.idgen.userdefined; - -import org.hibernate.boot.model.relational.Database; -import org.hibernate.boot.model.relational.ExportableProducer; -import org.hibernate.boot.model.relational.SqlStringGenerationContext; -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.generator.BeforeExecutionGenerator; -import org.hibernate.generator.EventType; -import org.hibernate.generator.Generator; -import org.hibernate.generator.GeneratorCreationContext; -import org.hibernate.generator.OnExecutionGenerator; -import org.hibernate.id.Configurable; -import org.hibernate.id.IdentityGenerator; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; -import org.hibernate.persister.entity.EntityPersister; - -import java.lang.reflect.Member; -import java.util.EnumSet; -import java.util.Properties; - -public class NativeGenerator - implements OnExecutionGenerator, BeforeExecutionGenerator, Configurable, ExportableProducer { - - private final Generator generator; - - public NativeGenerator(NativeId nativeId, Member member, GeneratorCreationContext creationContext) { - final String strategy = creationContext.getDatabase().getDialect().getNativeIdentifierGeneratorStrategy(); - switch (strategy) { - case "sequence": - generator = new SequenceStyleGenerator(); - break; - case "identity": - creationContext.getProperty().getValue().getColumns().get(0).setIdentity(true); - generator = new IdentityGenerator(); - break; - default: - throw new IllegalArgumentException(); - } - } - - @Override - public EnumSet getEventTypes() { - return generator.getEventTypes(); - } - - @Override - public boolean generatedOnExecution() { - return generator.generatedOnExecution(); - } - - @Override - public void configure(GeneratorCreationContext creationContext, Properties parameters) { - if ( generator instanceof Configurable ) { - ((Configurable) generator).configure( creationContext, parameters ); - } - } - - @Override - public void registerExportables(Database database) { - if ( generator instanceof ExportableProducer exportableProducer ) { - exportableProducer.registerExportables(database); - } - } - - @Override - public void initialize(SqlStringGenerationContext context) { - if ( generator instanceof Configurable configurable ) { - configurable.initialize(context); - } - } - - @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { - return ((BeforeExecutionGenerator) generator).generate(session, owner, currentValue, eventType); - } - - @Override - public boolean referenceColumnsInSql(Dialect dialect) { - return ((OnExecutionGenerator) generator).referenceColumnsInSql(dialect); - } - - @Override - public boolean writePropertyValue() { - return ((OnExecutionGenerator) generator).writePropertyValue(); - } - - @Override - public String[] getReferencedColumnValues(Dialect dialect) { - return ((OnExecutionGenerator) generator).getReferencedColumnValues(dialect); - } - - @Override - public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(EntityPersister persister) { - return ((OnExecutionGenerator) generator).getGeneratedIdentifierDelegate(persister); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java deleted file mode 100644 index ab788c255b37..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.idgen.userdefined; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.SessionFactory; -import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.junit.jupiter.api.Test; - -@SessionFactory -@DomainModel(annotatedClasses = NativeGeneratorTest.NativeEntity.class) -public class NativeGeneratorTest { - @Test void test(SessionFactoryScope scope) { - scope.inTransaction(s -> s.persist(new NativeEntity())); - } - @Entity - public static class NativeEntity { - @Id @NativeId - long id; - String data; - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java deleted file mode 100644 index b6dd4c494174..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * SPDX-License-Identifier: LGPL-2.1-or-later - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.orm.test.idgen.userdefined; - -import org.hibernate.annotations.IdGeneratorType; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@IdGeneratorType(NativeGenerator.class) -public @interface NativeId { -} From bd4e43888985b89d063fa56ea6f5321f7707c7e2 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 20 Sep 2024 18:52:38 -0500 Subject: [PATCH 2/4] HHH-18620 - Add @NativeGenerator --- .../annotations/NativeGenerator.java | 3 +- .../internal/GeneratorAnnotationHelper.java | 108 +++++++++++-- .../boot/model/internal/GeneratorBinder.java | 8 +- .../model/internal/GeneratorParameters.java | 64 ++++++-- .../IdBagIdGeneratorResolverSecondPass.java | 144 +++++++++++------ .../IdGeneratorResolverSecondPass.java | 150 +++++------------- .../StrictIdGeneratorResolverSecondPass.java | 64 +++++--- .../org/hibernate/id/NativeGenerator.java | 13 +- .../enhanced/SequenceNamingStrategyTest.java | 12 +- .../id/enhanced/TableNamingStrategyTest.java | 12 +- .../auto/AutoGenerationTypeTests.java | 9 +- .../idgen/n_ative/GeneratorSettingsImpl.java | 57 +++++++ .../local/NativeGeneratorClassTest.java | 15 +- .../local/NativeGeneratorMemberTest.java | 15 +- .../pkg/NativeGeneratorPackageTest.java | 14 +- 15 files changed, 449 insertions(+), 239 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/GeneratorSettingsImpl.java diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java b/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java index ad3dd1d722b0..0ce2d45e257d 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/NativeGenerator.java @@ -7,6 +7,7 @@ import java.lang.annotation.Target; import java.lang.annotation.Retention; +import org.hibernate.Incubating; import org.hibernate.dialect.Dialect; import jakarta.persistence.SequenceGenerator; @@ -22,12 +23,12 @@ * Generator that picks a strategy based on the {@linkplain Dialect#getNativeValueGenerationStrategy() dialect}. * * @since 7.0 - * * @author Steve Ebersole */ @Target({METHOD, FIELD, TYPE, PACKAGE}) @Retention(RUNTIME) @IdGeneratorType(org.hibernate.id.NativeGenerator.class) +@Incubating public @interface NativeGenerator { SequenceGenerator sequenceForm() default @SequenceGenerator(); TableGenerator tableForm() default @TableGenerator(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java index 8fa9b0d1dd6b..facfcab2c159 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java @@ -8,16 +8,22 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Properties; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; import org.hibernate.boot.model.IdentifierGeneratorDefinition; +import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.models.HibernateAnnotations; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.generator.AnnotationBasedGenerator; import org.hibernate.generator.Generator; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentityGenerator; import org.hibernate.id.PersistentIdentifierGenerator; @@ -32,11 +38,15 @@ import org.hibernate.models.spi.MemberDetails; import org.hibernate.models.spi.SourceModelBuildingContext; import org.hibernate.models.spi.SourceModelContext; +import org.hibernate.resource.beans.container.spi.BeanContainer; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.TableGenerator; +import static org.hibernate.boot.model.internal.GeneratorParameters.collectBaselineProperties; +import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; +import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; import static org.hibernate.internal.util.config.ConfigurationHelper.setIfNotEmpty; /** @@ -237,20 +247,96 @@ private static void applyAnnotationParameters(GenericGenerator generatorConfig, } public static void handleTableGenerator( - String generatorName, - TableGenerator generatorConfig, + String nameFromGeneratedValue, + TableGenerator generatorAnnotation, PersistentClass entityMapping, SimpleValue idValue, - MetadataBuildingContext context) { - final Map configuration = new HashMap<>(); - applyBaselineConfiguration( generatorConfig, idValue, entityMapping.getRootClass(), context, configuration::put ); - org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorConfig, configuration::put ); + MemberDetails idMember, + MetadataBuildingContext buildingContext) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final org.hibernate.id.enhanced.TableGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + org.hibernate.id.enhanced.TableGenerator.class + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( + INCREMENT_PARAM, + fallbackAllocationSize( generatorAnnotation, buildingContext ) + ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> org.hibernate.id.enhanced.TableGenerator.applyConfiguration( + generatorAnnotation, + properties::put + ), + creationContext + ); + return identifierGenerator; + } ); + } - GeneratorBinder.createGeneratorFrom( - new IdentifierGeneratorDefinition( generatorName, org.hibernate.id.enhanced.TableGenerator.class.getName(), configuration ), - idValue, - context - ); + /** + * Prepares a generator for use by handling its various potential means of "configuration". + * + * @param generator The "empty" generator + * @param annotation The annotation which defines configuration for the generator + * @param idMember The member defining the id + * @param configBaseline Allows to set any default values. Called before common config is handled. + * @param configExtractor Allows to extract values from the generator annotation. Called after common config is handled. + * @param creationContext Access to useful information + */ + public static void prepareForUse( + Generator generator, + A annotation, + MemberDetails idMember, + Consumer configBaseline, + BiConsumer configExtractor, + GeneratorCreationContext creationContext) { + if ( generator instanceof Configurable configurable ) { + final Properties properties = new Properties(); + if ( configBaseline != null ) { + configBaseline.accept( properties ); + } + collectBaselineProperties( + creationContext.getProperty() != null + ? (SimpleValue) creationContext.getProperty().getValue() + : (SimpleValue) creationContext.getPersistentClass().getIdentifierProperty().getValue(), + creationContext.getDatabase().getDialect(), + creationContext.getRootClass(), + properties::setProperty + ); + if ( configExtractor != null ) { + configExtractor.accept( annotation, properties ); + } + configurable.configure( creationContext, properties ); + } + if ( generator instanceof AnnotationBasedGenerator ) { + // This will cause a CCE in case the generation type doesn't match the annotation type; As this would be + // a programming error of the generation type developer and thus should show up during testing, we don't + // check this explicitly; If required, this could be done e.g. using ClassMate + @SuppressWarnings("unchecked") + final AnnotationBasedGenerator generation = (AnnotationBasedGenerator) generator; + generation.initialize( annotation, idMember.toJavaMember(), creationContext ); + } + if ( generator instanceof ExportableProducer exportableProducer ) { + exportableProducer.registerExportables( creationContext.getDatabase() ); + } + if ( generator instanceof Configurable configurable ) { + configurable.initialize( creationContext.getSqlStringGenerationContext() ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java index 72d110aeb41d..a9d4946cd83c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java @@ -536,9 +536,9 @@ public B produceBeanInstance(String name, Class beanType) { * @param beanContainer an optional {@code BeanContainer} * @param generatorClass a class which implements {@code Generator} */ - private static Generator instantiateGeneratorAsBean( + private static T instantiateGeneratorAsBean( BeanContainer beanContainer, - Class generatorClass) { + Class generatorClass) { return beanContainer.getBean( generatorClass, new BeanContainer.LifecycleOptions() { @Override @@ -611,9 +611,9 @@ private static G instantiateGenerator( * @param beanContainer an optional {@code BeanContainer} * @param generatorClass a class which implements {@code Generator} */ - public static Generator instantiateGenerator( + public static T instantiateGenerator( BeanContainer beanContainer, - Class generatorClass) { + Class generatorClass) { if ( beanContainer != null ) { return instantiateGeneratorAsBean( beanContainer, generatorClass ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorParameters.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorParameters.java index 1b5c3dfa3d50..b7902a3075f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorParameters.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorParameters.java @@ -4,12 +4,14 @@ */ package org.hibernate.boot.model.internal; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.TableGenerator; -import jakarta.persistence.UniqueConstraint; +import java.lang.annotation.Annotation; +import java.util.Map; +import java.util.Properties; +import java.util.function.BiConsumer; + import org.hibernate.Internal; import org.hibernate.boot.model.IdentifierGeneratorDefinition; -import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; @@ -30,10 +32,11 @@ import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.Table; -import java.util.Map; -import java.util.Properties; -import java.util.function.BiConsumer; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.TableGenerator; +import jakarta.persistence.UniqueConstraint; +import static org.hibernate.cfg.MappingSettings.ID_DB_STRUCTURE_NAMING_STRATEGY; import static org.hibernate.id.IdentifierGenerator.CONTRIBUTOR_NAME; import static org.hibernate.id.IdentifierGenerator.ENTITY_NAME; import static org.hibernate.id.IdentifierGenerator.JPA_ENTITY_NAME; @@ -93,15 +96,49 @@ public static void collectParameters( RootClass rootClass, BiConsumer parameterCollector) { - final ConfigurationService configService = - identifierValue.getMetadata().getMetadataBuildingOptions().getServiceRegistry() - .requireService( ConfigurationService.class ); - + final ConfigurationService configService = identifierValue + .getMetadata() + .getMetadataBuildingOptions() + .getServiceRegistry() + .requireService( ConfigurationService.class ); // default initial value and allocation size per-JPA defaults parameterCollector.accept( INITIAL_PARAM, String.valueOf( DEFAULT_INITIAL_VALUE ) ); parameterCollector.accept( INCREMENT_PARAM, String.valueOf( defaultIncrement( configService ) ) ); + collectBaselineProperties( identifierValue, dialect, rootClass, parameterCollector ); + } + + public static int fallbackAllocationSize(Annotation generatorAnnotation, MetadataBuildingContext buildingContext) { + if ( generatorAnnotation == null ) { + final ConfigurationService configService = buildingContext + .getBootstrapContext() + .getServiceRegistry() + .requireService( ConfigurationService.class ); + final String idNamingStrategy = configService.getSetting( ID_DB_STRUCTURE_NAMING_STRATEGY, StandardConverters.STRING ); + if ( LegacyNamingStrategy.STRATEGY_NAME.equals( idNamingStrategy ) + || LegacyNamingStrategy.class.getName().equals( idNamingStrategy ) + || SingleNamingStrategy.STRATEGY_NAME.equals( idNamingStrategy ) + || SingleNamingStrategy.class.getName().equals( idNamingStrategy ) ) { + return 1; + } + } + + return OptimizableGenerator.DEFAULT_INCREMENT_SIZE; + } + + public static void collectBaselineProperties( + SimpleValue identifierValue, + Dialect dialect, + RootClass rootClass, + BiConsumer parameterCollector) { + + final ConfigurationService configService = identifierValue + .getMetadata() + .getMetadataBuildingOptions() + .getServiceRegistry() + .requireService( ConfigurationService.class ); + //init the table here instead of earlier, so that we can get a quoted table name //TODO: would it be better to simply pass the qualified table name, instead of // splitting it up into schema/catalog/table names @@ -144,6 +181,7 @@ public static void collectParameters( (String) settings.get( AvailableSettings.PREFERRED_POOLED_OPTIMIZER ) ); } + } public static String identityTablesString(Dialect dialect, RootClass rootClass) { @@ -157,10 +195,6 @@ public static String identityTablesString(Dialect dialect, RootClass rootClass) return tables.toString(); } - public static int defaultIncrement(MetadataImplementor metadata) { - return defaultIncrement( metadata.getMetadataBuildingOptions().getServiceRegistry().requireService( ConfigurationService.class ) ); - } - public static int defaultIncrement(ConfigurationService configService) { final String idNamingStrategy = configService.getSetting( AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdBagIdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdBagIdGeneratorResolverSecondPass.java index 6fa5ee085257..202064568cf6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdBagIdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdBagIdGeneratorResolverSecondPass.java @@ -24,14 +24,17 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; import org.hibernate.models.spi.MemberDetails; +import org.hibernate.resource.beans.container.spi.BeanContainer; import jakarta.persistence.GeneratedValue; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.TableGenerator; -import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.applyBaselineConfiguration; import static org.hibernate.boot.model.internal.GeneratorBinder.createGeneratorFrom; import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator; +import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; +import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; +import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; /** * IdGeneratorResolver for handling generators assigned to id-bag mappings @@ -41,7 +44,7 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver { private final PersistentClass entityMapping; private final SimpleValue idValue; - private final MemberDetails idAttributeMember; + private final MemberDetails idBagMember; private final String generatorType; private final String generatorName; private final MetadataBuildingContext buildingContext; @@ -50,13 +53,13 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver { public IdBagIdGeneratorResolverSecondPass( IdentifierBag idBagMapping, SimpleValue idValue, - MemberDetails idAttributeMember, + MemberDetails idBagMember, String generatorType, String generatorName, MetadataBuildingContext buildingContext) { this.entityMapping = null; this.idValue = idValue; - this.idAttributeMember = idAttributeMember; + this.idBagMember = idBagMember; this.generatorType = generatorType; this.generatorName = generatorName; this.buildingContext = buildingContext; @@ -66,27 +69,27 @@ public IdBagIdGeneratorResolverSecondPass( @Override public void doSecondPass(Map idGeneratorDefinitionMap) throws MappingException { - final GeneratedValue generatedValue = idAttributeMember.getDirectAnnotationUsage( GeneratedValue.class ); + final GeneratedValue generatedValue = idBagMember.getDirectAnnotationUsage( GeneratedValue.class ); switch ( generatedValue.strategy() ) { - case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idAttributeMember, buildingContext ); + case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idBagMember, buildingContext ); case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue ); case SEQUENCE -> handleSequenceStrategy( generatorName, idValue, - idAttributeMember, + idBagMember, buildingContext ); case TABLE -> handleTableStrategy( generatorName, entityMapping, idValue, - idAttributeMember, + idBagMember, buildingContext ); case AUTO -> handleAutoStrategy( generatorName, idValue, - idAttributeMember, + idBagMember, buildingContext ); } @@ -96,7 +99,7 @@ private void handleTableStrategy( String generatorName, PersistentClass entityMapping, SimpleValue idValue, - MemberDetails idAttributeMember, + MemberDetails idBagMember, MetadataBuildingContext buildingContext) { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final GlobalRegistrations globalRegistrations = metadataCollector.getGlobalRegistrations(); @@ -107,8 +110,8 @@ private void handleTableStrategy( handleTableGenerator( generatorName, globalTableGenerator.configuration(), - configuration, idValue, + idBagMember, buildingContext ); return; @@ -116,13 +119,13 @@ private void handleTableStrategy( final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, - idAttributeMember, + idBagMember, TableGenerator::name, generatorName, buildingContext ); if ( localizedTableMatch != null ) { - handleTableGenerator( generatorName, localizedTableMatch, configuration, idValue, buildingContext ); + handleTableGenerator( generatorName, localizedTableMatch, idValue, idBagMember, buildingContext ); return; } @@ -131,6 +134,7 @@ private void handleTableStrategy( new TableGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ), entityMapping, idValue, + idBagMember, buildingContext ); } @@ -138,7 +142,7 @@ private void handleTableStrategy( private void handleSequenceStrategy( String generatorName, SimpleValue idValue, - MemberDetails idAttributeMember, + MemberDetails idBagMember, MetadataBuildingContext buildingContext) { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final GlobalRegistrations globalRegistrations = metadataCollector.getGlobalRegistrations(); @@ -149,8 +153,8 @@ private void handleSequenceStrategy( handleSequenceGenerator( generatorName, globalSequenceGenerator.configuration(), - configuration, idValue, + idBagMember, buildingContext ); return; @@ -158,21 +162,21 @@ private void handleSequenceStrategy( final SequenceGenerator localizedSequencedMatch = GeneratorAnnotationHelper.findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, - idAttributeMember, + idBagMember, SequenceGenerator::name, generatorName, buildingContext ); if ( localizedSequencedMatch != null ) { - handleSequenceGenerator( generatorName, localizedSequencedMatch, configuration, idValue, buildingContext ); + handleSequenceGenerator( generatorName, localizedSequencedMatch, idValue, idBagMember, buildingContext ); return; } handleSequenceGenerator( generatorName, new SequenceGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ), - configuration, idValue, + idBagMember, buildingContext ); } @@ -180,7 +184,7 @@ private void handleSequenceStrategy( private void handleAutoStrategy( String generatorName, SimpleValue idValue, - MemberDetails idAttributeMember, + MemberDetails idBagMember, MetadataBuildingContext buildingContext) { final GlobalRegistrations globalRegistrations = buildingContext.getMetadataCollector().getGlobalRegistrations(); @@ -191,8 +195,8 @@ private void handleAutoStrategy( handleSequenceGenerator( generatorName, globalSequenceGenerator.configuration(), - configuration, idValue, + idBagMember, buildingContext ); return; @@ -204,8 +208,8 @@ private void handleAutoStrategy( handleTableGenerator( generatorName, globalTableGenerator.configuration(), - configuration, idValue, + idBagMember, buildingContext ); return; @@ -230,62 +234,98 @@ private void handleAutoStrategy( final SequenceGenerator localizedSequencedMatch = GeneratorAnnotationHelper.findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, - idAttributeMember, + idBagMember, SequenceGenerator::name, generatorName, buildingContext ); if ( localizedSequencedMatch != null ) { - handleSequenceGenerator( generatorName, localizedSequencedMatch, configuration, idValue, buildingContext ); + handleSequenceGenerator( generatorName, localizedSequencedMatch, idValue, idBagMember, buildingContext ); return; } final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, - idAttributeMember, + idBagMember, TableGenerator::name, generatorName, buildingContext ); if ( localizedTableMatch != null ) { - handleTableGenerator( generatorName, localizedTableMatch, configuration, idValue, buildingContext ); + handleTableGenerator( generatorName, localizedTableMatch, idValue, idBagMember, buildingContext ); return; } - makeIdGenerator( idValue, idAttributeMember, generatorType, generatorName, buildingContext, null ); + makeIdGenerator( idValue, idBagMember, generatorType, generatorName, buildingContext, null ); } public static void handleSequenceGenerator( - String generatorName, - SequenceGenerator generatorConfig, - Map configuration, + String nameFromGeneratedValue, + SequenceGenerator generatorAnnotation, SimpleValue idValue, - MetadataBuildingContext context) { - applyBaselineConfiguration( generatorConfig, idValue, null, context, configuration::put ); - SequenceStyleGenerator.applyConfiguration( generatorConfig, configuration::put ); - createGeneratorFrom( - new IdentifierGeneratorDefinition( generatorName, SequenceStyleGenerator.class.getName(), configuration ), - idValue, - (Map) configuration, - context - ); + MemberDetails idBagMember, + MetadataBuildingContext buildingContext) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + SequenceStyleGenerator.class + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idBagMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), + creationContext + ); + return identifierGenerator; + } ); } public static void handleTableGenerator( - String generatorName, - TableGenerator generatorConfig, - Map configuration, + String nameFromGeneratedValue, + TableGenerator generatorAnnotation, SimpleValue idValue, - MetadataBuildingContext context) { - GeneratorAnnotationHelper.applyBaselineConfiguration( generatorConfig, idValue, null, context, configuration::put ); - org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorConfig, configuration::put ); - - createGeneratorFrom( - new IdentifierGeneratorDefinition( generatorName, org.hibernate.id.enhanced.TableGenerator.class.getName(), configuration ), - idValue, - (Map) configuration, - context - ); - + MemberDetails idBagMember, + MetadataBuildingContext buildingContext) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final org.hibernate.id.enhanced.TableGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + org.hibernate.id.enhanced.TableGenerator.class + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idBagMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorAnnotation, properties::put ), + creationContext + ); + return identifierGenerator; + } ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java index 665bb28dab2e..d0f7a4a70f95 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java @@ -5,7 +5,6 @@ package org.hibernate.boot.model.internal; import java.lang.annotation.Annotation; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -21,16 +20,9 @@ import org.hibernate.boot.models.spi.GlobalRegistrations; import org.hibernate.boot.models.spi.SequenceGeneratorRegistration; import org.hibernate.boot.models.spi.TableGeneratorRegistration; -import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; -import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.generator.Generator; -import org.hibernate.id.IdentityGenerator; -import org.hibernate.id.OptimizableGenerator; -import org.hibernate.id.enhanced.LegacyNamingStrategy; import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.id.enhanced.SingleNamingStrategy; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; @@ -43,10 +35,8 @@ import jakarta.persistence.SequenceGenerator; import jakarta.persistence.TableGenerator; -import static org.hibernate.boot.model.internal.GeneratorBinder.callConfigure; -import static org.hibernate.boot.model.internal.GeneratorBinder.instantiateGenerator; +import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator; -import static org.hibernate.cfg.MappingSettings.ID_DB_STRUCTURE_NAMING_STRATEGY; import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; @@ -369,21 +359,19 @@ private Annotation findGeneratorAnnotation(AnnotationTarget annotationTarget) { private void handleIdGeneratorType(Annotation generatorAnnotation) { final IdGeneratorType markerAnnotation = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class ); idValue.setCustomIdGeneratorCreator( (creationContext) -> { - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); final Generator identifierGenerator = GeneratorBinder.instantiateGenerator( beanContainer, markerAnnotation.value() ); - final Map configuration = new HashMap<>(); - GeneratorParameters.collectParameters( - idValue, - buildingContext.getMetadataCollector().getDatabase().getDialect(), - entityMapping.getRootClass(), - configuration::put + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + null, + null, + creationContext ); - GeneratorBinder.callInitialize( generatorAnnotation, idMember, creationContext, identifierGenerator ); - callConfigure( creationContext, identifierGenerator, configuration, idValue ); return identifierGenerator; } ); } @@ -506,97 +494,45 @@ private boolean handleAsNamedGlobalAutoGenerator() { return false; } - private void handleSequenceGenerator(String nameFromGeneratedValue, SequenceGenerator generator) { - createGeneratorFrom( SequenceStyleGenerator.class, extractConfiguration( nameFromGeneratedValue, generator ) ); - } - - private Map extractConfiguration(String nameFromGenerated, SequenceGenerator generator) { - final Map configuration = new HashMap<>(); - if ( generator != null ) { - configuration.put( GENERATOR_NAME, generator.name() ); - } - else if ( nameFromGenerated != null ) { - configuration.put( GENERATOR_NAME, nameFromGenerated ); - } - - applyCommonConfiguration( configuration, generator ); - - if ( generator != null ) { - SequenceStyleGenerator.applyConfiguration( generator, configuration::put ); - } - - return configuration; + private void handleSequenceGenerator(String nameFromGeneratedValue, SequenceGenerator generatorAnnotation) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + SequenceStyleGenerator.class + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), + creationContext + ); + return identifierGenerator; + } ); } - private void applyCommonConfiguration(Map configuration, Annotation generatorAnnotation) { - GeneratorParameters.collectParameters( + private void handleTableGenerator(String nameFromGeneratedValue, TableGenerator generatorAnnotation) { + GeneratorAnnotationHelper.handleTableGenerator( + nameFromGeneratedValue, + generatorAnnotation, + entityMapping, idValue, - buildingContext.getMetadataCollector().getDatabase().getDialect(), - entityMapping.getRootClass(), - configuration::put + idMember, + buildingContext ); - - // we need to better handle default allocation-size here... - configuration.put( INCREMENT_PARAM, fallbackAllocationSize( buildingContext, generatorAnnotation ) ); } - private static int fallbackAllocationSize(MetadataBuildingContext buildingContext, Annotation generatorAnnotation) { - if ( generatorAnnotation == null ) { - // Special case where we have no matching SequenceGenerator/TableGenerator annotation. - // Historically we interpreted such cases using a default of 1, but JPA says the default - // here should be 50. As a migration aid, under the assumption that one of the legacy - // naming-strategies are used in such cases, we revert to the old default; otherwise we - // use the compliant value. - final ConfigurationService configService = - buildingContext.getBootstrapContext().getServiceRegistry() - .requireService( ConfigurationService.class ); - final String idNamingStrategy = - configService.getSetting( ID_DB_STRUCTURE_NAMING_STRATEGY, StandardConverters.STRING ); - if ( LegacyNamingStrategy.STRATEGY_NAME.equals( idNamingStrategy ) - || LegacyNamingStrategy.class.getName().equals( idNamingStrategy ) - || SingleNamingStrategy.STRATEGY_NAME.equals( idNamingStrategy ) - || SingleNamingStrategy.class.getName().equals( idNamingStrategy ) ) { - return 1; - } - } - - return OptimizableGenerator.DEFAULT_INCREMENT_SIZE; - } - - private void handleTableGenerator(String nameFromGeneratedValue, TableGenerator generator) { - createGeneratorFrom( org.hibernate.id.enhanced.TableGenerator.class, - extractConfiguration( nameFromGeneratedValue, generator ) ); - } - - private Map extractConfiguration(String nameFromGenerated, TableGenerator generator) { - final Map configuration = new HashMap<>(); - if ( generator != null ) { - configuration.put( GENERATOR_NAME, generator.name() ); - } - else if ( nameFromGenerated != null ) { - configuration.put( GENERATOR_NAME, nameFromGenerated ); - } - - applyCommonConfiguration( configuration, generator ); - - if ( generator != null ) { - org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generator, configuration::put ); - } - - return configuration; - } - - private void createGeneratorFrom( - Class generatorClass, - Map configuration) { - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); - idValue.setCustomIdGeneratorCreator( (creationContext) -> { - final Generator identifierGenerator = instantiateGenerator( beanContainer, generatorClass ); - callConfigure( creationContext, identifierGenerator, configuration, idValue ); - if ( identifierGenerator instanceof IdentityGenerator ) { - idValue.setColumnToIdentity(); - } - return identifierGenerator; - } ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java index f6ef3da0b529..d27bf34a049b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java @@ -26,18 +26,21 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; import org.hibernate.models.spi.MemberDetails; +import org.hibernate.resource.beans.container.spi.BeanContainer; import jakarta.persistence.GeneratedValue; import jakarta.persistence.SequenceGenerator; import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.*; import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleUuidStrategy; +import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; import static org.hibernate.boot.model.internal.GeneratorParameters.identityTablesString; import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator; import static org.hibernate.id.IdentifierGenerator.ENTITY_NAME; import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; import static org.hibernate.id.IdentifierGenerator.JPA_ENTITY_NAME; import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE; +import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; import static org.hibernate.id.PersistentIdentifierGenerator.PK; import static org.hibernate.id.PersistentIdentifierGenerator.TABLE; import static org.hibernate.id.PersistentIdentifierGenerator.TABLES; @@ -107,7 +110,6 @@ private void handleUnnamedSequenceGenerator() { handleSequenceGenerator( entityMapping.getJpaEntityName(), globalMatch.configuration(), - entityMapping, idValue, idMember, buildingContext @@ -118,7 +120,6 @@ private void handleUnnamedSequenceGenerator() { handleSequenceGenerator( entityMapping.getJpaEntityName(), new SequenceGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ), - entityMapping, idValue, idMember, buildingContext @@ -135,7 +136,6 @@ private void handleNamedSequenceGenerator(GeneratedValue generatedValue) { handleSequenceGenerator( generatedValue.generator(), globalMatch.configuration(), - entityMapping, idValue, idMember, buildingContext @@ -146,7 +146,6 @@ private void handleNamedSequenceGenerator(GeneratedValue generatedValue) { handleSequenceGenerator( generatedValue.generator(), new SequenceGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ), - entityMapping, idValue, idMember, buildingContext @@ -174,6 +173,7 @@ private void handleUnnamedTableGenerator() { globalMatch.configuration(), entityMapping, idValue, + idMember, buildingContext ); return; @@ -184,6 +184,7 @@ private void handleUnnamedTableGenerator() { new TableGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ), entityMapping, idValue, + idMember, buildingContext ); } @@ -200,6 +201,7 @@ private void handleNamedTableGenerator(GeneratedValue generatedValue) { globalMatch.configuration(), entityMapping, idValue, + idMember, buildingContext ); @@ -211,6 +213,7 @@ private void handleNamedTableGenerator(GeneratedValue generatedValue) { new TableGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ), entityMapping, idValue, + idMember, buildingContext ); } @@ -228,7 +231,6 @@ private void handleAutoStrategy(GeneratedValue generatedValue) { handleSequenceGenerator( globalRegistrationName, globalSequenceMatch.configuration(), - entityMapping, idValue, idMember, buildingContext @@ -244,6 +246,7 @@ private void handleAutoStrategy(GeneratedValue generatedValue) { globalTableMatch.configuration(), entityMapping, idValue, + idMember, buildingContext ); return; @@ -289,7 +292,6 @@ private void handleAutoStrategy(GeneratedValue generatedValue) { handleSequenceGenerator( globalRegistrationName, new SequenceGeneratorJpaAnnotation( generator, metadataCollector.getSourceModelBuildingContext() ), - entityMapping, idValue, idMember, buildingContext @@ -327,27 +329,37 @@ private HashMap buildLegacyGeneratorConfig() { } public static void handleSequenceGenerator( - String generatorName, - SequenceGenerator generatorConfig, - PersistentClass entityMapping, + String nameFromGeneratedValue, + SequenceGenerator generatorAnnotation, SimpleValue idValue, MemberDetails idMember, - MetadataBuildingContext context) { - //generator settings - final Map configuration = new HashMap<>(); - applyBaselineConfiguration( generatorConfig, idValue, entityMapping.getRootClass(), context, configuration::put ); - if ( generatorConfig == null ) { - configuration.put( GENERATOR_NAME, generatorName ); - } - else { - SequenceStyleGenerator.applyConfiguration( generatorConfig, configuration::put ); - } - - GeneratorBinder.createGeneratorFrom( - new IdentifierGeneratorDefinition( generatorName, SequenceStyleGenerator.class.getName(), configuration ), - idValue, - context - ); - + MetadataBuildingContext buildingContext) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + SequenceStyleGenerator.class + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), + creationContext + ); + return identifierGenerator; + } ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java index dd45a1ed1ea0..43aaa6eab984 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/NativeGenerator.java @@ -38,6 +38,7 @@ /** * Generator that picks a strategy based on the {@linkplain Dialect#getNativeValueGenerationStrategy() dialect}. * + * @see org.hibernate.annotations.NativeGenerator * @since 7.0 * * @author Steve Ebersole @@ -152,11 +153,7 @@ private void applyProperties( final Map mapRef = (Map) properties; mapRef.put( GENERATOR_NAME, sequenceAnnotation.name() ); applyCommonConfiguration( mapRef, creationContext ); - SequenceStyleGenerator.applyConfiguration( - sequenceAnnotation, - (SimpleValue) creationContext.getProperty().getValue(), - mapRef::put - ); + SequenceStyleGenerator.applyConfiguration( sequenceAnnotation, mapRef::put ); } private void applyProperties( @@ -167,11 +164,7 @@ private void applyProperties( final Map mapRef = (Map) properties; mapRef.put( GENERATOR_NAME, tableGenerator.name() ); applyCommonConfiguration( mapRef, creationContext ); - TableGenerator.applyConfiguration( - tableGenerator, - (SimpleValue) creationContext.getProperty().getValue(), - mapRef::put - ); + TableGenerator.applyConfiguration( tableGenerator, mapRef::put ); } private static void applyCommonConfiguration( diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/SequenceNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/SequenceNamingStrategyTest.java index ee4a2643655d..f17df72c956f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/SequenceNamingStrategyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/SequenceNamingStrategyTest.java @@ -27,6 +27,7 @@ import org.hibernate.id.enhanced.StandardNamingStrategy; import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.PersistentClass; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; import org.hibernate.service.ServiceRegistry; import org.hibernate.testing.orm.junit.BaseUnitTest; @@ -129,7 +130,7 @@ private void verify(Class entityType, String strategy, String expectedName) { assertThat( sequence ).isNotNull(); final PersistentClass entityBinding = metadata.getEntityBinding( entityType.getName() ); - final IdentifierGenerator generator = extractGenerator( entityBinding ); + final IdentifierGenerator generator = extractGenerator( metadata, entityBinding ); assertThat( generator ).isInstanceOf( SequenceStyleGenerator.class ); final SequenceStyleGenerator sequenceStyleGenerator = (SequenceStyleGenerator) generator; assertThat( sequenceStyleGenerator.getDatabaseStructure() ).isInstanceOf( SequenceStructure.class ); @@ -158,9 +159,14 @@ private static void withMetadata(Class entityClass, String namingStrategy, Co } } - private IdentifierGenerator extractGenerator(PersistentClass entityBinding) { + private IdentifierGenerator extractGenerator(MetadataImplementor metadataImplementor, PersistentClass entityBinding) { KeyValue keyValue = entityBinding.getIdentifier(); - final Generator generator = keyValue.createGenerator(null, null); + final Generator generator = keyValue.createGenerator( + metadataImplementor.getDatabase().getDialect(), + entityBinding.getRootClass(), + entityBinding.getIdentifierProperty(), + new GeneratorSettingsImpl( metadataImplementor ) + ); return generator instanceof IdentifierGenerator ? (IdentifierGenerator) generator : null; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/TableNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/TableNamingStrategyTest.java index 022c52f7337b..2b3bd8931417 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/TableNamingStrategyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/enhanced/TableNamingStrategyTest.java @@ -24,6 +24,7 @@ import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Table; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; import org.hibernate.service.ServiceRegistry; import org.hibernate.testing.orm.junit.BaseUnitTest; @@ -112,7 +113,7 @@ private void verify(Class entityType, String strategy, String expectedName) { assertThat( table ).isNotNull(); final PersistentClass entityBinding = metadata.getEntityBinding( entityType.getName() ); - final IdentifierGenerator generator = extractGenerator( entityBinding ); + final IdentifierGenerator generator = extractGenerator( entityBinding, metadata ); assertThat( generator ).isInstanceOf( org.hibernate.id.enhanced.TableGenerator.class ); final org.hibernate.id.enhanced.TableGenerator tableGenerator = (org.hibernate.id.enhanced.TableGenerator) generator; assertThat( tableGenerator.getTableName() ).isEqualTo( expectedName ); @@ -139,9 +140,14 @@ private static void withMetadata(Class entityClass, String namingStrategy, Co } } - private IdentifierGenerator extractGenerator(PersistentClass entityBinding) { + private IdentifierGenerator extractGenerator(PersistentClass entityBinding, MetadataImplementor metadata) { KeyValue keyValue = entityBinding.getIdentifier(); - final Generator generator = keyValue.createGenerator(null, null); + final Generator generator = keyValue.createGenerator( + metadata.getDatabase().getDialect(), + entityBinding.getRootClass(), + entityBinding.getIdentifierProperty(), + new GeneratorSettingsImpl( metadata ) + ); return generator instanceof IdentifierGenerator ? (IdentifierGenerator) generator : null; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/auto/AutoGenerationTypeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/auto/AutoGenerationTypeTests.java index 263ca32b469f..8c88ad77efc1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/auto/AutoGenerationTypeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/enhanced/auto/AutoGenerationTypeTests.java @@ -32,6 +32,8 @@ import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; + import org.hibernate.testing.orm.junit.FailureExpectedExtension; import org.hibernate.testing.util.ServiceRegistryUtil; import org.junit.jupiter.api.Test; @@ -85,7 +87,12 @@ public void testAutoGeneratedValueGenerator() { final PersistentClass entityBinding = metadata.getEntityBinding( Entity2.class.getName() ); final KeyValue idMapping = entityBinding.getRootClass().getIdentifier(); Dialect dialect = new H2Dialect(); - final Generator generator = idMapping.createGenerator( dialect, entityBinding.getRootClass()); + final Generator generator = idMapping.createGenerator( + dialect, + entityBinding.getRootClass(), + entityBinding.getIdentifierProperty(), + new GeneratorSettingsImpl( metadata ) + ); final SequenceStyleGenerator idGenerator = (SequenceStyleGenerator) (generator instanceof IdentifierGenerator ? (IdentifierGenerator) generator : null); final DatabaseStructure database2Structure = idGenerator.getDatabaseStructure(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/GeneratorSettingsImpl.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/GeneratorSettingsImpl.java new file mode 100644 index 000000000000..b73985d1736e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/GeneratorSettingsImpl.java @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.idgen.n_ative; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.Namespace; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.mapping.GeneratorSettings; + +import static org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl.fromExplicit; + +/** + * @author Steve Ebersole + */ +public class GeneratorSettingsImpl implements GeneratorSettings { + private final String defaultCatalog; + private final String defaultSchema; + private final SqlStringGenerationContext sqlStringGenerationContext; + + public GeneratorSettingsImpl(Metadata domainModel) { + final Database database = domainModel.getDatabase(); + final Namespace defaultNamespace = database.getDefaultNamespace(); + final Namespace.Name defaultNamespaceName = defaultNamespace.getName(); + + defaultCatalog = defaultNamespaceName.getCatalog() == null + ? "" + : defaultNamespaceName.getCatalog().render( database.getDialect() ); + defaultSchema = defaultNamespaceName.getSchema() == null + ? "" + : defaultNamespaceName.getSchema().render( database.getDialect() ); + + sqlStringGenerationContext = fromExplicit( + database.getJdbcEnvironment(), + database, + defaultCatalog, + defaultSchema + ); + } + + @Override + public String getDefaultCatalog() { + return defaultCatalog; + } + + @Override + public String getDefaultSchema() { + return defaultSchema; + } + + @Override + public SqlStringGenerationContext getSqlStringGenerationContext() { + return sqlStringGenerationContext; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java index 7bbd6a29d140..5ebd26dc9430 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorClassTest.java @@ -8,6 +8,9 @@ import org.hibernate.generator.Generator; import org.hibernate.id.NativeGenerator; import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModelScope; @@ -27,8 +30,16 @@ public class NativeGeneratorClassTest { @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { scope.inTransaction(s -> s.persist(new NativeEntity())); - final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); - final Generator generator = identifier.createGenerator( null, null, null ); + final PersistentClass entityBinding = domainModelScope.getEntityBinding( NativeEntity.class ); + final Property idProperty = entityBinding.getIdentifierProperty(); + final KeyValue identifier = entityBinding.getIdentifier(); + + final Generator generator = identifier.createGenerator( + domainModelScope.getDomainModel().getDatabase().getDialect(), + entityBinding.getRootClass(), + idProperty, + new GeneratorSettingsImpl( domainModelScope.getDomainModel() ) + ); assertThat( generator ).isInstanceOf( NativeGenerator.class ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java index 3c291680a866..b2731a6bade2 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/local/NativeGeneratorMemberTest.java @@ -8,6 +8,9 @@ import org.hibernate.generator.Generator; import org.hibernate.id.NativeGenerator; import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModelScope; @@ -27,8 +30,16 @@ public class NativeGeneratorMemberTest { @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { scope.inTransaction(s -> s.persist(new NativeEntity())); - final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); - final Generator generator = identifier.createGenerator( null, null, null ); + final PersistentClass entityBinding = domainModelScope.getEntityBinding( NativeEntity.class ); + final Property idProperty = entityBinding.getIdentifierProperty(); + final KeyValue identifier = entityBinding.getIdentifier(); + + final Generator generator = identifier.createGenerator( + domainModelScope.getDomainModel().getDatabase().getDialect(), + entityBinding.getRootClass(), + idProperty, + new GeneratorSettingsImpl( domainModelScope.getDomainModel() ) + ); assertThat( generator ).isInstanceOf( NativeGenerator.class ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java index da727961b57c..78d9ef14794a 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/n_ative/pkg/NativeGeneratorPackageTest.java @@ -8,6 +8,9 @@ import org.hibernate.generator.Generator; import org.hibernate.id.NativeGenerator; import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.idgen.n_ative.GeneratorSettingsImpl; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.DomainModelScope; @@ -27,8 +30,15 @@ public class NativeGeneratorPackageTest { @Test void test(DomainModelScope domainModelScope, SessionFactoryScope scope) { scope.inTransaction(s -> s.persist(new NativeEntity())); - final KeyValue identifier = domainModelScope.getEntityBinding( NativeEntity.class ).getIdentifier(); - final Generator generator = identifier.createGenerator( null, null, null ); + final PersistentClass entityBinding = domainModelScope.getEntityBinding( NativeEntity.class ); + final Property idProperty = entityBinding.getIdentifierProperty(); + final KeyValue identifier = entityBinding.getIdentifier(); + final Generator generator = identifier.createGenerator( + domainModelScope.getDomainModel().getDatabase().getDialect(), + entityBinding.getRootClass(), + idProperty, + new GeneratorSettingsImpl( domainModelScope.getDomainModel() ) + ); assertThat( generator ).isInstanceOf( NativeGenerator.class ); } From c979b9cb0a580abad96a213340ca6d856ac5fc87 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Sat, 21 Sep 2024 15:22:00 -0500 Subject: [PATCH 3/4] HHH-18620 - Add @NativeGenerator --- .../model/internal/GeneratorAnnotationHelper.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java index facfcab2c159..3519739b9ef5 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java @@ -305,6 +305,11 @@ public static void prepareForUse( Consumer configBaseline, BiConsumer configExtractor, GeneratorCreationContext creationContext) { + if ( generator instanceof AnnotationBasedGenerator ) { + @SuppressWarnings("unchecked") + final AnnotationBasedGenerator generation = (AnnotationBasedGenerator) generator; + generation.initialize( annotation, idMember.toJavaMember(), creationContext ); + } if ( generator instanceof Configurable configurable ) { final Properties properties = new Properties(); if ( configBaseline != null ) { @@ -323,14 +328,6 @@ public static void prepareForUse( } configurable.configure( creationContext, properties ); } - if ( generator instanceof AnnotationBasedGenerator ) { - // This will cause a CCE in case the generation type doesn't match the annotation type; As this would be - // a programming error of the generation type developer and thus should show up during testing, we don't - // check this explicitly; If required, this could be done e.g. using ClassMate - @SuppressWarnings("unchecked") - final AnnotationBasedGenerator generation = (AnnotationBasedGenerator) generator; - generation.initialize( annotation, idMember.toJavaMember(), creationContext ); - } if ( generator instanceof ExportableProducer exportableProducer ) { exportableProducer.registerExportables( creationContext.getDatabase() ); } From a052c26dcd19e76db61c35026096c58aebe95c4b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Mon, 23 Sep 2024 09:43:27 -0500 Subject: [PATCH 4/4] HHH-18620 - Add @NativeGenerator --- .../AbstractEntityIdGeneratorResolver.java | 198 ++++++++++++++++ .../internal/GeneratorAnnotationHelper.java | 209 +++++++++-------- .../boot/model/internal/GeneratorBinder.java | 1 + .../IdGeneratorResolverSecondPass.java | 216 ++++-------------- .../StrictIdGeneratorResolverSecondPass.java | 181 +++------------ 5 files changed, 394 insertions(+), 411 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractEntityIdGeneratorResolver.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractEntityIdGeneratorResolver.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractEntityIdGeneratorResolver.java new file mode 100644 index 000000000000..77d11e05d5f6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AbstractEntityIdGeneratorResolver.java @@ -0,0 +1,198 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.boot.model.internal; + +import java.lang.annotation.Annotation; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.MappingException; +import org.hibernate.annotations.IdGeneratorType; +import org.hibernate.boot.model.IdentifierGeneratorDefinition; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.dialect.Dialect; +import org.hibernate.generator.Generator; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.models.spi.AnnotationTarget; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.MemberDetails; + +import jakarta.persistence.GeneratedValue; + +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleIdGeneratorType; +import static org.hibernate.boot.model.internal.GeneratorParameters.identityTablesString; +import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator; +import static org.hibernate.id.IdentifierGenerator.ENTITY_NAME; +import static org.hibernate.id.IdentifierGenerator.JPA_ENTITY_NAME; +import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE; +import static org.hibernate.id.PersistentIdentifierGenerator.PK; +import static org.hibernate.id.PersistentIdentifierGenerator.TABLE; +import static org.hibernate.id.PersistentIdentifierGenerator.TABLES; + +/** + * Template support for IdGeneratorResolver implementations dealing with entity identifiers + * + * @author Steve Ebersole + */ +public abstract class AbstractEntityIdGeneratorResolver implements IdGeneratorResolver { + protected final PersistentClass entityMapping; + protected final SimpleValue idValue; + protected final MemberDetails idMember; + protected final GeneratedValue generatedValue; + protected final MetadataBuildingContext buildingContext; + + public AbstractEntityIdGeneratorResolver( + PersistentClass entityMapping, + SimpleValue idValue, + MemberDetails idMember, + GeneratedValue generatedValue, + MetadataBuildingContext buildingContext) { + this.entityMapping = entityMapping; + this.idValue = idValue; + this.idMember = idMember; + this.generatedValue = generatedValue; + this.buildingContext = buildingContext; + } + + @Override + public final void doSecondPass(Map persistentClasses) throws MappingException { + switch ( generatedValue.strategy() ) { + case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext ); + case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue ); + case SEQUENCE -> handleSequenceStrategy(); + case TABLE -> handleTableStrategy(); + case AUTO -> handleAutoStrategy(); + } + } + + private void handleSequenceStrategy() { + if ( generatedValue.generator().isEmpty() ) { + handleUnnamedSequenceGenerator(); + } + else { + handleNamedSequenceGenerator(); + } + } + + protected abstract void handleUnnamedSequenceGenerator(); + + protected abstract void handleNamedSequenceGenerator(); + + private void handleTableStrategy() { + if ( generatedValue.generator().isEmpty() ) { + handleUnnamedTableGenerator(); + } + else { + handleNamedTableGenerator(); + } + } + + protected abstract void handleUnnamedTableGenerator(); + + protected abstract void handleNamedTableGenerator(); + + private void handleAutoStrategy() { + if ( generatedValue.generator().isEmpty() ) { + handleUnnamedAutoGenerator(); + } + else { + handleNamedAutoGenerator(); + } + } + + protected abstract void handleUnnamedAutoGenerator(); + + protected abstract void handleNamedAutoGenerator(); + + protected boolean handleAsMetaAnnotated() { + final Annotation fromMember = findGeneratorAnnotation( idMember ); + if ( fromMember != null ) { + handleIdGeneratorType( fromMember, idValue, idMember, buildingContext ); + return true; + } + + final Annotation fromClass = findGeneratorAnnotation( idMember.getDeclaringType() ); + if ( fromClass != null ) { + handleIdGeneratorType( fromClass, idValue, idMember, buildingContext ); + return true; + } + + final ClassDetails packageInfoDetails = GeneratorAnnotationHelper.locatePackageInfoDetails( idMember.getDeclaringType(), buildingContext ); + if ( packageInfoDetails != null ) { + final Annotation fromPackage = findGeneratorAnnotation( packageInfoDetails ); + if ( fromPackage != null ) { + handleIdGeneratorType( fromPackage, idValue, idMember, buildingContext ); + return true; + } + } + + return false; + } + + private Annotation findGeneratorAnnotation(AnnotationTarget annotationTarget) { + final List metaAnnotated = annotationTarget.getMetaAnnotated( IdGeneratorType.class, buildingContext.getMetadataCollector().getSourceModelBuildingContext() ); + if ( CollectionHelper.size( metaAnnotated ) > 0 ) { + return metaAnnotated.get( 0 ); + } + + return null; + } + + protected boolean handleAsLegacyGenerator() { + // Handle a few legacy Hibernate generators... + final String nameFromGeneratedValue = generatedValue.generator(); + if ( !nameFromGeneratedValue.isEmpty() ) { + final Class legacyNamedGenerator = mapLegacyNamedGenerator( nameFromGeneratedValue, idValue ); + if ( legacyNamedGenerator != null ) { + final Map configuration = buildLegacyGeneratorConfig(); + //noinspection unchecked,rawtypes + GeneratorBinder.createGeneratorFrom( + new IdentifierGeneratorDefinition( nameFromGeneratedValue, legacyNamedGenerator.getName(), configuration ), + idValue, + (Map) configuration, + buildingContext + ); + return true; + } + } + + return false; + } + + private HashMap buildLegacyGeneratorConfig() { + final Database database = buildingContext.getMetadataCollector().getDatabase(); + final Dialect dialect = database.getDialect(); + + final HashMap configuration = new HashMap<>(); + + final String tableName = idValue.getTable().getQuotedName( dialect ); + configuration.put( TABLE, tableName ); + + final Column idColumn = (Column) idValue.getSelectables().get( 0); + final String idColumnName = idColumn.getQuotedName( dialect ); + configuration.put( PK, idColumnName ); + + configuration.put( ENTITY_NAME, entityMapping.getEntityName() ); + configuration.put( JPA_ENTITY_NAME, entityMapping.getJpaEntityName() ); + + // The table name is not really a good default for subselect entities, + // so use the JPA entity name which is short + configuration.put( + IMPLICIT_NAME_BASE, + idValue.getTable().isSubselect() + ? entityMapping.getJpaEntityName() + : idValue.getTable().getName() + ); + + configuration.put( TABLES, identityTablesString( dialect, entityMapping.getRootClass() ) ); + + return configuration; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java index 3519739b9ef5..8a47eba3477f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorAnnotationHelper.java @@ -14,6 +14,7 @@ import java.util.function.Function; import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.IdGeneratorType; import org.hibernate.annotations.Parameter; import org.hibernate.boot.model.IdentifierGeneratorDefinition; import org.hibernate.boot.model.relational.ExportableProducer; @@ -27,10 +28,10 @@ import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentityGenerator; import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.uuid.UuidGenerator; import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.PersistentClass; -import org.hibernate.mapping.RootClass; import org.hibernate.mapping.SimpleValue; import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.ClassDetails; @@ -149,101 +150,39 @@ public static ClassDetails locatePackageInfoDetails(ClassDetails classDetails, C } } - public static void handleUuidStrategy( + public static void handleSequenceGenerator( + String nameFromGeneratedValue, + SequenceGenerator generatorAnnotation, SimpleValue idValue, MemberDetails idMember, - MetadataBuildingContext context) { - final org.hibernate.annotations.UuidGenerator generatorConfig = findLocalizedMatch( - HibernateAnnotations.UUID_GENERATOR, - idMember, - null, - null, - context - ); - idValue.setCustomIdGeneratorCreator( (creationContext) -> new UuidGenerator( generatorConfig, idMember ) ); - } - - public static void handleIdentityStrategy(SimpleValue idValue) { - idValue.setCustomIdGeneratorCreator( (creationContext) -> new IdentityGenerator() ); - idValue.setColumnToIdentity(); - } - - public static void applyBaselineConfiguration( - SequenceGenerator generatorConfig, - SimpleValue idValue, - RootClass rootClass, - MetadataBuildingContext context, - BiConsumer configurationCollector) { - if ( generatorConfig != null && !generatorConfig.name().isEmpty() ) { - configurationCollector.accept( GENERATOR_NAME, generatorConfig.name() ); - } - - GeneratorParameters.collectParameters( - idValue, - context.getMetadataCollector().getDatabase().getDialect(), - rootClass, - configurationCollector - ); - - } - - static void applyBaselineConfiguration( - TableGenerator generatorConfig, - SimpleValue idValue, - RootClass rootClass, - MetadataBuildingContext context, - BiConsumer configurationCollector) { - if ( !generatorConfig.name().isEmpty() ) { - configurationCollector.accept( GENERATOR_NAME, generatorConfig.name() ); - } - - GeneratorParameters.collectParameters( - idValue, - context.getMetadataCollector().getDatabase().getDialect(), - rootClass, - configurationCollector - ); - - } - - public static void handleGenericGenerator( - String generatorName, - GenericGenerator generatorConfig, - PersistentClass entityMapping, - SimpleValue idValue, - MetadataBuildingContext context) { - //generator settings - final Map configuration = new HashMap<>(); - setIfNotEmpty( generatorConfig.name(), IdentifierGenerator.GENERATOR_NAME, configuration ); - configuration.put( IdentifierGenerator.ENTITY_NAME, entityMapping.getEntityName() ); - configuration.put( IdentifierGenerator.JPA_ENTITY_NAME, entityMapping.getJpaEntityName() ); - - applyAnnotationParameters( generatorConfig, configuration ); - - configuration.put( PersistentIdentifierGenerator.TABLE, idValue.getTable().getName() ); - if ( idValue.getColumnSpan() == 1 ) { - configuration.put( PersistentIdentifierGenerator.PK, idValue.getColumns().get(0).getName() ); - } - - GeneratorBinder.createGeneratorFrom( - new IdentifierGeneratorDefinition( generatorName, determineStrategyName( generatorConfig ), configuration ), - idValue, - context - ); - } - - private static String determineStrategyName(GenericGenerator generatorConfig) { - final Class type = generatorConfig.type(); - if ( !Objects.equals( type, Generator.class ) ) { - return type.getName(); - } - return generatorConfig.strategy(); - } - - private static void applyAnnotationParameters(GenericGenerator generatorConfig, Map configuration) { - for ( Parameter parameter : generatorConfig.parameters() ) { - configuration.put( parameter.name(), parameter.value() ); - } + MetadataBuildingContext buildingContext) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + SequenceStyleGenerator.class + ); + prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + properties -> { + if ( generatorAnnotation != null ) { + properties.put( GENERATOR_NAME, generatorAnnotation.name() ); + } + else if ( nameFromGeneratedValue != null ) { + properties.put( GENERATOR_NAME, nameFromGeneratedValue ); + } + // we need to better handle default allocation-size here... + properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); + }, + generatorAnnotation == null + ? null + : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), + creationContext + ); + return identifierGenerator; + } ); } public static void handleTableGenerator( @@ -288,6 +227,30 @@ else if ( nameFromGeneratedValue != null ) { } ); } + public static void handleIdGeneratorType( + Annotation generatorAnnotation, + SimpleValue idValue, + MemberDetails idMember, + MetadataBuildingContext buildingContext) { + final IdGeneratorType markerAnnotation = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class ); + idValue.setCustomIdGeneratorCreator( (creationContext) -> { + final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); + final Generator identifierGenerator = GeneratorBinder.instantiateGenerator( + beanContainer, + markerAnnotation.value() + ); + GeneratorAnnotationHelper.prepareForUse( + identifierGenerator, + generatorAnnotation, + idMember, + null, + null, + creationContext + ); + return identifierGenerator; + } ); + } + /** * Prepares a generator for use by handling its various potential means of "configuration". * @@ -334,6 +297,64 @@ public static void prepareForUse( if ( generator instanceof Configurable configurable ) { configurable.initialize( creationContext.getSqlStringGenerationContext() ); } + } + public static void handleUuidStrategy( + SimpleValue idValue, + MemberDetails idMember, + MetadataBuildingContext context) { + final org.hibernate.annotations.UuidGenerator generatorConfig = findLocalizedMatch( + HibernateAnnotations.UUID_GENERATOR, + idMember, + null, + null, + context + ); + idValue.setCustomIdGeneratorCreator( (creationContext) -> new UuidGenerator( generatorConfig, idMember ) ); + } + + public static void handleIdentityStrategy(SimpleValue idValue) { + idValue.setCustomIdGeneratorCreator( (creationContext) -> new IdentityGenerator() ); + idValue.setColumnToIdentity(); + } + + public static void handleGenericGenerator( + String generatorName, + GenericGenerator generatorConfig, + PersistentClass entityMapping, + SimpleValue idValue, + MetadataBuildingContext context) { + //generator settings + final Map configuration = new HashMap<>(); + setIfNotEmpty( generatorConfig.name(), IdentifierGenerator.GENERATOR_NAME, configuration ); + configuration.put( IdentifierGenerator.ENTITY_NAME, entityMapping.getEntityName() ); + configuration.put( IdentifierGenerator.JPA_ENTITY_NAME, entityMapping.getJpaEntityName() ); + + applyAnnotationParameters( generatorConfig, configuration ); + + configuration.put( PersistentIdentifierGenerator.TABLE, idValue.getTable().getName() ); + if ( idValue.getColumnSpan() == 1 ) { + configuration.put( PersistentIdentifierGenerator.PK, idValue.getColumns().get(0).getName() ); + } + + GeneratorBinder.createGeneratorFrom( + new IdentifierGeneratorDefinition( generatorName, determineStrategyName( generatorConfig ), configuration ), + idValue, + context + ); + } + + private static String determineStrategyName(GenericGenerator generatorConfig) { + final Class type = generatorConfig.type(); + if ( !Objects.equals( type, Generator.class ) ) { + return type.getName(); + } + return generatorConfig.strategy(); + } + + private static void applyAnnotationParameters(GenericGenerator generatorConfig, Map configuration) { + for ( Parameter parameter : generatorConfig.parameters() ) { + configuration.put( parameter.name(), parameter.value() ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java index a9d4946cd83c..7aa761945e02 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/GeneratorBinder.java @@ -725,6 +725,7 @@ private static void createIdGenerator( persistentClass, idValue, idMember, + generatedValue, context ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java index d0f7a4a70f95..bd4964d56be9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/IdGeneratorResolverSecondPass.java @@ -4,15 +4,11 @@ */ package org.hibernate.boot.model.internal; -import java.lang.annotation.Annotation; -import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.UUID; import org.hibernate.MappingException; import org.hibernate.annotations.GenericGenerator; -import org.hibernate.annotations.IdGeneratorType; import org.hibernate.boot.model.IdentifierGeneratorDefinition; import org.hibernate.boot.models.HibernateAnnotations; import org.hibernate.boot.models.JpaAnnotations; @@ -22,23 +18,17 @@ import org.hibernate.boot.models.spi.TableGeneratorRegistration; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.generator.Generator; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; -import org.hibernate.models.spi.AnnotationTarget; -import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.MemberDetails; -import org.hibernate.resource.beans.container.spi.BeanContainer; import jakarta.persistence.GeneratedValue; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.TableGenerator; -import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.findLocalizedMatch; +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleSequenceGenerator; import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator; -import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; -import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; /** * SecondPass implementing delayed resolution of id-generators associated with an entity. @@ -47,50 +37,25 @@ * * @author Steve Ebersole */ -public class IdGeneratorResolverSecondPass implements IdGeneratorResolver { - private final PersistentClass entityMapping; - private final SimpleValue idValue; - private final MemberDetails idMember; - private final GeneratedValue generatedValue; - private final MetadataBuildingContext buildingContext; - +public class IdGeneratorResolverSecondPass extends AbstractEntityIdGeneratorResolver { public IdGeneratorResolverSecondPass( PersistentClass entityMapping, SimpleValue idValue, MemberDetails idMember, GeneratedValue generatedValue, MetadataBuildingContext buildingContext) { - this.entityMapping = entityMapping; - this.idValue = idValue; - this.idMember = idMember; - this.generatedValue = generatedValue; - this.buildingContext = buildingContext; + super( entityMapping, idValue, idMember, generatedValue, buildingContext ); } - @Override - public void doSecondPass(Map persistentClasses) throws MappingException { - switch ( generatedValue.strategy() ) { - case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext ); - case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue ); - case SEQUENCE -> handleSequenceStrategy(); - case TABLE -> handleTableStrategy(); - case AUTO -> handleAutoStrategy(); - } - } - private void handleSequenceStrategy() { - if ( generatedValue.generator().isEmpty() ) { - handleUnnamedSequenceGenerator(); - } - else { - handleNamedSequenceGenerator(); - } - } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // SEQUENCE - private void handleUnnamedSequenceGenerator() { + @Override + protected void handleUnnamedSequenceGenerator() { // todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"? - final SequenceGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final SequenceGenerator localizedMatch = findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, idMember, null, @@ -98,17 +63,18 @@ private void handleUnnamedSequenceGenerator() { buildingContext ); if ( localizedMatch != null ) { - handleSequenceGenerator( null, localizedMatch ); + handleSequenceGenerator( null, localizedMatch, idValue, idMember, buildingContext ); return; } - handleSequenceGenerator( null, null ); + handleSequenceGenerator( null, null, idValue, idMember, buildingContext ); } - private void handleNamedSequenceGenerator() { + @Override + protected void handleNamedSequenceGenerator() { final String generator = generatedValue.generator(); - final SequenceGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final SequenceGenerator localizedMatch = findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, idMember, SequenceGenerator::name, @@ -116,7 +82,7 @@ private void handleNamedSequenceGenerator() { buildingContext ); if ( localizedMatch != null ) { - handleSequenceGenerator( generator, localizedMatch ); + handleSequenceGenerator( generator, localizedMatch, idValue, idMember, buildingContext ); return; } @@ -127,13 +93,13 @@ private void handleNamedSequenceGenerator() { .getSequenceGeneratorRegistrations() .get( generator ); if ( globalMatch != null ) { - handleSequenceGenerator( generator, globalMatch.configuration() ); + handleSequenceGenerator( generator, globalMatch.configuration(), idValue, idMember, buildingContext ); return; } validateSequenceGeneration(); - handleSequenceGenerator( generator, null ); + handleSequenceGenerator( generator, null, idValue, idMember, buildingContext ); } private void validateSequenceGeneration() { @@ -170,19 +136,15 @@ private void validateSequenceGeneration() { } } - private void handleTableStrategy() { - if ( generatedValue.generator().isEmpty() ) { - handleUnnamedTableGenerator(); - } - else { - handleNamedTableGenerator(); - } - } - private void handleUnnamedTableGenerator() { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // TABLE + + @Override + protected void handleUnnamedTableGenerator() { // todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"? - final TableGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final TableGenerator localizedMatch = findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, idMember, null, @@ -192,10 +154,11 @@ private void handleUnnamedTableGenerator() { handleTableGenerator( null, localizedMatch ); } - private void handleNamedTableGenerator() { + @Override + protected void handleNamedTableGenerator() { final String generator = generatedValue.generator(); - final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final TableGenerator localizedTableMatch = findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, idMember, TableGenerator::name, @@ -255,19 +218,15 @@ private void validateTableGeneration() { } } - private void handleAutoStrategy() { - if ( generatedValue.generator().isEmpty() ) { - handleUnnamedAutoGenerator(); - } - else { - handleNamedAutoGenerator(); - } - } - private void handleUnnamedAutoGenerator() { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // AUTO + + @Override + protected void handleUnnamedAutoGenerator() { // todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"? - final SequenceGenerator localizedSequenceMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final SequenceGenerator localizedSequenceMatch = findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, idMember, null, @@ -275,11 +234,11 @@ private void handleUnnamedAutoGenerator() { buildingContext ); if ( localizedSequenceMatch != null ) { - handleSequenceGenerator( null, localizedSequenceMatch ); + handleSequenceGenerator( null, localizedSequenceMatch, idValue, idMember, buildingContext ); return; } - final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final TableGenerator localizedTableMatch = findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, idMember, null, @@ -291,7 +250,7 @@ private void handleUnnamedAutoGenerator() { return; } - final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final GenericGenerator localizedGenericMatch = findLocalizedMatch( HibernateAnnotations.GENERIC_GENERATOR, idMember, null, @@ -319,64 +278,15 @@ private void handleUnnamedAutoGenerator() { return; } - handleSequenceGenerator( null, null ); - } - - private boolean handleAsMetaAnnotated() { - final Annotation fromMember = findGeneratorAnnotation( idMember ); - if ( fromMember != null ) { - handleIdGeneratorType( fromMember ); - return true; - } - - final Annotation fromClass = findGeneratorAnnotation( idMember.getDeclaringType() ); - if ( fromClass != null ) { - handleIdGeneratorType( fromClass ); - return true; - } - - final ClassDetails packageInfoDetails = GeneratorAnnotationHelper.locatePackageInfoDetails( idMember.getDeclaringType(), buildingContext ); - if ( packageInfoDetails != null ) { - final Annotation fromPackage = findGeneratorAnnotation( packageInfoDetails ); - if ( fromPackage != null ) { - handleIdGeneratorType( fromPackage ); - return true; - } - } - - return false; - } - - private Annotation findGeneratorAnnotation(AnnotationTarget annotationTarget) { - final List metaAnnotated = annotationTarget.getMetaAnnotated( IdGeneratorType.class, buildingContext.getMetadataCollector().getSourceModelBuildingContext() ); - if ( CollectionHelper.size( metaAnnotated ) > 0 ) { - return metaAnnotated.get( 0 ); + if ( handleAsLegacyGenerator() ) { + return; } - return null; + handleSequenceGenerator( null, null, idValue, idMember, buildingContext ); } - private void handleIdGeneratorType(Annotation generatorAnnotation) { - final IdGeneratorType markerAnnotation = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class ); - idValue.setCustomIdGeneratorCreator( (creationContext) -> { - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); - final Generator identifierGenerator = GeneratorBinder.instantiateGenerator( - beanContainer, - markerAnnotation.value() - ); - GeneratorAnnotationHelper.prepareForUse( - identifierGenerator, - generatorAnnotation, - idMember, - null, - null, - creationContext - ); - return identifierGenerator; - } ); - } - - private void handleNamedAutoGenerator() { + @Override + protected void handleNamedAutoGenerator() { if ( handleAsLocalAutoGenerator() ) { return; } @@ -407,14 +317,18 @@ private void handleNamedAutoGenerator() { return; } - handleSequenceGenerator(generator, null ); + if ( handleAsLegacyGenerator() ) { + return; + } + + handleSequenceGenerator( generator, null, idValue, idMember, buildingContext ); } private boolean handleAsLocalAutoGenerator() { final String generator = generatedValue.generator(); assert !generator.isEmpty(); - final SequenceGenerator localizedSequenceMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final SequenceGenerator localizedSequenceMatch = findLocalizedMatch( JpaAnnotations.SEQUENCE_GENERATOR, idMember, SequenceGenerator::name, @@ -422,11 +336,11 @@ private boolean handleAsLocalAutoGenerator() { buildingContext ); if ( localizedSequenceMatch != null ) { - handleSequenceGenerator( generator, localizedSequenceMatch ); + handleSequenceGenerator( generator, localizedSequenceMatch, idValue, idMember, buildingContext ); return true; } - final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final TableGenerator localizedTableMatch = findLocalizedMatch( JpaAnnotations.TABLE_GENERATOR, idMember, TableGenerator::name, @@ -438,7 +352,7 @@ private boolean handleAsLocalAutoGenerator() { return true; } - final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch( + final GenericGenerator localizedGenericMatch = findLocalizedMatch( HibernateAnnotations.GENERIC_GENERATOR, idMember, GenericGenerator::name, @@ -467,7 +381,7 @@ private boolean handleAsNamedGlobalAutoGenerator() { final SequenceGeneratorRegistration globalSequenceMatch = globalRegistrations.getSequenceGeneratorRegistrations().get( generator ); if ( globalSequenceMatch != null ) { - handleSequenceGenerator( generator, globalSequenceMatch.configuration() ); + handleSequenceGenerator( generator, globalSequenceMatch.configuration(), idValue, idMember, buildingContext ); return true; } @@ -494,36 +408,6 @@ private boolean handleAsNamedGlobalAutoGenerator() { return false; } - private void handleSequenceGenerator(String nameFromGeneratedValue, SequenceGenerator generatorAnnotation) { - idValue.setCustomIdGeneratorCreator( (creationContext) -> { - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); - final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( - beanContainer, - SequenceStyleGenerator.class - ); - GeneratorAnnotationHelper.prepareForUse( - identifierGenerator, - generatorAnnotation, - idMember, - properties -> { - if ( generatorAnnotation != null ) { - properties.put( GENERATOR_NAME, generatorAnnotation.name() ); - } - else if ( nameFromGeneratedValue != null ) { - properties.put( GENERATOR_NAME, nameFromGeneratedValue ); - } - // we need to better handle default allocation-size here... - properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); - }, - generatorAnnotation == null - ? null - : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), - creationContext - ); - return identifierGenerator; - } ); - } - private void handleTableGenerator(String nameFromGeneratedValue, TableGenerator generatorAnnotation) { GeneratorAnnotationHelper.handleTableGenerator( nameFromGeneratedValue, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java index d27bf34a049b..b8c046b82a35 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/StrictIdGeneratorResolverSecondPass.java @@ -4,13 +4,8 @@ */ package org.hibernate.boot.model.internal; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; -import org.hibernate.MappingException; -import org.hibernate.boot.model.IdentifierGeneratorDefinition; -import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.models.annotations.internal.SequenceGeneratorJpaAnnotation; import org.hibernate.boot.models.annotations.internal.TableGeneratorJpaAnnotation; import org.hibernate.boot.models.spi.GenericGeneratorRegistration; @@ -19,31 +14,16 @@ import org.hibernate.boot.models.spi.TableGeneratorRegistration; import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; -import org.hibernate.dialect.Dialect; -import org.hibernate.generator.Generator; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.mapping.Column; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.SimpleValue; import org.hibernate.models.spi.MemberDetails; -import org.hibernate.resource.beans.container.spi.BeanContainer; import jakarta.persistence.GeneratedValue; -import jakarta.persistence.SequenceGenerator; -import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.*; +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleGenericGenerator; +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleSequenceGenerator; +import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleTableGenerator; import static org.hibernate.boot.model.internal.GeneratorAnnotationHelper.handleUuidStrategy; -import static org.hibernate.boot.model.internal.GeneratorParameters.fallbackAllocationSize; -import static org.hibernate.boot.model.internal.GeneratorParameters.identityTablesString; -import static org.hibernate.boot.model.internal.GeneratorStrategies.mapLegacyNamedGenerator; -import static org.hibernate.id.IdentifierGenerator.ENTITY_NAME; -import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME; -import static org.hibernate.id.IdentifierGenerator.JPA_ENTITY_NAME; -import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE; -import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM; -import static org.hibernate.id.PersistentIdentifierGenerator.PK; -import static org.hibernate.id.PersistentIdentifierGenerator.TABLE; -import static org.hibernate.id.PersistentIdentifierGenerator.TABLES; /** * SecondPass implementing delayed resolution of id-generators associated with an entity @@ -60,46 +40,18 @@ * * @author Steve Ebersole */ -public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver { - private final PersistentClass entityMapping; - private final SimpleValue idValue; - private final MemberDetails idMember; - - private final MetadataBuildingContext buildingContext; - +public class StrictIdGeneratorResolverSecondPass extends AbstractEntityIdGeneratorResolver { public StrictIdGeneratorResolverSecondPass( PersistentClass entityMapping, SimpleValue idValue, MemberDetails idMember, + GeneratedValue generatedValue, MetadataBuildingContext buildingContext) { - this.entityMapping = entityMapping; - this.idValue = idValue; - this.idMember = idMember; - this.buildingContext = buildingContext; + super( entityMapping, idValue, idMember, generatedValue, buildingContext ); } @Override - public void doSecondPass(Map persistentClasses) throws MappingException { - final GeneratedValue generatedValue = idMember.getDirectAnnotationUsage( GeneratedValue.class ); - switch ( generatedValue.strategy() ) { - case UUID -> handleUuidStrategy( idValue, idMember, buildingContext ); - case IDENTITY -> handleIdentityStrategy( idValue ); - case SEQUENCE -> handleSequenceStrategy( generatedValue ); - case TABLE -> handleTableStrategy( generatedValue ); - case AUTO -> handleAutoStrategy( generatedValue ); - } - } - - private void handleSequenceStrategy(GeneratedValue generatedValue) { - if ( generatedValue.generator().isEmpty() ) { - handleUnnamedSequenceGenerator(); - } - else { - handleNamedSequenceGenerator( generatedValue ); - } - } - - private void handleUnnamedSequenceGenerator() { + protected void handleUnnamedSequenceGenerator() { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); // according to the spec, this should locate a generator with the same name as the entity-name @@ -126,7 +78,8 @@ private void handleUnnamedSequenceGenerator() { ); } - private void handleNamedSequenceGenerator(GeneratedValue generatedValue) { + @Override + protected void handleNamedSequenceGenerator() { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final SequenceGeneratorRegistration globalMatch = @@ -152,16 +105,8 @@ private void handleNamedSequenceGenerator(GeneratedValue generatedValue) { ); } - private void handleTableStrategy(GeneratedValue generatedValue) { - if ( generatedValue.generator().isEmpty() ) { - handleUnnamedTableGenerator(); - } - else { - handleNamedTableGenerator( generatedValue ); - } - } - - private void handleUnnamedTableGenerator() { + @Override + protected void handleUnnamedTableGenerator() { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final TableGeneratorRegistration globalMatch = @@ -189,7 +134,8 @@ private void handleUnnamedTableGenerator() { ); } - private void handleNamedTableGenerator(GeneratedValue generatedValue) { + @Override + protected void handleNamedTableGenerator() { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final TableGeneratorRegistration globalMatch = @@ -218,10 +164,17 @@ private void handleNamedTableGenerator(GeneratedValue generatedValue) { ); } - private void handleAutoStrategy(GeneratedValue generatedValue) { - final String generator = generatedValue.generator(); - final String globalRegistrationName = generator.isEmpty() ? entityMapping.getJpaEntityName() : generator; + @Override + protected void handleUnnamedAutoGenerator() { + handleAutoGenerator( entityMapping.getJpaEntityName() ); + } + @Override + protected void handleNamedAutoGenerator() { + handleAutoGenerator( generatedValue.generator() ); + } + + private void handleAutoGenerator(String globalRegistrationName) { final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); final GlobalRegistrations globalRegistrations = metadataCollector.getGlobalRegistrations(); @@ -265,6 +218,10 @@ private void handleAutoStrategy(GeneratedValue generatedValue) { return; } + if ( handleAsMetaAnnotated() ) { + return; + } + // Implicit handling of UUID generation if ( idMember.getType().isImplementor( UUID.class ) || idMember.getType().isImplementor( String.class ) ) { @@ -272,94 +229,16 @@ private void handleAutoStrategy(GeneratedValue generatedValue) { return; } - - // Handle a few legacy Hibernate generators... - if ( !generator.isEmpty() ) { - final Class legacyNamedGenerator = mapLegacyNamedGenerator( generator, idValue ); - if ( legacyNamedGenerator != null ) { - final Map configuration = buildLegacyGeneratorConfig(); - //noinspection unchecked,rawtypes - GeneratorBinder.createGeneratorFrom( - new IdentifierGeneratorDefinition( generator, legacyNamedGenerator.getName(), configuration ), - idValue, - (Map) configuration, - buildingContext - ); - return; - } + if ( handleAsLegacyGenerator() ) { + return; } handleSequenceGenerator( globalRegistrationName, - new SequenceGeneratorJpaAnnotation( generator, metadataCollector.getSourceModelBuildingContext() ), + new SequenceGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ), idValue, idMember, buildingContext ); } - - private HashMap buildLegacyGeneratorConfig() { - final Database database = buildingContext.getMetadataCollector().getDatabase(); - final Dialect dialect = database.getDialect(); - - final HashMap configuration = new HashMap<>(); - - final String tableName = idValue.getTable().getQuotedName( dialect ); - configuration.put( TABLE, tableName ); - - final Column idColumn = (Column) idValue.getSelectables().get( 0); - final String idColumnName = idColumn.getQuotedName( dialect ); - configuration.put( PK, idColumnName ); - - configuration.put( ENTITY_NAME, entityMapping.getEntityName() ); - configuration.put( JPA_ENTITY_NAME, entityMapping.getJpaEntityName() ); - - // The table name is not really a good default for subselect entities, - // so use the JPA entity name which is short - configuration.put( - IMPLICIT_NAME_BASE, - idValue.getTable().isSubselect() - ? entityMapping.getJpaEntityName() - : idValue.getTable().getName() - ); - - configuration.put( TABLES, identityTablesString( dialect, entityMapping.getRootClass() ) ); - - return configuration; - } - - public static void handleSequenceGenerator( - String nameFromGeneratedValue, - SequenceGenerator generatorAnnotation, - SimpleValue idValue, - MemberDetails idMember, - MetadataBuildingContext buildingContext) { - idValue.setCustomIdGeneratorCreator( (creationContext) -> { - final BeanContainer beanContainer = GeneratorBinder.beanContainer( buildingContext ); - final SequenceStyleGenerator identifierGenerator = GeneratorBinder.instantiateGenerator( - beanContainer, - SequenceStyleGenerator.class - ); - GeneratorAnnotationHelper.prepareForUse( - identifierGenerator, - generatorAnnotation, - idMember, - properties -> { - if ( generatorAnnotation != null ) { - properties.put( GENERATOR_NAME, generatorAnnotation.name() ); - } - else if ( nameFromGeneratedValue != null ) { - properties.put( GENERATOR_NAME, nameFromGeneratedValue ); - } - // we need to better handle default allocation-size here... - properties.put( INCREMENT_PARAM, fallbackAllocationSize( generatorAnnotation, buildingContext ) ); - }, - generatorAnnotation == null - ? null - : (a, properties) -> SequenceStyleGenerator.applyConfiguration( generatorAnnotation, properties::put ), - creationContext - ); - return identifierGenerator; - } ); - } }