HHH-18620 - Add @NativeGenerator

This commit is contained in:
Steve Ebersole 2024-09-19 06:36:10 -05:00
parent fccbb8d582
commit df405d37ab
29 changed files with 1135 additions and 660 deletions

View File

@ -51,6 +51,7 @@ import org.hibernate.type.StandardBasicTypes;
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 class CacheDialect extends Dialect {
return true;
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "identity";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.IDENTITY;
}
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -75,6 +75,7 @@ import org.hibernate.type.spi.TypeConfiguration;
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 class CockroachLegacyDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
@Override

View File

@ -100,6 +100,7 @@ import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
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;
@ -962,8 +963,8 @@ public class OracleLegacyDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
// features which change between 8i, 9i, and 10g ~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -93,6 +93,7 @@ import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
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;
@ -894,8 +895,8 @@ public class PostgreSQLLegacyDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
@Override

View File

@ -0,0 +1,35 @@
/*
* 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.Incubating;
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)
@Incubating
public @interface NativeGenerator {
SequenceGenerator sequenceForm() default @SequenceGenerator();
TableGenerator tableForm() default @TableGenerator();
}

View File

@ -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<String, PersistentClass> 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<? extends Annotation> 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<? extends Generator> legacyNamedGenerator = mapLegacyNamedGenerator( nameFromGeneratedValue, idValue );
if ( legacyNamedGenerator != null ) {
final Map<String,String> configuration = buildLegacyGeneratorConfig();
//noinspection unchecked,rawtypes
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( nameFromGeneratedValue, legacyNamedGenerator.getName(), configuration ),
idValue,
(Map) configuration,
buildingContext
);
return true;
}
}
return false;
}
private HashMap<String, String> buildLegacyGeneratorConfig() {
final Database database = buildingContext.getMetadataCollector().getDatabase();
final Dialect dialect = database.getDialect();
final HashMap<String, String> 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;
}
}

View File

@ -8,33 +8,46 @@ import java.lang.annotation.Annotation;
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.IdGeneratorType;
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;
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;
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 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;
/**
@ -92,13 +105,9 @@ public class GeneratorAnnotationHelper {
}
// 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,13 +127,178 @@ public class GeneratorAnnotationHelper {
}
}
}
catch (ClassLoadingException e) {
// means there is no package-info
}
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;
}
}
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
);
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(
String nameFromGeneratedValue,
TableGenerator generatorAnnotation,
PersistentClass entityMapping,
SimpleValue idValue,
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;
} );
}
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".
*
* @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 <A extends Annotation> void prepareForUse(
Generator generator,
A annotation,
MemberDetails idMember,
Consumer<Properties> configBaseline,
BiConsumer<A,Properties> configExtractor,
GeneratorCreationContext creationContext) {
if ( generator instanceof AnnotationBasedGenerator ) {
@SuppressWarnings("unchecked")
final AnnotationBasedGenerator<A> generation = (AnnotationBasedGenerator<A>) generator;
generation.initialize( annotation, idMember.toJavaMember(), 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 ExportableProducer exportableProducer ) {
exportableProducer.registerExportables( creationContext.getDatabase() );
}
if ( generator instanceof Configurable configurable ) {
configurable.initialize( creationContext.getSqlStringGenerationContext() );
}
}
public static void handleUuidStrategy(
SimpleValue idValue,
MemberDetails idMember,
@ -144,44 +318,6 @@ public class GeneratorAnnotationHelper {
idValue.setColumnToIdentity();
}
public static void applyBaselineConfiguration(
SequenceGenerator generatorConfig,
SimpleValue idValue,
RootClass rootClass,
MetadataBuildingContext context,
BiConsumer<String,String> 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<String, String> 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,
@ -221,22 +357,4 @@ public class GeneratorAnnotationHelper {
configuration.put( parameter.name(), parameter.value() );
}
}
public static void handleTableGenerator(
String generatorName,
TableGenerator generatorConfig,
PersistentClass entityMapping,
SimpleValue idValue,
MetadataBuildingContext context) {
final Map<String,String> configuration = new HashMap<>();
applyBaselineConfiguration( generatorConfig, idValue, entityMapping.getRootClass(), context, configuration::put );
org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorConfig, configuration::put );
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, org.hibernate.id.enhanced.TableGenerator.class.getName(), configuration ),
idValue,
context
);
}
}

View File

@ -536,9 +536,9 @@ public class GeneratorBinder {
* @param beanContainer an optional {@code BeanContainer}
* @param generatorClass a class which implements {@code Generator}
*/
private static Generator instantiateGeneratorAsBean(
private static <T extends Generator> T instantiateGeneratorAsBean(
BeanContainer beanContainer,
Class<? extends Generator> generatorClass) {
Class<T> generatorClass) {
return beanContainer.getBean( generatorClass,
new BeanContainer.LifecycleOptions() {
@Override
@ -611,9 +611,9 @@ public class GeneratorBinder {
* @param beanContainer an optional {@code BeanContainer}
* @param generatorClass a class which implements {@code Generator}
*/
public static Generator instantiateGenerator(
public static <T extends Generator> T instantiateGenerator(
BeanContainer beanContainer,
Class<? extends Generator> generatorClass) {
Class<T> generatorClass) {
if ( beanContainer != null ) {
return instantiateGeneratorAsBean( beanContainer, generatorClass );
}
@ -637,7 +637,7 @@ public class GeneratorBinder {
}
}
private static <A extends Annotation> void callInitialize(
public static <A extends Annotation> void callInitialize(
A annotation,
MemberDetails memberDetails,
GeneratorCreationContext creationContext,
@ -725,6 +725,7 @@ public class GeneratorBinder {
persistentClass,
idValue,
idMember,
generatedValue,
context
) );
}

View File

@ -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.RootClass;
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 class GeneratorParameters {
RootClass rootClass,
BiConsumer<String,String> parameterCollector) {
final ConfigurationService configService =
identifierValue.getMetadata().getMetadataBuildingOptions().getServiceRegistry()
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<String,String> 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 class GeneratorParameters {
(String) settings.get( AvailableSettings.PREFERRED_POOLED_OPTIMIZER )
);
}
}
public static String identityTablesString(Dialect dialect, RootClass rootClass) {
@ -157,10 +195,6 @@ public class GeneratorParameters {
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,

View File

@ -24,14 +24,17 @@ import org.hibernate.mapping.IdentifierBag;
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 @@ import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
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 class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
@Override
public void doSecondPass(Map<String, PersistentClass> 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 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
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 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
handleTableGenerator(
generatorName,
globalTableGenerator.configuration(),
configuration,
idValue,
idBagMember,
buildingContext
);
return;
@ -116,13 +119,13 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
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 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
new TableGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
idValue,
idBagMember,
buildingContext
);
}
@ -138,7 +142,7 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
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 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
handleSequenceGenerator(
generatorName,
globalSequenceGenerator.configuration(),
configuration,
idValue,
idBagMember,
buildingContext
);
return;
@ -158,21 +162,21 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
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 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
private void handleAutoStrategy(
String generatorName,
SimpleValue idValue,
MemberDetails idAttributeMember,
MemberDetails idBagMember,
MetadataBuildingContext buildingContext) {
final GlobalRegistrations globalRegistrations =
buildingContext.getMetadataCollector().getGlobalRegistrations();
@ -191,8 +195,8 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
handleSequenceGenerator(
generatorName,
globalSequenceGenerator.configuration(),
configuration,
idValue,
idBagMember,
buildingContext
);
return;
@ -204,8 +208,8 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
handleTableGenerator(
generatorName,
globalTableGenerator.configuration(),
configuration,
idValue,
idBagMember,
buildingContext
);
return;
@ -230,62 +234,98 @@ public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
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<String,String> 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<String,String> 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;
} );
}
}

View File

@ -4,16 +4,11 @@
*/
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;
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;
@ -21,32 +16,19 @@ import org.hibernate.boot.models.spi.GenericGeneratorRegistration;
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;
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.GeneratorBinder.callConfigure;
import static org.hibernate.boot.model.internal.GeneratorBinder.instantiateGenerator;
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.cfg.MappingSettings.ID_DB_STRUCTURE_NAMING_STRATEGY;
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.
@ -55,50 +37,25 @@ import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM;
*
* @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 );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SEQUENCE
@Override
public void doSecondPass(Map<String, PersistentClass> 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();
}
}
private void handleUnnamedSequenceGenerator() {
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,
@ -106,17 +63,18 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
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,
@ -124,7 +82,7 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
buildingContext
);
if ( localizedMatch != null ) {
handleSequenceGenerator( generator, localizedMatch );
handleSequenceGenerator( generator, localizedMatch, idValue, idMember, buildingContext );
return;
}
@ -135,13 +93,13 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
.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() {
@ -178,19 +136,15 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
}
}
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,
@ -200,10 +154,11 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
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,
@ -263,19 +218,15 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
}
}
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,
@ -283,11 +234,11 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
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,
@ -299,7 +250,7 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
return;
}
final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch(
final GenericGenerator localizedGenericMatch = findLocalizedMatch(
HibernateAnnotations.GENERIC_GENERATOR,
idMember,
null,
@ -317,16 +268,25 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
return;
}
if ( handleAsMetaAnnotated() ) {
return;
}
if ( idMember.getType().isImplementor( UUID.class )
|| idMember.getType().isImplementor( String.class ) ) {
GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext );
return;
}
handleSequenceGenerator( null, null );
if ( handleAsLegacyGenerator() ) {
return;
}
private void handleNamedAutoGenerator() {
handleSequenceGenerator( null, null, idValue, idMember, buildingContext );
}
@Override
protected void handleNamedAutoGenerator() {
if ( handleAsLocalAutoGenerator() ) {
return;
}
@ -347,29 +307,7 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
return;
}
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
final List<? extends Annotation> 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<String,Object> configuration = new HashMap<>();
GeneratorParameters.collectParameters(
idValue,
metadataCollector.getDatabase().getDialect(),
entityMapping.getRootClass(),
configuration::put
);
callConfigure( creationContext, identifierGenerator, configuration, idValue );
return identifierGenerator;
} );
if ( handleAsMetaAnnotated() ) {
return;
}
@ -379,14 +317,18 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
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,
@ -394,11 +336,11 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
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,
@ -410,7 +352,7 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
return true;
}
final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch(
final GenericGenerator localizedGenericMatch = findLocalizedMatch(
HibernateAnnotations.GENERIC_GENERATOR,
idMember,
GenericGenerator::name,
@ -439,7 +381,7 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
final SequenceGeneratorRegistration globalSequenceMatch =
globalRegistrations.getSequenceGeneratorRegistrations().get( generator );
if ( globalSequenceMatch != null ) {
handleSequenceGenerator( generator, globalSequenceMatch.configuration() );
handleSequenceGenerator( generator, globalSequenceMatch.configuration(), idValue, idMember, buildingContext );
return true;
}
@ -466,97 +408,15 @@ public class IdGeneratorResolverSecondPass implements IdGeneratorResolver {
return false;
}
private void handleSequenceGenerator(String nameFromGeneratedValue, SequenceGenerator generator) {
createGeneratorFrom( SequenceStyleGenerator.class, extractConfiguration( nameFromGeneratedValue, generator ) );
}
private Map<String,Object> extractConfiguration(String nameFromGenerated, SequenceGenerator generator) {
final Map<String, Object> 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 applyCommonConfiguration(Map<String, Object> 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<String,Object> extractConfiguration(String nameFromGenerated, TableGenerator generator) {
final Map<String, Object> 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<? extends Generator> generatorClass,
Map<String, Object> 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;
} );
}
}

View File

@ -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,28 +14,16 @@ 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.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 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.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.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
@ -57,46 +40,18 @@ import static org.hibernate.id.PersistentIdentifierGenerator.TABLES;
*
* @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<String, PersistentClass> 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
@ -107,7 +62,6 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
handleSequenceGenerator(
entityMapping.getJpaEntityName(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
@ -118,14 +72,14 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
handleSequenceGenerator(
entityMapping.getJpaEntityName(),
new SequenceGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleNamedSequenceGenerator(GeneratedValue generatedValue) {
@Override
protected void handleNamedSequenceGenerator() {
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
final SequenceGeneratorRegistration globalMatch =
@ -135,7 +89,6 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
handleSequenceGenerator(
generatedValue.generator(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
@ -146,23 +99,14 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
handleSequenceGenerator(
generatedValue.generator(),
new SequenceGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
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 =
@ -174,6 +118,7 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
@ -184,11 +129,13 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
new TableGeneratorJpaAnnotation( metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleNamedTableGenerator(GeneratedValue generatedValue) {
@Override
protected void handleNamedTableGenerator() {
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
final TableGeneratorRegistration globalMatch =
@ -200,6 +147,7 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
@ -211,14 +159,22 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
new TableGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
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();
@ -228,7 +184,6 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
handleSequenceGenerator(
globalRegistrationName,
globalSequenceMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
@ -244,6 +199,7 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
globalTableMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
@ -262,6 +218,10 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
return;
}
if ( handleAsMetaAnnotated() ) {
return;
}
// Implicit handling of UUID generation
if ( idMember.getType().isImplementor( UUID.class )
|| idMember.getType().isImplementor( String.class ) ) {
@ -269,85 +229,16 @@ public class StrictIdGeneratorResolverSecondPass implements IdGeneratorResolver
return;
}
// Handle a few legacy Hibernate generators...
if ( !generator.isEmpty() ) {
final Class<? extends Generator> legacyNamedGenerator = mapLegacyNamedGenerator( generator, idValue );
if ( legacyNamedGenerator != null ) {
final Map<String,String> configuration = buildLegacyGeneratorConfig();
//noinspection unchecked,rawtypes
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generator, legacyNamedGenerator.getName(), configuration ),
idValue,
(Map) configuration,
buildingContext
);
if ( handleAsLegacyGenerator() ) {
return;
}
}
handleSequenceGenerator(
globalRegistrationName,
new SequenceGeneratorJpaAnnotation( generator, metadataCollector.getSourceModelBuildingContext() ),
entityMapping,
new SequenceGeneratorJpaAnnotation( generatedValue.generator(), metadataCollector.getSourceModelBuildingContext() ),
idValue,
idMember,
buildingContext
);
}
private HashMap<String, String> buildLegacyGeneratorConfig() {
final Database database = buildingContext.getMetadataCollector().getDatabase();
final Dialect dialect = database.getDialect();
final HashMap<String, String> 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 generatorName,
SequenceGenerator generatorConfig,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
//generator settings
final Map<String, String> 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
);
}
}

View File

@ -75,6 +75,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType;
import static java.lang.Integer.parseInt;
@ -620,8 +621,8 @@ public class CockroachDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
@Override

View File

@ -196,6 +196,7 @@ import org.hibernate.type.spi.TypeConfiguration;
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 abstract class Dialect implements ConversionContext, TypeContributor, Fun
* {@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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -87,6 +87,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
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 class DialectDelegateWrapper extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return wrapped.getNativeIdentifierGeneratorStrategy();
public GenerationType getNativeValueGenerationStrategy() {
return wrapped.getNativeValueGenerationStrategy();
}
@Override

View File

@ -100,6 +100,7 @@ import org.hibernate.type.descriptor.sql.internal.NamedNativeOrdinalEnumDdlTypeI
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;
@ -1045,8 +1046,8 @@ public class OracleDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
// features which change between 8i, 9i, and 10g ~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -95,6 +95,7 @@ import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
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;
@ -856,8 +857,8 @@ public class PostgreSQLDialect extends Dialect {
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "sequence";
public GenerationType getNativeValueGenerationStrategy() {
return GenerationType.SEQUENCE;
}
@Override

View File

@ -0,0 +1,181 @@
/*
* 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}.
*
* @see org.hibernate.annotations.NativeGenerator
* @since 7.0
*
* @author Steve Ebersole
*/
public class NativeGenerator
implements OnExecutionGenerator, BeforeExecutionGenerator, Configurable, ExportableProducer, AnnotationBasedGenerator<org.hibernate.annotations.NativeGenerator> {
private GenerationType generationType;
private org.hibernate.annotations.NativeGenerator annotation;
private Generator dialectNativeGenerator;
public GenerationType getGenerationType() {
return generationType;
}
@Override
public EnumSet<EventType> 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<String,Object> mapRef = (Map) properties;
mapRef.put( GENERATOR_NAME, sequenceAnnotation.name() );
applyCommonConfiguration( mapRef, creationContext );
SequenceStyleGenerator.applyConfiguration( sequenceAnnotation, mapRef::put );
}
private void applyProperties(
Properties properties,
jakarta.persistence.TableGenerator tableGenerator,
GeneratorCreationContext creationContext) {
//noinspection unchecked,rawtypes
final Map<String,Object> mapRef = (Map) properties;
mapRef.put( GENERATOR_NAME, tableGenerator.name() );
applyCommonConfiguration( mapRef, creationContext );
TableGenerator.applyConfiguration( tableGenerator, mapRef::put );
}
private static void applyCommonConfiguration(
Map<String, Object> mapRef,
GeneratorCreationContext context) {
GeneratorParameters.collectParameters(
(SimpleValue) context.getProperty().getValue(),
context.getDatabase().getDialect(),
context.getRootClass(),
mapRef::put
);
mapRef.put( INCREMENT_PARAM, 1 );
}
}

View File

@ -27,6 +27,7 @@ import org.hibernate.id.enhanced.SequenceStyleGenerator;
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 @@ public class SequenceNamingStrategyTest {
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 @@ public class SequenceNamingStrategyTest {
}
}
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;
}

View File

@ -24,6 +24,7 @@ import org.hibernate.id.enhanced.StandardNamingStrategy;
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 @@ public class TableNamingStrategyTest {
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 @@ public class TableNamingStrategyTest {
}
}
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;
}

View File

@ -32,6 +32,8 @@ import org.hibernate.mapping.IdentifierBag;
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 class AutoGenerationTypeTests {
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();

View File

@ -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;
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.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;
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 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 );
}
@Entity
@org.hibernate.annotations.NativeGenerator
public static class NativeEntity {
@Id @GeneratedValue
long id;
String data;
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.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;
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 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 );
}
@Entity
public static class NativeEntity {
@Id @GeneratedValue
@org.hibernate.annotations.NativeGenerator
long id;
String data;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.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;
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 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 );
}
@Entity
public static class NativeEntity {
@Id @GeneratedValue
long id;
String data;
}
}

View File

@ -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;

View File

@ -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<EventType> 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);
}
}

View File

@ -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;
}
}

View File

@ -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 {
}