HHH-18498 - Support for unnamed generators

HHH-18593 - Enforce GeneratedValue GenerationType
HHH-18609 - Use UuidGenerator for GenerationType.UUID
This commit is contained in:
Steve Ebersole 2024-09-12 13:08:40 -05:00
parent 76df41f9be
commit 4b6822a8bc
45 changed files with 2644 additions and 248 deletions

View File

@ -46,7 +46,8 @@ import org.hibernate.boot.model.internal.AggregateComponentSecondPass;
import org.hibernate.boot.model.internal.AnnotatedClassType;
import org.hibernate.boot.model.internal.CreateKeySecondPass;
import org.hibernate.boot.model.internal.FkSecondPass;
import org.hibernate.boot.model.internal.IdGeneratorResolverSecondPass;
import org.hibernate.boot.model.internal.IdBagIdGeneratorResolverSecondPass;
import org.hibernate.boot.model.internal.IdGeneratorResolver;
import org.hibernate.boot.model.internal.ImplicitToOneJoinTableSecondPass;
import org.hibernate.boot.model.internal.OptionalDeterminationSecondPass;
import org.hibernate.boot.model.internal.QuerySecondPass;
@ -714,9 +715,14 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
throw new IllegalArgumentException( "ID generator object or name is null." );
}
if ( generator.getName().isEmpty() ) {
return;
}
if ( defaultIdentifierGeneratorNames.contains( generator.getName() ) ) {
return;
}
final IdentifierGeneratorDefinition old = idGeneratorDefinitionMap.put( generator.getName(), generator );
if ( old != null && !old.equals( generator ) ) {
if ( bootstrapContext.getJpaCompliance().isGlobalGeneratorScopeEnabled() ) {
@ -1675,7 +1681,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
}
private ArrayList<IdGeneratorResolverSecondPass> idGeneratorResolverSecondPassList;
private ArrayList<IdGeneratorResolver> idGeneratorResolverSecondPassList;
private ArrayList<SetBasicValueTypeSecondPass> setBasicValueTypeSecondPassList;
private ArrayList<AggregateComponentSecondPass> aggregateComponentSecondPassList;
private ArrayList<FkSecondPass> fkSecondPassList;
@ -1696,8 +1702,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
@Override
public void addSecondPass(SecondPass secondPass, boolean onTopOfTheQueue) {
if ( secondPass instanceof IdGeneratorResolverSecondPass ) {
addIdGeneratorResolverSecondPass( (IdGeneratorResolverSecondPass) secondPass, onTopOfTheQueue );
if ( secondPass instanceof IdGeneratorResolver ) {
addIdGeneratorResolverSecondPass( (IdGeneratorResolver) secondPass, onTopOfTheQueue );
}
else if ( secondPass instanceof SetBasicValueTypeSecondPass ) {
addSetBasicValueTypeSecondPass( (SetBasicValueTypeSecondPass) secondPass, onTopOfTheQueue );
@ -1764,7 +1770,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
addSecondPass( secondPass, aggregateComponentSecondPassList, onTopOfTheQueue );
}
private void addIdGeneratorResolverSecondPass(IdGeneratorResolverSecondPass secondPass, boolean onTopOfTheQueue) {
private void addIdGeneratorResolverSecondPass(IdGeneratorResolver secondPass, boolean onTopOfTheQueue) {
if ( idGeneratorResolverSecondPassList == null ) {
idGeneratorResolverSecondPassList = new ArrayList<>();
}

View File

@ -49,7 +49,6 @@ import jakarta.persistence.Table;
import static org.hibernate.boot.model.internal.AnnotatedClassType.EMBEDDABLE;
import static org.hibernate.boot.model.internal.AnnotatedClassType.ENTITY;
import static org.hibernate.boot.model.internal.FilterDefBinder.bindFilterDefs;
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
import static org.hibernate.boot.model.internal.GeneratorParameters.interpretSequenceGenerator;
import static org.hibernate.boot.model.internal.GeneratorParameters.interpretTableGenerator;
import static org.hibernate.boot.model.internal.InheritanceState.getInheritanceStateOfSuperEntity;
@ -132,7 +131,7 @@ public final class AnnotationBinder {
sourceContext( context ).getClassDetailsRegistry()
.resolveClassDetails( pack.getName() + ".package-info" );
buildGenerators( packageInfoClassDetails, context );
GeneratorBinder.registerGlobalGenerators( packageInfoClassDetails, context );
bindTypeDescriptorRegistrations( packageInfoClassDetails, context );
bindEmbeddableInstantiatorRegistrations( packageInfoClassDetails, context );
@ -218,9 +217,9 @@ public final class AnnotationBinder {
bindConverterRegistrations( classDetails, context );
// try to find class level generators
final Map<String, IdentifierGeneratorDefinition> generators = buildGenerators( classDetails, context );
// GeneratorBinder.registerGlobalGenerators( classDetails, context );
if ( context.getMetadataCollector().getClassType( classDetails ) == ENTITY ) {
EntityBinder.bindEntityClass( classDetails, inheritanceStatePerClass, generators, context );
EntityBinder.bindEntityClass( classDetails, inheritanceStatePerClass, context );
}
}

View File

@ -169,7 +169,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.isDefault;
import static org.hibernate.boot.model.internal.BinderHelper.isPrimitive;
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation;
import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
import static org.hibernate.boot.model.internal.GeneratorBinder.visitIdGeneratorDefinitions;
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle.fromResultCheckStyle;
import static org.hibernate.internal.util.ReflectHelper.getDefaultSupplier;
@ -263,7 +263,6 @@ public abstract class CollectionBinder {
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext context,
@ -358,9 +357,26 @@ public abstract class CollectionBinder {
if ( property.hasAnnotationUsage( CollectionId.class, sourceModelContext ) ) {
//do not compute the generators unless necessary
final HashMap<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>(classGenerators);
localGenerators.putAll( buildGenerators( property, context ) );
collectionBinder.setLocalGenerators( localGenerators );
final HashMap<String, IdentifierGeneratorDefinition> availableGenerators = new HashMap<>();
visitIdGeneratorDefinitions(
property.getDeclaringType(),
definition -> {
if ( !definition.getName().isEmpty() ) {
availableGenerators.put( definition.getName(), definition );
}
},
context
);
visitIdGeneratorDefinitions(
property,
definition -> {
if ( !definition.getName().isEmpty() ) {
availableGenerators.put( definition.getName(), definition );
}
},
context
);
collectionBinder.setLocalGenerators( availableGenerators );
}
collectionBinder.bind();

View File

@ -461,7 +461,6 @@ public class EmbeddableBinder {
? Nullability.FORCED_NULL
: ( isNullable ? Nullability.NO_CONSTRAINT : Nullability.FORCED_NOT_NULL ),
propertyAnnotatedElement,
Map.of(),
entityBinder,
isIdentifierMapper,
isComponentEmbedded,
@ -480,7 +479,6 @@ public class EmbeddableBinder {
subholder,
propertyAnnotatedElement,
value,
Map.of(),
context
);
}

View File

@ -213,7 +213,6 @@ public class EntityBinder {
public static void bindEntityClass(
ClassDetails clazzToProcess,
Map<ClassDetails, InheritanceState> inheritanceStates,
Map<String, IdentifierGeneratorDefinition> generators,
MetadataBuildingContext context) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding entity from annotated class: %s", clazzToProcess.getName() );
@ -242,7 +241,7 @@ public class EntityBinder {
inheritanceStates
);
entityBinder.handleInheritance( inheritanceState, superEntity, holder );
entityBinder.handleIdentifier( holder, inheritanceStates, generators, inheritanceState );
entityBinder.handleIdentifier( holder, inheritanceStates, inheritanceState );
final InFlightMetadataCollector collector = context.getMetadataCollector();
if ( persistentClass instanceof RootClass rootClass ) {
@ -396,7 +395,6 @@ public class EntityBinder {
private void handleIdentifier(
PropertyHolder propertyHolder,
Map<ClassDetails, InheritanceState> inheritanceStates,
Map<String, IdentifierGeneratorDefinition> generators,
InheritanceState inheritanceState) {
final ElementsToProcess elementsToProcess = inheritanceState.postProcess( persistentClass, this );
final Set<String> idPropertiesIfIdClass = handleIdClass(
@ -412,7 +410,6 @@ public class EntityBinder {
inheritanceState,
context,
propertyHolder,
generators,
idPropertiesIfIdClass,
elementsToProcess,
inheritanceStates
@ -1032,7 +1029,6 @@ public class EntityBinder {
InheritanceState inheritanceState,
MetadataBuildingContext context,
PropertyHolder propertyHolder,
Map<String, IdentifierGeneratorDefinition> generators,
Set<String> idPropertiesIfIdClass,
ElementsToProcess elementsToProcess,
Map<ClassDetails, InheritanceState> inheritanceStates) {
@ -1063,7 +1059,6 @@ public class EntityBinder {
? Nullability.FORCED_NULL
: Nullability.NO_CONSTRAINT,
propertyAnnotatedElement,
generators,
this,
false,
false,

View File

@ -0,0 +1,252 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.generator.Generator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
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.MemberDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME;
import static org.hibernate.internal.util.config.ConfigurationHelper.setIfNotEmpty;
/**
* Helper for dealing with generators defined via annotations
*
* @author Steve Ebersole
*/
public class GeneratorAnnotationHelper {
public static <A extends Annotation> A findLocalizedMatch(
AnnotationDescriptor<A> generatorAnnotationType,
MemberDetails idMember,
Function<A,String> nameExtractor,
String matchName,
MetadataBuildingContext context) {
final SourceModelBuildingContext sourceModelContext = context.getMetadataCollector().getSourceModelBuildingContext();
A possibleMatch = null;
// first we look on the member
for ( A generatorAnnotation : idMember.getRepeatedAnnotationUsages( generatorAnnotationType, sourceModelContext ) ) {
if ( nameExtractor != null ) {
final String registrationName = nameExtractor.apply( generatorAnnotation );
if ( registrationName.isEmpty() ) {
possibleMatch = generatorAnnotation;
continue;
}
if ( registrationName.equals( matchName ) ) {
return generatorAnnotation;
}
}
else {
return generatorAnnotation;
}
}
// next, on the class
for ( A generatorAnnotation : idMember.getDeclaringType().getRepeatedAnnotationUsages( generatorAnnotationType, sourceModelContext ) ) {
if ( nameExtractor != null ) {
final String registrationName = nameExtractor.apply( generatorAnnotation );
if ( registrationName.isEmpty() ) {
if ( possibleMatch == null ) {
possibleMatch = generatorAnnotation;
}
continue;
}
if ( registrationName.equals( matchName ) ) {
return generatorAnnotation;
}
}
else {
return generatorAnnotation;
}
}
// lastly, on the package
final String packageInfoFqn = StringHelper.qualifier( idMember.getDeclaringType().getClassName() ) + ".package-info";
try {
final ClassDetails packageInfo = context.getMetadataCollector()
.getSourceModelBuildingContext()
.getClassDetailsRegistry()
.resolveClassDetails( packageInfoFqn );
for ( A generatorAnnotation : packageInfo.getRepeatedAnnotationUsages( generatorAnnotationType, sourceModelContext ) ) {
if ( nameExtractor != null ) {
final String registrationName = nameExtractor.apply( generatorAnnotation );
if ( registrationName.isEmpty() ) {
if ( possibleMatch == null ) {
possibleMatch = generatorAnnotation;
}
continue;
}
if ( registrationName.equals( matchName ) ) {
return generatorAnnotation;
}
}
else {
return generatorAnnotation;
}
}
}
catch (ClassLoadingException e) {
// means there is no package-info
}
return possibleMatch;
}
public static void handleUuidStrategy(
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
final org.hibernate.annotations.UuidGenerator generatorConfig = findLocalizedMatch(
HibernateAnnotations.UUID_GENERATOR,
idMember,
null,
null,
context
);
idValue.setCustomIdGeneratorCreator( (creationContext) -> new UuidGenerator( generatorConfig, idMember ) );
}
public static void handleIdentityStrategy(
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
idValue.setCustomIdGeneratorCreator( (creationContext) -> new IdentityGenerator() );
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,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
//generator settings
final Map<String,String> configuration = new HashMap<>();
setIfNotEmpty( generatorConfig.name(), IdentifierGenerator.GENERATOR_NAME, configuration );
configuration.put( IdentifierGenerator.ENTITY_NAME, entityMapping.getEntityName() );
configuration.put( IdentifierGenerator.JPA_ENTITY_NAME, entityMapping.getJpaEntityName() );
applyAnnotationParameters( generatorConfig, configuration );
configuration.put( PersistentIdentifierGenerator.TABLE, idValue.getTable().getName() );
if ( idValue.getColumnSpan() == 1 ) {
configuration.put( PersistentIdentifierGenerator.PK, idValue.getColumns().get(0).getName() );
}
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, determineStrategyName( generatorConfig ), configuration ),
idMember,
idValue,
entityMapping,
context
);
}
private static String determineStrategyName(GenericGenerator generatorConfig) {
final Class<? extends Generator> type = generatorConfig.type();
if ( !Objects.equals( type, Generator.class ) ) {
return type.getName();
}
return generatorConfig.strategy();
}
private static void applyAnnotationParameters(GenericGenerator generatorConfig, Map<String, String> configuration) {
for ( Parameter parameter : generatorConfig.parameters() ) {
configuration.put( parameter.name(), parameter.value() );
}
}
public static void handleTableGenerator(
String generatorName,
TableGenerator generatorConfig,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
final Map<String,String> configuration = new HashMap<>();
applyBaselineConfiguration( generatorConfig, idValue, entityMapping.getRootClass(), context, configuration::put );
org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorConfig, idValue, configuration::put );
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, org.hibernate.id.enhanced.TableGenerator.class.getName(), configuration ),
idMember,
idValue,
entityMapping,
context
);
}
}

View File

@ -6,11 +6,18 @@
*/
package org.hibernate.boot.model.internal;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.Version;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
@ -20,6 +27,7 @@ import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.model.source.internal.hbm.MappingDocument;
import org.hibernate.boot.models.spi.GlobalRegistrar;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
@ -33,10 +41,14 @@ 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.UuidValueGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.MemberDetails;
@ -46,15 +58,13 @@ import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.Version;
import static jakarta.persistence.GenerationType.AUTO;
import static java.util.Collections.emptyMap;
import static org.hibernate.boot.model.internal.AnnotationHelper.extractParameterMap;
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
@ -63,7 +73,7 @@ import static org.hibernate.boot.model.internal.GeneratorParameters.collectParam
import static org.hibernate.boot.model.internal.GeneratorParameters.interpretSequenceGenerator;
import static org.hibernate.boot.model.internal.GeneratorParameters.interpretTableGenerator;
import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorClass;
import static org.hibernate.boot.model.internal.GeneratorStrategies.generatorStrategy;
import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME;
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.resource.beans.internal.Helper.allowExtensionsInCdi;
@ -94,7 +104,7 @@ public class GeneratorBinder {
* Create a generator, based on a {@link GeneratedValue} annotation.
*/
public static void makeIdGenerator(
SimpleValue id,
SimpleValue identifierValue,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
@ -103,15 +113,105 @@ public class GeneratorBinder {
//generator settings
final Map<String,Object> configuration = new HashMap<>();
configuration.put( IdentifierGenerator.GENERATOR_NAME, generatorName );
configuration.put( PersistentIdentifierGenerator.TABLE, id.getTable().getName() );
if ( id.getColumnSpan() == 1 ) {
configuration.put( PersistentIdentifierGenerator.PK, id.getColumns().get(0).getName() );
configuration.put( GENERATOR_NAME, generatorName );
configuration.put( PersistentIdentifierGenerator.TABLE, identifierValue.getTable().getName() );
if ( identifierValue.getColumnSpan() == 1 ) {
configuration.put( PersistentIdentifierGenerator.PK, identifierValue.getColumns().get(0).getName() );
}
final String generatorStrategy =
determineStrategy( idAttributeMember, generatorType, generatorName, context, localGenerators, configuration );
setGeneratorCreator( id, configuration, generatorStrategy, context );
if ( generatorName.isEmpty() ) {
final GeneratedValue generatedValue = idAttributeMember.getDirectAnnotationUsage( GeneratedValue.class );
if ( generatedValue != null ) {
// The mapping used @GeneratedValue but specified no name. This is a special case added in JPA 3.2.
// Look for a matching "implied generator" based on the GenerationType
final GenerationType strategy = generatedValue.strategy();
final String strategyGeneratorClassName = correspondingGeneratorName( strategy );
final IdentifierGeneratorDefinition impliedGenerator = determineImpliedGenerator(
strategy,
strategyGeneratorClassName,
localGenerators
);
if ( impliedGenerator != null ) {
configuration.putAll( impliedGenerator.getParameters() );
final BeanContainer beanContainer = beanContainer( context );
identifierValue.setCustomIdGeneratorCreator( creationContext -> {
final Generator identifierGenerator = instantiateGenerator(
beanContainer,
generatorClass( strategyGeneratorClassName, identifierValue )
);
callConfigure( creationContext, identifierGenerator, configuration, identifierValue );
if ( identifierGenerator instanceof IdentityGenerator) {
identifierValue.setColumnToIdentity();
}
return identifierGenerator;
} );
return;
}
}
}
final String generatorStrategy = determineStrategy(
idAttributeMember,
generatorType,
generatorName,
context,
localGenerators,
configuration
);
setGeneratorCreator( identifierValue, configuration, generatorStrategy, context );
}
private static IdentifierGeneratorDefinition determineImpliedGenerator(
GenerationType strategy,
String strategyGeneratorClassName,
Map<String, ? extends IdentifierGeneratorDefinition> localGenerators) {
if ( localGenerators == null ) {
return null;
}
if ( localGenerators.size() == 1 ) {
final IdentifierGeneratorDefinition generatorDefinition = localGenerators.entrySet().iterator().next().getValue();
// NOTE : a little bit of a special rule here for the case of just one -
// we consider it a match, based on strategy, if the strategy is AUTO or matches...
if ( strategy == AUTO
|| isImpliedGenerator( strategy, strategyGeneratorClassName, generatorDefinition ) ) {
return generatorDefinition;
}
}
IdentifierGeneratorDefinition matching = null;
for ( Map.Entry<String, ? extends IdentifierGeneratorDefinition> localGeneratorEntry : localGenerators.entrySet() ) {
if ( isImpliedGenerator( strategy, strategyGeneratorClassName, localGeneratorEntry.getValue() ) ) {
if ( matching != null ) {
// we found multiple matching generators
return null;
}
matching = localGeneratorEntry.getValue();
}
}
return matching;
}
private static boolean isImpliedGenerator(
GenerationType strategy,
String strategyGeneratorClassName,
IdentifierGeneratorDefinition generatorDefinition) {
return generatorDefinition.getStrategy().equals( strategyGeneratorClassName );
}
private static String correspondingGeneratorName(GenerationType strategy) {
return switch ( strategy ) {
// case UUID -> org.hibernate.id.uuid.UuidGenerator.class.getName();
case UUID -> UuidValueGenerator.class.getName();
case TABLE -> org.hibernate.id.enhanced.TableGenerator.class.getName();
case IDENTITY -> null;
default -> SequenceStyleGenerator.class.getName();
};
}
private static String determineStrategy(
@ -182,39 +282,66 @@ public class GeneratorBinder {
private static GenerationType interpretGenerationType(GeneratedValue generatedValueAnn) {
// todo (jpa32) : when can this ever be null?
final GenerationType strategy = generatedValueAnn.strategy();
return strategy == null ? GenerationType.AUTO : strategy;
return strategy == null ? AUTO : strategy;
}
/**
* Build any generators declared using {@link TableGenerator}, {@link SequenceGenerator},
* and {@link GenericGenerator} annotations of the given program element.
* Collects definition objects for all generators defined using any of {@link TableGenerator},
* {@link SequenceGenerator}, and {@link GenericGenerator} on the given annotated element.
*/
public static Map<String, IdentifierGeneratorDefinition> buildGenerators(
public static List<IdentifierGeneratorDefinition> collectIdGeneratorDefinitions(
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final ArrayList<IdentifierGeneratorDefinition> definitions = new ArrayList<>();
visitIdGeneratorDefinitions(
annotatedElement,
definitions::add,
context
);
return definitions;
}
public static void visitIdGeneratorDefinitions(
AnnotationTarget annotatedElement,
Consumer<IdentifierGeneratorDefinition> consumer,
MetadataBuildingContext context) {
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final SourceModelBuildingContext sourceModelContext = metadataCollector.getSourceModelBuildingContext();
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>();
annotatedElement.forEachAnnotationUsage( TableGenerator.class, sourceModelContext, (usage) -> {
IdentifierGeneratorDefinition idGenerator = buildTableIdGenerator( usage );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
final IdentifierGeneratorDefinition idGenerator = buildTableIdGenerator( usage );
consumer.accept( idGenerator );
} );
annotatedElement.forEachAnnotationUsage( SequenceGenerator.class, sourceModelContext, (usage) -> {
IdentifierGeneratorDefinition idGenerator = buildSequenceIdGenerator( usage );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
final IdentifierGeneratorDefinition idGenerator = buildSequenceIdGenerator( usage );
consumer.accept( idGenerator );
} );
annotatedElement.forEachAnnotationUsage( GenericGenerator.class, sourceModelContext, (usage) -> {
final IdentifierGeneratorDefinition idGenerator = buildIdGenerator( usage );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
consumer.accept( idGenerator );
} );
return generators;
}
public static void registerGlobalGenerators(
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
if ( !context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled() ) {
return;
}
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
visitIdGeneratorDefinitions(
annotatedElement,
(definition) -> {
if ( !definition.getName().isEmpty() ) {
metadataCollector.addIdentifierGenerator( definition );
}
},
context
);
}
private static IdentifierGeneratorDefinition buildIdGenerator(GenericGenerator generatorAnnotation) {
@ -502,7 +629,7 @@ public class GeneratorBinder {
* @param beanContainer an optional {@code BeanContainer}
* @param generatorClass a class which implements {@code Generator}
*/
private static Generator instantiateGenerator(
public static Generator instantiateGenerator(
BeanContainer beanContainer,
Class<? extends Generator> generatorClass) {
if ( beanContainer != null ) {
@ -563,7 +690,7 @@ public class GeneratorBinder {
* call its {@link Configurable#configure(GeneratorCreationContext, Properties)
* configure()} method.
*/
private static void callConfigure(
public static void callConfigure(
GeneratorCreationContext creationContext,
Generator generator,
Map<String, Object> configuration,
@ -594,35 +721,93 @@ public class GeneratorBinder {
* Create a generator, based on a {@link GeneratedValue} annotation.
*/
private static void createIdGenerator(
MemberDetails idMember,
SimpleValue idValue,
Map<String, IdentifierGeneratorDefinition> classGenerators,
MetadataBuildingContext context,
MemberDetails idMember) {
//manage composite related metadata
//guess if its a component and find id data access (property, field etc)
PersistentClass persistentClass,
MetadataBuildingContext context) {
// NOTE: `generatedValue` is never null here
final GeneratedValue generatedValue = castNonNull( idMember.getDirectAnnotationUsage( GeneratedValue.class ) );
final String generatorType =
generatorStrategy( generatedValue.strategy(), generatedValue.generator(), idMember.getType() );
final String generatorName = generatedValue.generator();
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( idMember, context );
context.getMetadataCollector()
.addSecondPass( new IdGeneratorResolverSecondPass(
idValue,
idMember,
generatorType,
generatorName,
context
) );
// process and register any generators defined on the member.
// according to JPA these are also global.
context.getMetadataCollector().getGlobalRegistrations().as( GlobalRegistrar.class ).collectIdGenerators( idMember );
context.getMetadataCollector().addSecondPass( new StrictIdGeneratorResolverSecondPass(
persistentClass,
idValue,
idMember,
context
) );
}
else {
//clone classGenerator and override with local values
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>( classGenerators );
generators.putAll( buildGenerators( idMember, context ) );
makeIdGenerator( idValue, idMember, generatorType, generatorName, context, generators );
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
persistentClass,
idValue,
idMember,
generatedValue,
context
) );
}
}
public static void createGeneratorFrom(
IdentifierGeneratorDefinition defaultedGenerator,
MemberDetails idMember,
SimpleValue idValue,
Map<String, Object> configuration,
MetadataBuildingContext context) {
configuration.putAll( defaultedGenerator.getParameters() );
final BeanContainer beanContainer = beanContainer( context );
idValue.setCustomIdGeneratorCreator( creationContext -> {
final Generator identifierGenerator = instantiateGenerator(
beanContainer,
generatorClass( defaultedGenerator.getStrategy(), idValue )
);
callConfigure( creationContext, identifierGenerator, configuration, idValue );
if ( identifierGenerator instanceof IdentityGenerator) {
idValue.setColumnToIdentity();
}
if ( identifierGenerator instanceof ExportableProducer exportableProducer ) {
exportableProducer.registerExportables( creationContext.getDatabase() );
}
return identifierGenerator;
} );
}
public static void createGeneratorFrom(
IdentifierGeneratorDefinition defaultedGenerator,
MemberDetails idMember,
SimpleValue idValue,
PersistentClass persistentClass,
MetadataBuildingContext context) {
createGeneratorFrom(
defaultedGenerator,
idMember,
idValue,
buildConfigurationMap( defaultedGenerator, idValue, persistentClass ),
context
);
}
public static Map<String, Object> buildConfigurationMap(
IdentifierGeneratorDefinition defaultedGenerator,
SimpleValue idValue,
PersistentClass persistentClass) {
final Map<String,Object> configuration = new HashMap<>();
configuration.put( PersistentIdentifierGenerator.TABLE, idValue.getTable().getName() );
if ( idValue.getColumnSpan() == 1 ) {
configuration.put( PersistentIdentifierGenerator.PK, idValue.getColumns().get( 0).getName() );
}
return configuration;
}
/**
* Set up the identifier generator for an id defined in a {@code hbm.xml} mapping.
*
@ -658,7 +843,7 @@ public class GeneratorBinder {
/**
* Obtain a {@link BeanContainer} to be used for instantiating generators.
*/
private static BeanContainer beanContainer(MetadataBuildingContext buildingContext) {
public static BeanContainer beanContainer(MetadataBuildingContext buildingContext) {
final ServiceRegistry serviceRegistry = buildingContext.getBootstrapContext().getServiceRegistry();
return allowExtensionsInCdi( serviceRegistry )
? serviceRegistry.requireService( ManagedBeanRegistry.class ).getBeanContainer()
@ -701,7 +886,6 @@ public class GeneratorBinder {
PropertyHolder propertyHolder,
PropertyData inferredData,
SimpleValue idValue,
Map<String, IdentifierGeneratorDefinition> classGenerators,
MetadataBuildingContext context) {
final SourceModelBuildingContext sourceModelContext =
@ -745,7 +929,7 @@ public class GeneratorBinder {
) );
}
else if ( idAttributeMember.hasDirectAnnotationUsage( GeneratedValue.class ) ) {
createIdGenerator( idValue, classGenerators, context, idAttributeMember );
createIdGenerator( idAttributeMember, idValue, propertyHolder.getPersistentClass(), context );
}
}
@ -769,4 +953,10 @@ public class GeneratorBinder {
+ "' has too many generator annotations: " + generatorAnnotations );
}
}
public static void applyIfNotEmpty(String name, String value, BiConsumer<String,String> consumer) {
if ( StringHelper.isNotEmpty( value ) ) {
consumer.accept( name, value );
}
}
}

View File

@ -11,6 +11,8 @@ import jakarta.persistence.TableGenerator;
import jakarta.persistence.UniqueConstraint;
import org.hibernate.Internal;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
@ -36,6 +38,18 @@ import org.hibernate.type.Type;
import java.util.Map;
import java.util.Properties;
import java.util.function.BiConsumer;
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;
import static org.hibernate.id.OptimizableGenerator.DEFAULT_INITIAL_VALUE;
import static org.hibernate.id.OptimizableGenerator.IMPLICIT_NAME_BASE;
import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM;
import static org.hibernate.id.OptimizableGenerator.INITIAL_PARAM;
import static org.hibernate.id.PersistentIdentifierGenerator.PK;
import static org.hibernate.id.PersistentIdentifierGenerator.TABLE;
import static org.hibernate.id.PersistentIdentifierGenerator.TABLES;
/**
* Responsible for setting up the parameters which are passed to
@ -50,6 +64,19 @@ public class GeneratorParameters {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( GeneratorBinder.class );
/**
* Collect the parameters which should be passed to
* {@link Configurable#configure(GeneratorCreationContext, Properties)}.
*/
public static Properties collectParameters(
SimpleValue identifierValue,
Dialect dialect,
RootClass rootClass) {
final Properties params = new Properties();
collectParameters( identifierValue, dialect, rootClass, params::put );
return params;
}
/**
* Collect the parameters which should be passed to
* {@link Configurable#configure(GeneratorCreationContext, Properties)}.
@ -59,63 +86,73 @@ public class GeneratorParameters {
Dialect dialect,
RootClass rootClass,
Map<String, Object> configuration) {
final Properties params = collectParameters( identifierValue, dialect, rootClass );
if ( configuration != null ) {
params.putAll( configuration );
}
return params;
}
public static void collectParameters(
SimpleValue identifierValue,
Dialect dialect,
RootClass rootClass,
BiConsumer<String,String> parameterCollector) {
final ConfigurationService configService =
identifierValue.getMetadata().getMetadataBuildingOptions().getServiceRegistry()
.requireService( ConfigurationService.class );
final Properties params = new Properties();
// default initial value and allocation size per-JPA defaults
params.setProperty( OptimizableGenerator.INITIAL_PARAM,
String.valueOf( OptimizableGenerator.DEFAULT_INITIAL_VALUE ) );
parameterCollector.accept( INITIAL_PARAM, String.valueOf( DEFAULT_INITIAL_VALUE ) );
parameterCollector.accept( INCREMENT_PARAM, String.valueOf( defaultIncrement( configService ) ) );
params.setProperty( OptimizableGenerator.INCREMENT_PARAM,
String.valueOf( defaultIncrement( configService ) ) );
//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
final String tableName = identifierValue.getTable().getQuotedName( dialect );
params.setProperty( PersistentIdentifierGenerator.TABLE, tableName );
parameterCollector.accept( TABLE, tableName );
//pass the column name (a generated id almost always has a single column)
final Column column = (Column) identifierValue.getSelectables().get(0);
final String columnName = column.getQuotedName( dialect );
params.setProperty( PersistentIdentifierGenerator.PK, columnName );
if ( identifierValue.getColumnSpan() == 1 ) {
final Column column = (Column) identifierValue.getSelectables().get( 0 );
final String columnName = column.getQuotedName( dialect );
parameterCollector.accept( PK, columnName );
}
//pass the entity-name, if not a collection-id
if ( rootClass != null ) {
params.setProperty( IdentifierGenerator.ENTITY_NAME, rootClass.getEntityName() );
params.setProperty( IdentifierGenerator.JPA_ENTITY_NAME, rootClass.getJpaEntityName() );
parameterCollector.accept( ENTITY_NAME, rootClass.getEntityName() );
parameterCollector.accept( JPA_ENTITY_NAME, rootClass.getJpaEntityName() );
// The table name is not really a good default for subselect entities,
// so use the JPA entity name which is short
params.setProperty( OptimizableGenerator.IMPLICIT_NAME_BASE,
parameterCollector.accept(
IMPLICIT_NAME_BASE,
identifierValue.getTable().isSubselect()
? rootClass.getJpaEntityName()
: identifierValue.getTable().getName() );
: identifierValue.getTable().getName()
);
params.setProperty( PersistentIdentifierGenerator.TABLES,
identityTablesString( dialect, rootClass ) );
parameterCollector.accept( TABLES, identityTablesString( dialect, rootClass ) );
}
else {
params.setProperty( PersistentIdentifierGenerator.TABLES, tableName );
params.setProperty( OptimizableGenerator.IMPLICIT_NAME_BASE, tableName );
parameterCollector.accept( TABLES, tableName );
parameterCollector.accept( IMPLICIT_NAME_BASE, tableName );
}
params.put( IdentifierGenerator.CONTRIBUTOR_NAME,
identifierValue.getBuildingContext().getCurrentContributorName() );
parameterCollector.accept( CONTRIBUTOR_NAME, identifierValue.getBuildingContext().getCurrentContributorName() );
final Map<String, Object> settings = configService.getSettings();
if ( settings.containsKey( AvailableSettings.PREFERRED_POOLED_OPTIMIZER ) ) {
params.put( AvailableSettings.PREFERRED_POOLED_OPTIMIZER,
settings.get( AvailableSettings.PREFERRED_POOLED_OPTIMIZER ) );
parameterCollector.accept(
AvailableSettings.PREFERRED_POOLED_OPTIMIZER,
(String) settings.get( AvailableSettings.PREFERRED_POOLED_OPTIMIZER )
);
}
params.putAll( configuration );
return params;
}
private static String identityTablesString(Dialect dialect, RootClass rootClass) {
public static String identityTablesString(Dialect dialect, RootClass rootClass) {
final StringBuilder tables = new StringBuilder();
for ( Table table : rootClass.getIdentityTables() ) {
tables.append( table.getQuotedName( dialect ) );
@ -126,7 +163,11 @@ public class GeneratorParameters {
return tables.toString();
}
private static int defaultIncrement(ConfigurationService configService) {
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,
StandardConverters.STRING, null );
@ -198,13 +239,13 @@ public class GeneratorParameters {
}
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.INCREMENT_PARAM,
INCREMENT_PARAM,
String.valueOf( tableGeneratorAnnotation.allocationSize() )
);
// See comment on HHH-4884 wrt initialValue. Basically initialValue is really the stated value + 1
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.INITIAL_PARAM,
INITIAL_PARAM,
String.valueOf( tableGeneratorAnnotation.initialValue() + 1 )
);
@ -238,11 +279,11 @@ public class GeneratorParameters {
}
definitionBuilder.addParam(
SequenceStyleGenerator.INCREMENT_PARAM,
INCREMENT_PARAM,
String.valueOf( sequenceGeneratorAnnotation.allocationSize() )
);
definitionBuilder.addParam(
SequenceStyleGenerator.INITIAL_PARAM,
INITIAL_PARAM,
String.valueOf( sequenceGeneratorAnnotation.initialValue() )
);

View File

@ -9,6 +9,8 @@ package org.hibernate.boot.model.internal;
import jakarta.persistence.GenerationType;
import org.hibernate.MappingException;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.generator.Generator;
import org.hibernate.id.ForeignGenerator;
import org.hibernate.id.GUIDGenerator;
@ -113,4 +115,44 @@ public class GeneratorStrategies {
return clazz;
}
public static Class<? extends Generator> mapLegacyNamedGenerator(String strategy, Dialect dialect) {
if ( "native".equals(strategy) ) {
strategy = dialect.getNativeIdentifierGeneratorStrategy();
}
switch (strategy) {
case "assigned":
return org.hibernate.id.Assigned.class;
case "enhanced-sequence":
case "sequence":
return SequenceStyleGenerator.class;
case "enhanced-table":
case "table":
return org.hibernate.id.enhanced.TableGenerator.class;
case "identity":
return IdentityGenerator.class;
case "increment":
return IncrementGenerator.class;
case "foreign":
return ForeignGenerator.class;
case "uuid":
case "uuid.hex":
return UUIDHexGenerator.class;
case "uuid2":
return UUIDGenerator.class;
case "select":
return SelectGenerator.class;
case "guid":
return GUIDGenerator.class;
}
return null;
}
public static Class<? extends Generator> mapLegacyNamedGenerator(String strategy, MetadataBuildingContext buildingContext) {
return mapLegacyNamedGenerator( strategy, buildingContext.getMetadataCollector().getDatabase().getDialect() );
}
public static Class<? extends Generator> mapLegacyNamedGenerator(String strategy, SimpleValue idValue) {
return mapLegacyNamedGenerator( strategy, idValue.getMetadata().getDatabase().getDialect() );
}
}

View File

@ -133,7 +133,8 @@ public class IdBagBinder extends BagBinder {
}
if ( isGlobalGeneratorNameGlobal( buildingContext ) ) {
SecondPass secondPass = new IdGeneratorResolverSecondPass(
SecondPass secondPass = new IdBagIdGeneratorResolverSecondPass(
(IdentifierBag) collection,
id,
property,
generatorType,

View File

@ -0,0 +1,303 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.internal;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.annotations.internal.SequenceGeneratorJpaAnnotation;
import org.hibernate.boot.models.annotations.internal.TableGeneratorJpaAnnotation;
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.MetadataBuildingContext;
import org.hibernate.generator.Generator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.mapping.IdentifierBag;
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 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;
/**
* IdGeneratorResolver for handling generators assigned to id-bag mappings
*
* @author Andrea Boriero
*/
public class IdBagIdGeneratorResolverSecondPass implements IdGeneratorResolver {
private final PersistentClass entityMapping;
private final SimpleValue idValue;
private final MemberDetails idAttributeMember;
private final String generatorType;
private final String generatorName;
private final MetadataBuildingContext buildingContext;
private final Map<String,String> configuration;
public IdBagIdGeneratorResolverSecondPass(
IdentifierBag idBagMapping,
SimpleValue idValue,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
MetadataBuildingContext buildingContext) {
this.entityMapping = null;
this.idValue = idValue;
this.idAttributeMember = idAttributeMember;
this.generatorType = generatorType;
this.generatorName = generatorName;
this.buildingContext = buildingContext;
this.configuration = new HashMap<>();
}
@Override
public void doSecondPass(Map<String, PersistentClass> idGeneratorDefinitionMap) throws MappingException {
final GeneratedValue generatedValue = idAttributeMember.getDirectAnnotationUsage( GeneratedValue.class );
switch ( generatedValue.strategy() ) {
case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idAttributeMember, buildingContext );
case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue, idAttributeMember, buildingContext );
case SEQUENCE -> handleSequenceStrategy(
generatorName,
idValue,
idAttributeMember,
buildingContext
);
case TABLE -> handleTableStrategy(
generatorName,
entityMapping,
idValue,
idAttributeMember,
generatedValue,
buildingContext
);
case AUTO -> handleAutoStrategy(
generatorName,
entityMapping,
idValue,
idAttributeMember,
generatedValue,
buildingContext
);
}
}
private void handleTableStrategy(
String generatorName,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idAttributeMember,
GeneratedValue generatedValue,
MetadataBuildingContext buildingContext) {
final GlobalRegistrations globalRegistrations = buildingContext.getMetadataCollector().getGlobalRegistrations();
final TableGeneratorRegistration globalTableGenerator = globalRegistrations.getTableGeneratorRegistrations().get( generatorName );
if ( globalTableGenerator != null ) {
handleTableGenerator(
generatorName,
globalTableGenerator.configuration(),
configuration,
idValue,
idAttributeMember,
buildingContext
);
return;
}
final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idAttributeMember,
TableGenerator::name,
generatorName,
buildingContext
);
if ( localizedTableMatch != null ) {
handleTableGenerator( generatorName, localizedTableMatch, configuration, idValue, idAttributeMember, buildingContext );
return;
}
GeneratorAnnotationHelper.handleTableGenerator(
generatorName,
new TableGeneratorJpaAnnotation( buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
idValue,
idAttributeMember,
buildingContext
);
}
private void handleSequenceStrategy(
String generatorName,
SimpleValue idValue,
MemberDetails idAttributeMember,
MetadataBuildingContext buildingContext) {
final GlobalRegistrations globalRegistrations = buildingContext.getMetadataCollector().getGlobalRegistrations();
final SequenceGeneratorRegistration globalSequenceGenerator = globalRegistrations.getSequenceGeneratorRegistrations().get( generatorName );
if ( globalSequenceGenerator != null ) {
handleSequenceGenerator(
generatorName,
globalSequenceGenerator.configuration(),
configuration,
idValue,
idAttributeMember,
buildingContext
);
return;
}
final SequenceGenerator localizedSequencedMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idAttributeMember,
SequenceGenerator::name,
generatorName,
buildingContext
);
if ( localizedSequencedMatch != null ) {
handleSequenceGenerator( generatorName, localizedSequencedMatch, configuration, idValue, idAttributeMember, buildingContext );
return;
}
handleSequenceGenerator(
generatorName,
new SequenceGeneratorJpaAnnotation( buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
configuration,
idValue,
idAttributeMember,
buildingContext
);
}
private void handleAutoStrategy(
String generatorName,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idAttributeMember,
GeneratedValue generatedValue,
MetadataBuildingContext buildingContext) {
final GlobalRegistrations globalRegistrations = buildingContext.getMetadataCollector().getGlobalRegistrations();
final SequenceGeneratorRegistration globalSequenceGenerator = globalRegistrations.getSequenceGeneratorRegistrations().get( generatorName );
if ( globalSequenceGenerator != null ) {
handleSequenceGenerator(
generatorName,
globalSequenceGenerator.configuration(),
configuration,
idValue,
idAttributeMember,
buildingContext
);
return;
}
final TableGeneratorRegistration globalTableGenerator = globalRegistrations.getTableGeneratorRegistrations().get( generatorName );
if ( globalTableGenerator != null ) {
handleTableGenerator(
generatorName,
globalTableGenerator.configuration(),
configuration,
idValue,
idAttributeMember,
buildingContext
);
return;
}
final Class<? extends Generator> legacyNamedGenerator = GeneratorStrategies.mapLegacyNamedGenerator( generatorName, idValue );
if ( legacyNamedGenerator != null ) {
//generator settings
if ( idValue.getColumnSpan() == 1 ) {
configuration.put( PersistentIdentifierGenerator.PK, idValue.getColumns().get(0).getName() );
}
createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, legacyNamedGenerator.getName(), configuration ),
idAttributeMember,
idValue,
(Map) configuration,
buildingContext
);
return;
}
final SequenceGenerator localizedSequencedMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idAttributeMember,
SequenceGenerator::name,
generatorName,
buildingContext
);
if ( localizedSequencedMatch != null ) {
handleSequenceGenerator( generatorName, localizedSequencedMatch, configuration, idValue, idAttributeMember, buildingContext );
return;
}
final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idAttributeMember,
TableGenerator::name,
generatorName,
buildingContext
);
if ( localizedTableMatch != null ) {
handleTableGenerator( generatorName, localizedTableMatch, configuration, idValue, idAttributeMember, buildingContext );
return;
}
makeIdGenerator( idValue, idAttributeMember, generatorType, generatorName, buildingContext, null );
}
public static void handleSequenceGenerator(
String generatorName,
SequenceGenerator generatorConfig,
Map<String,String> configuration,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
applyBaselineConfiguration( generatorConfig, idValue, null, context, configuration::put );
SequenceStyleGenerator.applyConfiguration( generatorConfig, idValue, configuration::put );
createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, SequenceStyleGenerator.class.getName(), configuration ),
idMember,
idValue,
(Map) configuration,
context
);
}
public static void handleTableGenerator(
String generatorName,
TableGenerator generatorConfig,
Map<String,String> configuration,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext context) {
GeneratorAnnotationHelper.applyBaselineConfiguration( generatorConfig, idValue, null, context, configuration::put );
org.hibernate.id.enhanced.TableGenerator.applyConfiguration( generatorConfig, idValue, configuration::put );
createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, org.hibernate.id.enhanced.TableGenerator.class.getName(), configuration ),
idMember,
idValue,
(Map) configuration,
context
);
}
}

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.internal;
import org.hibernate.boot.spi.SecondPass;
/**
* Marker interface for second passes which bind id generators
*
* @author Steve Ebersole
*/
public interface IdGeneratorResolver extends SecondPass {
}

View File

@ -6,68 +6,570 @@
*/
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.model.relational.ExportableProducer;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.spi.GenericGeneratorRegistration;
import org.hibernate.boot.models.spi.SequenceGeneratorRegistration;
import org.hibernate.boot.models.spi.TableGeneratorRegistration;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.cfg.AvailableSettings;
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 static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
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.GeneratorStrategies.mapLegacyNamedGenerator;
import static org.hibernate.id.IdentifierGenerator.GENERATOR_NAME;
import static org.hibernate.id.OptimizableGenerator.INCREMENT_PARAM;
/**
* @author Andrea Boriero
* SecondPass implementing delayed resolution of id-generators associated with an entity.
*
* @see StrictIdGeneratorResolverSecondPass
*
* @author Steve Ebersole
*/
public class IdGeneratorResolverSecondPass implements SecondPass {
private final SimpleValue id;
private final MemberDetails idAttributeMember;
private final String generatorType;
private final String generatorName;
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;
// private IdentifierGeneratorDefinition localIdentifierGeneratorDefinition;
public IdGeneratorResolverSecondPass(
SimpleValue id,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idMember,
GeneratedValue generatedValue,
MetadataBuildingContext buildingContext) {
this.id = id;
this.idAttributeMember = idAttributeMember;
this.generatorType = generatorType;
this.generatorName = generatorName;
this.entityMapping = entityMapping;
this.idValue = idValue;
this.idMember = idMember;
this.generatedValue = generatedValue;
this.buildingContext = buildingContext;
}
// public IdGeneratorResolverSecondPass(
// SimpleValue id,
// MemberDetails idAttributeMember,
// String generatorType,
// String generatorName,
// MetadataBuildingContext buildingContext,
// IdentifierGeneratorDefinition localIdentifierGeneratorDefinition) {
// this( id, idAttributeMember, generatorType, generatorName, buildingContext );
// this.localIdentifierGeneratorDefinition = localIdentifierGeneratorDefinition;
// }
@Override
public void doSecondPass(Map<String, PersistentClass> idGeneratorDefinitionMap) throws MappingException {
// makeIdGenerator( id, idAttributeMember, generatorType, generatorName, buildingContext, localIdentifierGeneratorDefinition );
makeIdGenerator( id, idAttributeMember, generatorType, generatorName, buildingContext, null );
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
switch ( generatedValue.strategy() ) {
case UUID -> GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext );
case IDENTITY -> GeneratorAnnotationHelper.handleIdentityStrategy( idValue, idMember, buildingContext );
case SEQUENCE -> handleSequenceStrategy();
case TABLE -> handleTableStrategy();
case AUTO -> handleAutoStrategy();
}
}
// public static void makeIdGenerator(
// SimpleValue id,
// MemberDetails idAttributeMember,
// String generatorType,
// String generatorName,
// MetadataBuildingContext buildingContext,
// IdentifierGeneratorDefinition foreignKGeneratorDefinition) {
// makeIdGenerator( id, idAttributeMember, generatorType, generatorName, buildingContext,
// foreignKGeneratorDefinition != null
// ? Map.of( foreignKGeneratorDefinition.getName(), foreignKGeneratorDefinition )
// : null );
// }
private void handleSequenceStrategy() {
if ( generatedValue.generator().isEmpty() ) {
handleUnnamedSequenceGenerator();
}
else {
handleNamedSequenceGenerator();
}
}
private void handleUnnamedSequenceGenerator() {
// todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"?
final SequenceGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idMember,
null,
null,
buildingContext
);
if ( localizedMatch != null ) {
handleSequenceGenerator( null, localizedMatch );
return;
}
handleSequenceGenerator( null, null );
}
private void handleNamedSequenceGenerator() {
final SequenceGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idMember,
SequenceGenerator::name,
generatedValue.generator(),
buildingContext
);
if ( localizedMatch != null ) {
handleSequenceGenerator( generatedValue.generator(), localizedMatch );
return;
}
// look for the matching global registration, if one.
final SequenceGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalMatch != null ) {
handleSequenceGenerator( generatedValue.generator(), globalMatch.configuration() );
return;
}
validateSequenceGeneration();
handleSequenceGenerator( generatedValue.generator(), null );
}
private void validateSequenceGeneration() {
// basically, make sure there is neither a TableGenerator nor GenericGenerator with this name
final TableGeneratorRegistration globalTableMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalTableMatch != null ) {
throw new MappingException(
String.format(
Locale.ROOT,
"@GeneratedValue for %s (%s) specified SEQUENCE generation, but referred to a @TableGenerator",
entityMapping.getEntityName(),
generatedValue.generator()
)
);
}
final GenericGeneratorRegistration globalGenericMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getGenericGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalGenericMatch != null ) {
throw new MappingException(
String.format(
Locale.ROOT,
"@GeneratedValue for %s (%s) specified SEQUENCE generation, but referred to a @GenericGenerator",
entityMapping.getEntityName(),
generatedValue.generator()
)
);
}
}
private void handleTableStrategy() {
if ( generatedValue.generator().isEmpty() ) {
handleUnnamedTableGenerator();
}
else {
handleNamedTableGenerator();
}
}
private void handleUnnamedTableGenerator() {
// todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"?
final TableGenerator localizedMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idMember,
null,
null,
buildingContext
);
if ( localizedMatch != null ) {
handleTableGenerator( null, localizedMatch );
return;
}
handleTableGenerator( null, null );
}
private void handleNamedTableGenerator() {
final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idMember,
TableGenerator::name,
generatedValue.generator(),
buildingContext
);
if ( localizedTableMatch != null ) {
handleTableGenerator( generatedValue.generator(), localizedTableMatch );
return;
}
// look for the matching global registration, if one.
final TableGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalMatch != null ) {
handleTableGenerator( generatedValue.generator(), globalMatch.configuration() );
return;
}
validateTableGeneration();
handleTableGenerator( generatedValue.generator(), null );
}
private void validateTableGeneration() {
// basically, make sure there is neither a SequenceGenerator nor a GenericGenerator with this name
final SequenceGeneratorRegistration globalSequenceMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalSequenceMatch != null ) {
throw new MappingException(
String.format(
Locale.ROOT,
"@GeneratedValue for %s (%s) specified TABLE generation, but referred to a @SequenceGenerator",
entityMapping.getEntityName(),
generatedValue.generator()
)
);
}
final GenericGeneratorRegistration globalGenericMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getGenericGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalGenericMatch != null ) {
throw new MappingException(
String.format(
Locale.ROOT,
"@GeneratedValue for %s (%s) specified TABLE generation, but referred to a @GenericGenerator",
entityMapping.getEntityName(),
generatedValue.generator()
)
);
}
}
private void handleAutoStrategy() {
if ( generatedValue.generator().isEmpty() ) {
handleUnnamedAutoGenerator();
}
else {
handleNamedAutoGenerator();
}
}
private void handleUnnamedAutoGenerator() {
// todo (7.0) : null or entityMapping.getJpaEntityName() for "name from GeneratedValue"?
final SequenceGenerator localizedSequenceMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idMember,
null,
null,
buildingContext
);
if ( localizedSequenceMatch != null ) {
handleSequenceGenerator( null, localizedSequenceMatch );
return;
}
final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idMember,
null,
null,
buildingContext
);
if ( localizedTableMatch != null ) {
handleTableGenerator( null, localizedTableMatch );
return;
}
final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch(
HibernateAnnotations.GENERIC_GENERATOR,
idMember,
null,
null,
buildingContext
);
if ( localizedGenericMatch != null ) {
GeneratorAnnotationHelper.handleGenericGenerator(
entityMapping.getJpaEntityName(),
localizedGenericMatch,
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
if ( idMember.getType().isImplementor( UUID.class )
|| idMember.getType().isImplementor( String.class ) ) {
GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext );
return;
}
handleSequenceGenerator( null, null );
}
private void handleNamedAutoGenerator() {
if ( handleAsLocalAutoGenerator() ) {
return;
}
if ( handleAsNamedGlobalAutoGenerator() ) {
return;
}
final Class<? extends Generator> legacyNamedGenerator = mapLegacyNamedGenerator( generatedValue.generator(), buildingContext );
if ( legacyNamedGenerator != null ) {
//generator settings
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatedValue.generator(), legacyNamedGenerator.getName() ),
idMember,
idValue,
entityMapping,
buildingContext
);
return;
}
final List<? extends Annotation> metaAnnotated = idMember.getMetaAnnotated( IdGeneratorType.class, buildingContext.getMetadataCollector().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 = GeneratorBinder.instantiateGenerator(
beanContainer,
markerAnnotation.value()
);
final Map<String,Object> configuration = new HashMap<>();
GeneratorParameters.collectParameters(
idValue,
buildingContext.getMetadataCollector().getDatabase().getDialect(),
entityMapping.getRootClass(),
configuration::put
);
callConfigure( creationContext, identifierGenerator, configuration, idValue );
return identifierGenerator;
} );
return;
}
if ( idMember.getType().isImplementor( UUID.class )
|| idMember.getType().isImplementor( String.class ) ) {
GeneratorAnnotationHelper.handleUuidStrategy( idValue, idMember, buildingContext );
return;
}
handleSequenceGenerator( generatedValue.generator(), null );
}
private boolean handleAsLocalAutoGenerator() {
assert !generatedValue.generator().isEmpty();
final SequenceGenerator localizedSequenceMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.SEQUENCE_GENERATOR,
idMember,
SequenceGenerator::name,
generatedValue.generator(),
buildingContext
);
if ( localizedSequenceMatch != null ) {
handleSequenceGenerator( generatedValue.generator(), localizedSequenceMatch );
return true;
}
final TableGenerator localizedTableMatch = GeneratorAnnotationHelper.findLocalizedMatch(
JpaAnnotations.TABLE_GENERATOR,
idMember,
TableGenerator::name,
generatedValue.generator(),
buildingContext
);
if ( localizedTableMatch != null ) {
handleTableGenerator( generatedValue.generator(), localizedTableMatch );
return true;
}
final GenericGenerator localizedGenericMatch = GeneratorAnnotationHelper.findLocalizedMatch(
HibernateAnnotations.GENERIC_GENERATOR,
idMember,
GenericGenerator::name,
generatedValue.generator(),
buildingContext
);
if ( localizedGenericMatch != null ) {
GeneratorAnnotationHelper.handleGenericGenerator(
generatedValue.generator(),
localizedGenericMatch,
entityMapping,
idValue,
idMember,
buildingContext
);
return true;
}
return false;
}
private boolean handleAsNamedGlobalAutoGenerator() {
final SequenceGeneratorRegistration globalSequenceMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalSequenceMatch != null ) {
handleSequenceGenerator( generatedValue.generator(), globalSequenceMatch.configuration() );
return true;
}
final TableGeneratorRegistration globalTableMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalTableMatch != null ) {
handleTableGenerator( generatedValue.generator(), globalTableMatch.configuration() );
return true;
}
final GenericGeneratorRegistration globalGenericMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getGenericGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalGenericMatch != null ) {
GeneratorAnnotationHelper.handleGenericGenerator(
generatedValue.generator(),
globalGenericMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return true;
}
return false;
}
private void handleSequenceGenerator(String nameFromGeneratedValue, SequenceGenerator generator) {
final Map<String, Object> configuration = extractConfiguration( nameFromGeneratedValue, generator );
createGeneratorFrom( SequenceStyleGenerator.class, configuration );
}
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, idValue, configuration::put );
}
return configuration;
}
private void applyCommonConfiguration(Map<String, Object> configuration, Annotation generatorAnnotation) {
GeneratorParameters.collectParameters(
idValue,
buildingContext.getMetadataCollector().getDatabase().getDialect(),
entityMapping.getRootClass(),
configuration::put
);
// 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 StandardServiceRegistry serviceRegistry = buildingContext.getBootstrapContext().getServiceRegistry();
final ConfigurationService configService = serviceRegistry.requireService( ConfigurationService.class );
final String idNamingStrategy = configService.getSetting(
AvailableSettings.ID_DB_STRUCTURE_NAMING_STRATEGY,
StandardConverters.STRING,
null
);
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) {
final Map<String, Object> configuration = extractConfiguration( nameFromGeneratedValue, generator );
createGeneratorFrom( org.hibernate.id.enhanced.TableGenerator.class, configuration );
}
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, idValue, 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 = GeneratorBinder.instantiateGenerator( beanContainer, generatorClass );
callConfigure( creationContext, identifierGenerator, configuration, idValue );
if ( identifierGenerator instanceof IdentityGenerator ) {
idValue.setColumnToIdentity();
}
// if we get here we have either a sequence or table generator,
// both of which are ExportableProducers
( (ExportableProducer) identifierGenerator ).registerExportables( creationContext.getDatabase() );
return identifierGenerator;
} );
}
}

View File

@ -683,7 +683,6 @@ public class PropertyBinder {
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
@ -721,7 +720,6 @@ public class PropertyBinder {
propertyHolder,
nullability,
inferredData,
classGenerators,
entityBinder,
isIdentifierMapper,
isComponentEmbedded,
@ -753,7 +751,6 @@ public class PropertyBinder {
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
@ -797,7 +794,6 @@ public class PropertyBinder {
propertyHolder,
nullability,
inferredData,
classGenerators,
entityBinder,
isIdentifierMapper,
isComponentEmbedded,
@ -816,7 +812,6 @@ public class PropertyBinder {
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
@ -879,7 +874,6 @@ public class PropertyBinder {
propertyHolder,
nullability,
inferredData,
classGenerators,
entityBinder,
isIdentifierMapper,
context,
@ -895,7 +889,6 @@ public class PropertyBinder {
propertyHolder,
nullability,
inferredData,
classGenerators,
entityBinder,
isIdentifierMapper,
isComponentEmbedded,
@ -996,7 +989,6 @@ public class PropertyBinder {
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
boolean isComponentEmbedded,
@ -1145,7 +1137,6 @@ public class PropertyBinder {
propertyHolder,
inferredData,
(SimpleValue) propertyBinder.getValue(),
classGenerators,
context
);
}

View File

@ -0,0 +1,365 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.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;
import org.hibernate.boot.models.spi.SequenceGeneratorRegistration;
import org.hibernate.boot.models.spi.TableGeneratorRegistration;
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.handleUuidStrategy;
import static org.hibernate.boot.model.internal.GeneratorParameters.identityTablesString;
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
* using strict JPA resolution - based mainly on global resolution of generator names,
* along with support for UUID and String member types with AUTO. We also account for
* legacy (un-configurable) named generators ({@code increment}, {@code uuid.hex}, etc.).
*
* @implNote For unnamed generators defined on the entity class or on the id member, this
* strategy will register a global registration using the entity's name and later look it
* up by that name. This more strictly follows the JPA specification where all generator
* names should be considered global and resolved globally.
*
* @see IdGeneratorResolverSecondPass
*
* @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 StrictIdGeneratorResolverSecondPass(
PersistentClass entityMapping,
SimpleValue idValue,
MemberDetails idMember,
MetadataBuildingContext buildingContext) {
this.entityMapping = entityMapping;
this.idValue = idValue;
this.idMember = idMember;
this.buildingContext = 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, idMember, buildingContext );
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() {
// according to the spec, this should locate a generator with the same name as the entity-name
final SequenceGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( entityMapping.getJpaEntityName() );
if ( globalMatch != null ) {
handleSequenceGenerator(
entityMapping.getJpaEntityName(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
handleSequenceGenerator(
entityMapping.getJpaEntityName(),
new SequenceGeneratorJpaAnnotation( buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleNamedSequenceGenerator(GeneratedValue generatedValue) {
final SequenceGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalMatch != null ) {
handleSequenceGenerator(
generatedValue.generator(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
handleSequenceGenerator(
generatedValue.generator(),
new SequenceGeneratorJpaAnnotation( generatedValue.generator(), buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleTableStrategy(GeneratedValue generatedValue) {
if ( generatedValue.generator().isEmpty() ) {
handleUnnamedTableGenerator();
}
else {
handleNamedTableGenerator( generatedValue );
}
}
private void handleUnnamedTableGenerator() {
final TableGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( entityMapping.getJpaEntityName() );
if ( globalMatch != null ) {
handleTableGenerator(
entityMapping.getJpaEntityName(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
handleTableGenerator(
entityMapping.getJpaEntityName(),
new TableGeneratorJpaAnnotation( buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleNamedTableGenerator(GeneratedValue generatedValue) {
final TableGeneratorRegistration globalMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( generatedValue.generator() );
if ( globalMatch != null ) {
handleTableGenerator(
generatedValue.generator(),
globalMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
handleTableGenerator(
generatedValue.generator(),
new TableGeneratorJpaAnnotation( generatedValue.generator(), buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
idValue,
idMember,
buildingContext
);
}
private void handleAutoStrategy(GeneratedValue generatedValue) {
final String globalRegistrationName;
if ( generatedValue.generator().isEmpty() ) {
globalRegistrationName = entityMapping.getJpaEntityName();
}
else {
globalRegistrationName = generatedValue.generator();
}
final SequenceGeneratorRegistration globalSequenceMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getSequenceGeneratorRegistrations()
.get( globalRegistrationName );
if ( globalSequenceMatch != null ) {
handleSequenceGenerator(
globalRegistrationName,
globalSequenceMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
final TableGeneratorRegistration globalTableMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getTableGeneratorRegistrations()
.get( globalRegistrationName );
if ( globalTableMatch != null ) {
handleTableGenerator(
globalRegistrationName,
globalTableMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
final GenericGeneratorRegistration globalGenericMatch = buildingContext.getMetadataCollector()
.getGlobalRegistrations()
.getGenericGeneratorRegistrations()
.get( globalRegistrationName );
if ( globalGenericMatch != null ) {
handleGenericGenerator(
globalRegistrationName,
globalGenericMatch.configuration(),
entityMapping,
idValue,
idMember,
buildingContext
);
return;
}
// Implicit handling of UUID generation
if ( idMember.getType().isImplementor( UUID.class )
|| idMember.getType().isImplementor( String.class )) {
handleUuidStrategy( idValue, idMember, buildingContext );
return;
}
// Handle a few legacy Hibernate generators...
if ( !generatedValue.generator().isEmpty() ) {
final Class<? extends Generator> legacyNamedGenerator = GeneratorStrategies.mapLegacyNamedGenerator( generatedValue.generator(), idValue );
if ( legacyNamedGenerator != null ) {
final Map<String,String> configuration = buildLegacyGeneratorConfig();
//noinspection unchecked,rawtypes
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatedValue.generator(), legacyNamedGenerator.getName(), configuration ),
idMember,
idValue,
(Map) configuration,
buildingContext
);
return;
}
}
handleSequenceGenerator(
globalRegistrationName,
new SequenceGeneratorJpaAnnotation( generatedValue.generator(), buildingContext.getMetadataCollector().getSourceModelBuildingContext() ),
entityMapping,
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, idValue, configuration::put );
}
GeneratorBinder.createGeneratorFrom(
new IdentifierGeneratorDefinition( generatorName, SequenceStyleGenerator.class.getName(), configuration ),
idMember,
idValue,
entityMapping,
context
);
}
}

View File

@ -28,7 +28,18 @@ public class SequenceGeneratorJpaAnnotation implements SequenceGenerator {
* Used in creating dynamic annotation instances (e.g. from XML)
*/
public SequenceGeneratorJpaAnnotation(SourceModelBuildingContext modelContext) {
this.name = "";
this( "", modelContext );
}
/**
* Used in creating named, defaulted annotation instances. Generally this
* is a situation where we have:<ol>
* <li>{@linkplain GeneratedValue#strategy()} set to {@linkplain jakarta.persistence.GenerationType#SEQUENCE}</li>
* <li>{@linkplain GeneratedValue#generator()} set to a non-empty String, but with no matching {@linkplain SequenceGenerator}</li>
* </ol>
*/
public SequenceGeneratorJpaAnnotation(String name, SourceModelBuildingContext modelContext) {
this.name = name;
this.sequenceName = "";
this.catalog = "";
this.schema = "";

View File

@ -43,7 +43,18 @@ public class TableGeneratorJpaAnnotation implements TableGenerator, UniqueConstr
* Used in creating dynamic annotation instances (e.g. from XML)
*/
public TableGeneratorJpaAnnotation(SourceModelBuildingContext modelContext) {
this.name = "";
this( "", modelContext );
}
/**
* Used in creating named, defaulted annotation instances. Generally this
* is a situation where we have:<ol>
* <li>{@linkplain GeneratedValue#strategy()} set to {@linkplain jakarta.persistence.GenerationType#TABLE}</li>
* <li>{@linkplain GeneratedValue#generator()} set to a non-empty String, but with no matching {@linkplain TableGenerator}</li>
* </ol>
*/
public TableGeneratorJpaAnnotation(String name, SourceModelBuildingContext modelContext) {
this.name = name;
this.table = "";
this.catalog = "";
this.schema = "";

View File

@ -117,9 +117,7 @@ public class DomainModelCategorizationCollector {
getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( classDetails );
getGlobalRegistrations().collectFilterDefinitions( classDetails );
if ( areIdGeneratorsGlobal ) {
getGlobalRegistrations().collectIdGenerators( classDetails );
}
getGlobalRegistrations().collectIdGenerators( classDetails );
getGlobalRegistrations().collectImportRename( classDetails );

View File

@ -11,10 +11,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.hibernate.MappingException;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Imported;
import org.hibernate.annotations.ParamDef;
@ -56,6 +58,7 @@ import org.hibernate.boot.models.spi.ConverterRegistration;
import org.hibernate.boot.models.spi.EmbeddableInstantiatorRegistration;
import org.hibernate.boot.models.spi.FilterDefRegistration;
import org.hibernate.boot.models.spi.GenericGeneratorRegistration;
import org.hibernate.boot.models.spi.GlobalRegistrar;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.spi.JavaTypeRegistration;
import org.hibernate.boot.models.spi.JdbcTypeRegistration;
@ -81,12 +84,15 @@ import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.UserCollectionType;
import org.hibernate.usertype.UserType;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Entity;
import jakarta.persistence.GenerationType;
import jakarta.persistence.QueryHint;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
@ -102,6 +108,7 @@ import static org.hibernate.boot.models.HibernateAnnotations.FILTER_DEF;
import static org.hibernate.boot.models.HibernateAnnotations.GENERIC_GENERATOR;
import static org.hibernate.boot.models.HibernateAnnotations.JAVA_TYPE_REGISTRATION;
import static org.hibernate.boot.models.HibernateAnnotations.JDBC_TYPE_REGISTRATION;
import static org.hibernate.boot.models.JpaAnnotations.ENTITY;
import static org.hibernate.boot.models.JpaAnnotations.NAMED_STORED_PROCEDURE_QUERY;
import static org.hibernate.boot.models.JpaAnnotations.SEQUENCE_GENERATOR;
import static org.hibernate.boot.models.JpaAnnotations.TABLE_GENERATOR;
@ -113,7 +120,7 @@ import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
/**
* @author Steve Ebersole
*/
public class GlobalRegistrationsImpl implements GlobalRegistrations {
public class GlobalRegistrationsImpl implements GlobalRegistrations, GlobalRegistrar {
private final SourceModelBuildingContext sourceModelContext;
private final BootstrapContext bootstrapContext;
@ -144,6 +151,12 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
this.bootstrapContext = bootstrapContext;
}
@Override
public <T> T as(Class<T> type) {
//noinspection unchecked
return (T) this;
}
@Override
public List<JpaEventListener> getEntityListenerRegistrations() {
return jpaEventListeners == null ? emptyList() : jpaEventListeners;
@ -691,15 +704,20 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
}
public void collectIdGenerators(ClassDetails classDetails) {
if ( !classDetails.getName().endsWith( ".package-info" )
&& !bootstrapContext.getJpaCompliance().isGlobalGeneratorScopeEnabled() ) {
return;
}
classDetails.forEachRepeatedAnnotationUsages(
SEQUENCE_GENERATOR,
sourceModelContext,
this::collectSequenceGenerator
(sequenceGenerator) -> collectSequenceGenerator( classDetails, sequenceGenerator )
);
classDetails.forEachAnnotationUsage(
TABLE_GENERATOR,
sourceModelContext,
this::collectTableGenerator
tableGenerator -> collectTableGenerator( classDetails, tableGenerator )
);
classDetails.forEachAnnotationUsage(
GENERIC_GENERATOR,
@ -708,6 +726,51 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
);
}
@Override
public void collectIdGenerators(MemberDetails memberDetails) {
if ( !bootstrapContext.getJpaCompliance().isGlobalGeneratorScopeEnabled() ) {
return;
}
memberDetails.forEachRepeatedAnnotationUsages(
SEQUENCE_GENERATOR,
sourceModelContext,
(sequenceGenerator) -> collectSequenceGenerator( memberDetails, sequenceGenerator )
);
memberDetails.forEachAnnotationUsage(
TABLE_GENERATOR,
sourceModelContext,
tableGenerator -> collectTableGenerator( memberDetails, tableGenerator )
);
memberDetails.forEachAnnotationUsage(
GENERIC_GENERATOR,
sourceModelContext,
this::collectGenericGenerator
);
}
/**
* Account for implicit naming of sequence and table generators when applied to an entity class per JPA 3.2
*/
private String determineImplicitGeneratorNameBase(ClassDetails classDetails, GenerationType generationType) {
if ( classDetails.getName().endsWith( ".package-info" ) ) {
throw new MappingException( String.format(
Locale.ROOT,
"@%s placed on package (%s) specified no name",
generationType == GenerationType.SEQUENCE ? SequenceGenerator.class.getSimpleName() : TableGenerator.class.getSimpleName(),
StringHelper.qualifier( classDetails.getName() )
) );
}
final Entity entityAnnotation = classDetails.getDirectAnnotationUsage( ENTITY );
if ( entityAnnotation != null ) {
final String explicitEntityName = entityAnnotation.name();
return StringHelper.isNotEmpty( explicitEntityName )
? explicitEntityName
: StringHelper.unqualify( classDetails.getName() );
}
return null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Sequence generator
@ -752,17 +815,35 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
} );
}
public void collectSequenceGenerator(SequenceGenerator usage) {
collectSequenceGenerator( new SequenceGeneratorRegistration( usage.name(), usage ) );
public void collectSequenceGenerator(MemberDetails memberDetails, SequenceGenerator usage) {
collectSequenceGenerator( memberDetails.getDeclaringType(), usage );
}
public void collectSequenceGenerator(ClassDetails classDetails, SequenceGenerator usage) {
final String registrationName;
if ( !usage.name().isEmpty() ) {
registrationName = usage.name();
}
else {
final Entity entityAnnotation = classDetails.getDirectAnnotationUsage( Entity.class );
if ( entityAnnotation != null && !entityAnnotation.name().isEmpty() ) {
registrationName = entityAnnotation.name();
}
else {
registrationName = StringHelper.unqualify( classDetails.getName() );
}
}
collectSequenceGenerator( new SequenceGeneratorRegistration( registrationName, usage ) );
}
public void collectSequenceGenerator(SequenceGeneratorRegistration generatorRegistration) {
checkGeneratorName( generatorRegistration.name() );
if ( sequenceGeneratorRegistrations == null ) {
sequenceGeneratorRegistrations = new HashMap<>();
}
sequenceGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}
@ -842,16 +923,57 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
} );
}
public void collectTableGenerator(TableGenerator usage) {
collectTableGenerator( new TableGeneratorRegistration( usage.name(), usage ) );
public void collectTableGenerator(ClassDetails classDetails, TableGenerator usage) {
final TableGeneratorJpaAnnotation generatorAnnotation = new TableGeneratorJpaAnnotation(
usage,
sourceModelContext
);
final Entity entityAnnotation = classDetails.getDirectAnnotationUsage( Entity.class );
final String simpleName;
if ( entityAnnotation != null && !entityAnnotation.name().isEmpty() ) {
simpleName = entityAnnotation.name();
}
else {
simpleName = StringHelper.unqualify( classDetails.getName() );
}
final String registrationName;
if ( !usage.name().isEmpty() ) {
registrationName = usage.name();
}
else {
registrationName = simpleName;
}
generatorAnnotation.name( registrationName );
collectTableGenerator( new TableGeneratorRegistration( registrationName, usage ) );
}
public void collectTableGenerator(MemberDetails memberDetails, TableGenerator usage) {
final String registrationName;
if ( !usage.name().isEmpty() ) {
registrationName = usage.name();
}
else {
final Entity entityAnnotation = memberDetails.getDeclaringType().getDirectAnnotationUsage( Entity.class );
if ( entityAnnotation != null && !entityAnnotation.name().isEmpty() ) {
registrationName = entityAnnotation.name();
}
else {
registrationName = StringHelper.unqualify( memberDetails.getDeclaringType().getName() );
}
}
collectTableGenerator( new TableGeneratorRegistration( registrationName, usage ) );
}
public void collectTableGenerator(TableGeneratorRegistration generatorRegistration) {
checkGeneratorName( generatorRegistration.name() );
if ( tableGeneratorRegistrations == null ) {
tableGeneratorRegistrations = new HashMap<>();
}
tableGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}
@ -879,6 +1001,9 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
}
public void collectGenericGenerator(GenericGenerator usage) {
if ( usage.name().isEmpty() ) {
return;
}
collectGenericGenerator( new GenericGeneratorRegistration( usage.name(), usage ) );
}
@ -888,7 +1013,6 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
if ( genericGeneratorRegistrations == null ) {
genericGeneratorRegistrations = new HashMap<>();
}
genericGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}

View File

@ -0,0 +1,16 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.models.spi;
import org.hibernate.models.spi.MemberDetails;
/**
* @author Steve Ebersole
*/
public interface GlobalRegistrar {
void collectIdGenerators(MemberDetails memberDetails);
}

View File

@ -55,4 +55,6 @@ public interface GlobalRegistrations {
Map<String, NamedStoredProcedureQueryRegistration> getNamedStoredProcedureQueryRegistrations();
// todo : named entity graphs
<T> T as(Class<T> type);
}

View File

@ -10,6 +10,7 @@ import java.lang.invoke.MethodHandles;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.function.BiConsumer;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
@ -25,12 +26,14 @@ import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.GeneratorCreationContext;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceMismatchStrategy;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.schema.Action;
import org.hibernate.tool.schema.extract.spi.SequenceInformation;
@ -39,6 +42,8 @@ import org.hibernate.type.Type;
import org.jboss.logging.Logger;
import jakarta.persistence.SequenceGenerator;
import static java.util.Collections.singleton;
import static org.hibernate.id.IdentifierGeneratorHelper.getNamingStrategy;
import static org.hibernate.id.enhanced.OptimizerFactory.determineImplicitOptimizerName;
@ -110,7 +115,7 @@ import static org.hibernate.internal.util.config.ConfigurationHelper.getString;
* @author Lukasz Antoniak
*/
public class SequenceStyleGenerator
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator {
implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, BeforeExecutionGenerator {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
MethodHandles.lookup(),
@ -583,4 +588,27 @@ public class SequenceStyleGenerator
return ( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) )
&& ( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
}
public static void applyConfiguration(SequenceGenerator generatorConfig, SimpleValue idValue, BiConsumer<String,String> configCollector) {
if ( !generatorConfig.sequenceName().isEmpty() ) {
configCollector.accept( SEQUENCE_PARAM, generatorConfig.sequenceName() );
}
if ( !generatorConfig.catalog().isEmpty() ) {
configCollector.accept( CATALOG, generatorConfig.catalog() );
}
if ( !generatorConfig.schema().isEmpty() ) {
configCollector.accept( SCHEMA, generatorConfig.schema() );
}
if ( !generatorConfig.options().isEmpty() ) {
configCollector.accept( OPTIONS, generatorConfig.options() );
}
configCollector.accept( INITIAL_PARAM, Integer.toString( generatorConfig.initialValue() ) );
if ( generatorConfig.allocationSize() == 50 ) {
// don't do anything - assuming a proper default is already set
}
else {
configCollector.accept( INCREMENT_PARAM, Integer.toString( generatorConfig.allocationSize() ) );
}
}
}

View File

@ -14,6 +14,7 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Map;
import java.util.Properties;
import java.util.function.BiConsumer;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -43,9 +44,11 @@ import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.BasicTypeRegistry;
@ -55,6 +58,7 @@ import org.hibernate.type.Type;
import org.jboss.logging.Logger;
import static java.util.Collections.singletonMap;
import static org.hibernate.boot.model.internal.GeneratorBinder.applyIfNotEmpty;
import static org.hibernate.id.IdentifierGeneratorHelper.getNamingStrategy;
import static org.hibernate.id.enhanced.OptimizerFactory.determineImplicitOptimizerName;
import static org.hibernate.internal.util.StringHelper.isEmpty;
@ -225,6 +229,7 @@ public class TableGenerator implements PersistentIdentifierGenerator {
private String contributor;
private String options;
/**
* Type mapping for the identifier.
*
@ -723,4 +728,35 @@ public class TableGenerator implements PersistentIdentifierGenerator {
updateQuery = buildUpdateQuery( formattedPhysicalTableName, context );
insertQuery = buildInsertQuery( formattedPhysicalTableName, context );
}
public static void applyConfiguration(
jakarta.persistence.TableGenerator generatorConfig,
SimpleValue idValue,
Map<String, String> configuration) {
applyConfiguration( generatorConfig, idValue, configuration::put );
}
public static void applyConfiguration(
jakarta.persistence.TableGenerator generatorConfig,
SimpleValue idValue,
BiConsumer<String, String> configurationCollector) {
configurationCollector.accept( CONFIG_PREFER_SEGMENT_PER_ENTITY, "true" );
applyIfNotEmpty( TABLE_PARAM, generatorConfig.table(), configurationCollector );
applyIfNotEmpty( CATALOG, generatorConfig.catalog(), configurationCollector );
applyIfNotEmpty( SCHEMA, generatorConfig.schema(), configurationCollector );
applyIfNotEmpty( OPTIONS, generatorConfig.options(), configurationCollector );
applyIfNotEmpty( SEGMENT_COLUMN_PARAM, generatorConfig.pkColumnName(), configurationCollector );
applyIfNotEmpty( SEGMENT_VALUE_PARAM, generatorConfig.pkColumnValue(), configurationCollector );
applyIfNotEmpty( VALUE_COLUMN_PARAM, generatorConfig.valueColumnName(), configurationCollector );
configurationCollector.accept( INITIAL_PARAM, Integer.toString( generatorConfig.initialValue() + 1 ) );
if ( generatorConfig.allocationSize() == 50 ) {
// don't do anything - assuming a proper default is already set
}
else {
configurationCollector.accept( INCREMENT_PARAM, Integer.toString( generatorConfig.allocationSize() ) );
}
}
}

View File

@ -19,7 +19,7 @@ import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
import org.hibernate.generator.EventTypeSets;
import org.hibernate.generator.GeneratorCreationContext;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.descriptor.java.UUIDJavaType;
import org.hibernate.type.descriptor.java.UUIDJavaType.ValueTransformer;
@ -49,7 +49,47 @@ public class UuidGenerator implements BeforeExecutionGenerator {
valueTransformer = determineProperTransformer( memberType );
}
private UuidGenerator(
/**
* This form is used when there is no {@code @UuidGenerator} but we know we want this generator
*/
@Internal
public UuidGenerator(
org.hibernate.annotations.UuidGenerator config,
MemberDetails memberDetails) {
generator = determineValueGenerator( config, memberDetails );
final Class<?> memberType = memberDetails.getType().determineRawClass().toJavaClass();
valueTransformer = determineProperTransformer( memberType );
}
private static UuidValueGenerator determineValueGenerator(
org.hibernate.annotations.UuidGenerator config,
MemberDetails memberDetails) {
if ( config != null ) {
if ( config.algorithm() != UuidValueGenerator.class ) {
if ( config.style() != AUTO ) {
throw new MappingException(
String.format(
Locale.ROOT,
"Style [%s] should not be specified with custom UUID value generator : %s.%s",
config.style().name(),
memberDetails.getDeclaringType().getName(),
memberDetails.getName()
)
);
}
return instantiateCustomGenerator( config.algorithm() );
}
else if ( config.style() == TIME ) {
return new CustomVersionOneStrategy();
}
}
return StandardRandomStrategy.INSTANCE;
}
@Internal
public UuidGenerator(
org.hibernate.annotations.UuidGenerator config,
Member idMember) {
if ( config.algorithm() != UuidValueGenerator.class ) {

View File

@ -570,6 +570,18 @@ public final class ConfigurationHelper {
.getPreferredSqlTypeCodeForArray();
}
public static void setIfNotEmpty(String value, String settingName, Map<String, String> configuration) {
if ( StringHelper.isNotEmpty( value ) ) {
configuration.put( settingName, value );
}
}
public static void setIfNotNull(Object value, String settingName, Map<String, Object> configuration) {
if ( value != null ) {
configuration.put( settingName, value );
}
}
private static class TypeCodeConverter implements ConfigurationService.Converter<Integer> {
public static final TypeCodeConverter INSTANCE = new TypeCodeConverter();

View File

@ -87,7 +87,7 @@ public class IdClassAndAssociationsTest {
@Table(name = "course_enrollment")
public static class CourseEnrollment {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "course")
@ -123,7 +123,7 @@ public class IdClassAndAssociationsTest {
@Table(name = "units")
public static class Unit {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
@ -140,7 +140,7 @@ public class IdClassAndAssociationsTest {
@Table(name = "users")
public static class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@Column(name = "first_name")

View File

@ -12,6 +12,8 @@ import org.hibernate.id.enhanced.NoopOptimizer;
import org.hibernate.id.enhanced.PooledOptimizer;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.orm.test.annotations.id.generationmappings.sub.DedicatedSequenceEntity1;
import org.hibernate.orm.test.annotations.id.generationmappings.sub.DedicatedSequenceEntity2;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.DomainModel;
@ -130,7 +132,8 @@ public class NewGeneratorMappingsTest {
@JiraKey(value = "HHH-6790")
public void testSequencePerEntity(SessionFactoryScope scope) {
// Checking first entity.
EntityPersister persister = scope.getSessionFactory().getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(DedicatedSequenceEntity1.class.getName());
EntityPersister persister = scope.getSessionFactory().getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(
DedicatedSequenceEntity1.class.getName());
IdentifierGenerator generator = persister.getIdentifierGenerator();
assertTrue( SequenceStyleGenerator.class.isInstance( generator ) );
SequenceStyleGenerator seqGenerator = (SequenceStyleGenerator) generator;

View File

@ -2,9 +2,9 @@
* 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>.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.annotations.id.generationmappings;
package org.hibernate.orm.test.annotations.id.generationmappings.sub;
import java.io.Serializable;
import jakarta.persistence.Entity;

View File

@ -2,9 +2,9 @@
* 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>.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.annotations.id.generationmappings;
package org.hibernate.orm.test.annotations.id.generationmappings.sub;
import java.io.Serializable;
import jakarta.persistence.Entity;

View File

@ -2,7 +2,7 @@
* 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>.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
@GenericGenerator(name = "SequencePerEntityGenerator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
@ -12,7 +12,7 @@
}
)
package org.hibernate.orm.test.annotations.id.generationmappings;
package org.hibernate.orm.test.annotations.id.generationmappings.sub;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

View File

@ -0,0 +1,141 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.annotations.id.generators;
import java.util.UUID;
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;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class SimpleIdTests {
@Test
@DomainModel(annotatedClasses = Entity1.class)
@SessionFactory
void testSimpleAutoFallback(SessionFactoryScope sessions) {
sessions.inTransaction( (session) -> {
session.persist( new Entity1( "1" ) );
} );
}
@Test
@DomainModel(annotatedClasses = Entity2.class)
@SessionFactory
void testSimpleTableFallback(SessionFactoryScope sessions) {
sessions.inTransaction( (session) -> {
session.persist( new Entity2( "2" ) );
} );
}
@Test
@DomainModel(annotatedClasses = Entity3.class)
@SessionFactory
void testSimpleNamedGenerator(SessionFactoryScope sessions) {
sessions.inTransaction( (session) -> {
session.persist( new Entity3( "3" ) );
} );
}
@Test
@DomainModel(annotatedClasses = Entity4.class)
@SessionFactory
void testSimpleUuidFallback(SessionFactoryScope sessions) {
sessions.inTransaction( (session) -> {
session.persist( new Entity4( "4" ) );
} );
}
@Test
@DomainModel(annotatedClasses = Entity5.class)
@SessionFactory
void testSimpleUuidAsStringFallback(SessionFactoryScope sessions) {
sessions.inTransaction( (session) -> {
session.persist( new Entity5( "5" ) );
} );
}
@Entity(name="Entity1")
@Table(name="Entity1")
public static class Entity1 {
@Id
@GeneratedValue
private Integer id;
private String name;
public Entity1() {
}
public Entity1(String name) {
this.name = name;
}
}
@Entity(name="Entity2")
@Table(name="Entity2")
public static class Entity2 {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
private String name;
public Entity2() {
}
public Entity2(String name) {
this.name = name;
}
}
@Entity(name="Entity3")
@Table(name="Entity3")
public static class Entity3 {
@Id
@GeneratedValue(generator = "increment")
private Integer id;
private String name;
public Entity3() {
}
public Entity3(String name) {
this.name = name;
}
}
@Entity(name="Entity4")
@Table(name="Entity4")
public static class Entity4 {
@Id
@GeneratedValue
private UUID id;
private String name;
public Entity4() {
}
public Entity4(String name) {
this.name = name;
}
}
@Entity(name="Entity5")
@Table(name="Entity5")
public static class Entity5 {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
private String name;
public Entity5() {
}
public Entity5(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,177 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.annotations.id.generators;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.generator.Generator;
import org.hibernate.mapping.RootClass;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
public class UnnamedGeneratorTests {
@Test
void testAutoWithNoGenerator() {
checkSequence( Entity1.class, "Entity1_seq" );
}
@Test
void testAutoWithSequenceGenerator() {
checkSequence( Entity2.class, "Entity2_seq" );
checkSequence( Entity3.class, "my_seq" );
checkSequence( Entity4.class, "another_seq" );
}
@Test
void testAutoWithTableGenerator() {
checkTableGenerator( Entity5.class, "hibernate_sequences", "Entity5" );
checkTableGenerator( Entity6.class, "my_sequences", "Entity6" );
checkTableGenerator( Entity7.class, "sequences_table", "Entity7" );
checkTableGenerator( Entity8.class, "sequences_table", "ent_8" );
}
private void checkSequence(Class<?> entityClass, String expectedDbName) {
// strictly-global = false
withGenerator( entityClass, false, (generator) -> {
final String name = SEQUENCE_NAME_EXTRACTOR.apply( generator );
assertThat( name ).isEqualToIgnoringCase( expectedDbName );
} );
// strictly-global = true
withGenerator( entityClass, true, (generator) -> {
final String name = SEQUENCE_NAME_EXTRACTOR.apply( generator );
assertThat( name ).isEqualToIgnoringCase( expectedDbName );
} );
}
private void checkTableGenerator(Class<?> entityClass, String expectedTableName, String expectedSegmentName) {
// strictly-global = false
withGenerator( entityClass, false, (generator) -> {
final org.hibernate.id.enhanced.TableGenerator tableGenerator = (org.hibernate.id.enhanced.TableGenerator) generator;
assertThat( tableGenerator.getTableName() ).isEqualToIgnoringCase( expectedTableName );
assertThat( tableGenerator.getSegmentValue() ).isEqualTo( expectedSegmentName );
} );
// strictly-global = true
withGenerator( entityClass, true, (generator) -> {
final org.hibernate.id.enhanced.TableGenerator tableGenerator = (org.hibernate.id.enhanced.TableGenerator) generator;
assertThat( tableGenerator.getTableName() ).isEqualToIgnoringCase( expectedTableName );
assertThat( tableGenerator.getSegmentValue() ).isEqualTo( expectedSegmentName );
} );
}
private void withGenerator(Class<?> entityClass, boolean strictlyGlobal, Consumer<Generator> checks) {
try (StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE, strictlyGlobal )
.build()) {
final Metadata metadata = new MetadataSources( serviceRegistry )
.addAnnotatedClasses( entityClass )
.buildMetadata();
final RootClass entityBinding = metadata.getEntityBinding( entityClass.getName() ).getRootClass();
final Generator generator = entityBinding.getIdentifier().createGenerator(
metadata.getDatabase().getDialect(),
entityBinding,
entityBinding.getIdentifierProperty()
);
checks.accept( generator );
}
}
@Entity(name="Entity1")
public static class Entity1 {
@Id
@GeneratedValue
private Integer id;
private String name;
}
@Entity(name="Entity2")
public static class Entity2 {
@Id
@GeneratedValue
@SequenceGenerator
private Integer id;
private String name;
}
@Entity(name="Entity3")
public static class Entity3 {
@Id
@GeneratedValue
@SequenceGenerator(sequenceName = "my_seq")
private Integer id;
private String name;
}
@Entity(name="Entity4")
@SequenceGenerator(sequenceName = "another_seq")
public static class Entity4 {
@Id
@GeneratedValue
private Integer id;
private String name;
}
@Entity(name="Entity5")
public static class Entity5 {
@Id
@GeneratedValue
@TableGenerator
private Integer id;
private String name;
}
@Entity(name="Entity6")
public static class Entity6 {
@Id
@GeneratedValue
@TableGenerator(table = "my_sequences")
private Integer id;
private String name;
}
@Entity(name="Entity7")
@TableGenerator(table = "sequences_table")
public static class Entity7 {
@Id
@GeneratedValue
private Integer id;
private String name;
}
@Entity(name="Entity8")
@TableGenerator(table = "sequences_table", pkColumnValue = "ent_8")
public static class Entity8 {
@Id
@GeneratedValue
private Integer id;
private String name;
}
private static final Function<Generator,String> SEQUENCE_NAME_EXTRACTOR = (generator) -> {
return ( (org.hibernate.id.enhanced.SequenceStyleGenerator) generator ).getDatabaseStructure().getPhysicalName().getObjectName().getText();
};
}

View File

@ -1,45 +1,49 @@
package org.hibernate.orm.test.annotations.id.generators.entity;
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;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SessionFactory
@DomainModel(annotatedClasses =
{ClassLevelGeneratorTest.EntityWithAnonSequenceGenerator.class,
ClassLevelGeneratorTest.EntityWithAnonTableGenerator.class})
@FailureExpected( reason = "Support for unnamed generators is not implemented yet" )
@Jira( "https://hibernate.atlassian.net/browse/HHH-18498" )
@DomainModel(
annotatedClasses = {
ClassLevelGeneratorTest.EntityWithAnonSequenceGenerator.class,
ClassLevelGeneratorTest.EntityWithAnonTableGenerator.class
}
)
public class ClassLevelGeneratorTest {
@Test
void testAnonGenerator(SessionFactoryScope scope) {
// this won't work with global scoping due to
assertThat( scope.getSessionFactory().getSessionFactoryOptions().getJpaCompliance().isGlobalGeneratorScopeEnabled() ).isFalse();
scope.inSession(s-> {
EntityWithAnonSequenceGenerator entity1 = new EntityWithAnonSequenceGenerator();
EntityWithAnonTableGenerator entity2 = new EntityWithAnonTableGenerator();
s.persist(entity1);
s.persist(entity2);
assertEquals(42, entity1.id);
assertEquals(69, entity2.id);
assertEquals(70, entity2.id);
});
}
@Entity
@Entity(name = "EntityWithAnonSequenceGenerator")
@SequenceGenerator(initialValue = 42)
static class EntityWithAnonSequenceGenerator {
@Id
@GeneratedValue
long id;
}
@Entity
@Entity(name = "EntityWithAnonTableGenerator")
@TableGenerator(initialValue = 69)
static class EntityWithAnonTableGenerator {
@Id

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.orm.test.annotations.id.generators.entity;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SessionFactory
@ServiceRegistry(settings = @Setting(name= AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE, value = "true"))
@DomainModel(annotatedClasses = {
DefaultedGeneratorWithGlobalScopeTest.EntityWithAnonSequenceGenerator.class,
DefaultedGeneratorWithGlobalScopeTest.EntityWithAnonTableGenerator.class
})
public class DefaultedGeneratorWithGlobalScopeTest {
@Test
void testAnonGenerator(SessionFactoryScope scope) {
scope.inSession(s-> {
EntityWithAnonSequenceGenerator entity1 = new EntityWithAnonSequenceGenerator();
EntityWithAnonTableGenerator entity2 = new EntityWithAnonTableGenerator();
s.persist(entity1);
s.persist(entity2);
assertEquals(42, entity1.id);
assertEquals(70, entity2.id);
});
}
@Entity(name = "EntityWithAnonSequenceGenerator")
@SequenceGenerator(initialValue = 42)
static class EntityWithAnonSequenceGenerator {
@Id
@GeneratedValue
long id;
}
@Entity(name = "EntityWithAnonTableGenerator")
@TableGenerator(initialValue = 69)
static class EntityWithAnonTableGenerator {
@Id
@GeneratedValue
long id;
}
}

View File

@ -18,8 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@DomainModel(annotatedClasses =
{FieldLevelGeneratorTest.EntityWithAnonSequenceGenerator.class,
FieldLevelGeneratorTest.EntityWithAnonTableGenerator.class})
@FailureExpected( reason = "Support for unnamed generators is not implemented yet" )
@Jira( "https://hibernate.atlassian.net/browse/HHH-18498" )
public class FieldLevelGeneratorTest {
@Test
void testAnonGenerator(SessionFactoryScope scope) {
@ -29,17 +27,19 @@ public class FieldLevelGeneratorTest {
s.persist(entity1);
s.persist(entity2);
assertEquals(42, entity1.id);
assertEquals(69, entity2.id);
assertEquals(70, entity2.id);
});
}
@Entity
@Entity(name = "EntityWithAnonSequenceGenerator")
static class EntityWithAnonSequenceGenerator {
@Id
@GeneratedValue
@SequenceGenerator(initialValue = 42)
long id;
}
@Entity
@Entity(name = "EntityWithAnonTableGenerator")
static class EntityWithAnonTableGenerator {
@Id
@GeneratedValue

View File

@ -2,6 +2,7 @@ package org.hibernate.orm.test.annotations.id.generators.pkg;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
@ -13,33 +14,52 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SessionFactory
@DomainModel(annotatedClasses =
{PackageLevelGeneratorTest.EntityWithAnonSequenceGenerator.class,
PackageLevelGeneratorTest.EntityWithAnonTableGenerator.class})
@FailureExpected( reason = "Support for unnamed generators is not implemented yet" )
@Jira( "https://hibernate.atlassian.net/browse/HHH-18498" )
@DomainModel(annotatedClasses = {
PackageLevelGeneratorTest.EntityWithDefaultedPackageGenerator.class,
PackageLevelGeneratorTest.EntityWithDefaultedPackageGenerator2.class,
PackageLevelGeneratorTest.EntityWithDefaultedPackageGenerator3.class,
PackageLevelGeneratorTest.EntityWithDefaultedPackageGenerator4.class
})
public class PackageLevelGeneratorTest {
@Test
void testAnonGenerator(SessionFactoryScope scope) {
scope.inSession(s-> {
EntityWithAnonSequenceGenerator entity1 = new EntityWithAnonSequenceGenerator();
EntityWithAnonTableGenerator entity2 = new EntityWithAnonTableGenerator();
EntityWithDefaultedPackageGenerator entity1 = new EntityWithDefaultedPackageGenerator();
EntityWithDefaultedPackageGenerator2 entity2 = new EntityWithDefaultedPackageGenerator2();
EntityWithDefaultedPackageGenerator3 entity3 = new EntityWithDefaultedPackageGenerator3();
EntityWithDefaultedPackageGenerator4 entity4 = new EntityWithDefaultedPackageGenerator4();
s.persist(entity1);
s.persist(entity2);
s.persist(entity3);
s.persist(entity4);
assertEquals(42, entity1.id);
assertEquals(69, entity2.id);
assertEquals(42, entity2.id);
assertEquals(42, entity3.id);
assertEquals(70, entity4.id);
});
}
@Entity
static class EntityWithAnonSequenceGenerator {
@Entity(name="EntityWithDefaultedPackageGenerator")
static class EntityWithDefaultedPackageGenerator {
@Id
@GeneratedValue
long id;
}
@Entity
static class EntityWithAnonTableGenerator {
@Entity(name = "EntityWithDefaultedPackageGenerator2")
static class EntityWithDefaultedPackageGenerator2 {
@Id
@GeneratedValue
long id;
}
@Entity(name = "EntityWithDefaultedPackageGenerator3")
static class EntityWithDefaultedPackageGenerator3 {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
long id;
}
@Entity(name = "EntityWithDefaultedPackageGenerator4")
static class EntityWithDefaultedPackageGenerator4 {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
long id;
}
}

View File

@ -14,8 +14,8 @@ import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.orm.test.annotations.id.generationmappings.DedicatedSequenceEntity1;
import org.hibernate.orm.test.annotations.id.generationmappings.DedicatedSequenceEntity2;
import org.hibernate.orm.test.annotations.id.generationmappings.sub.DedicatedSequenceEntity1;
import org.hibernate.orm.test.annotations.id.generationmappings.sub.DedicatedSequenceEntity2;
import org.hibernate.orm.test.annotations.id.sequences.entities.Ball;
import org.hibernate.orm.test.annotations.id.sequences.entities.BreakDance;
import org.hibernate.orm.test.annotations.id.sequences.entities.Computer;

View File

@ -1130,7 +1130,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithDefaultQualifiersWithTableGenerator {
public static final String NAME = "EntityWithDefaultQualifiersWithTableGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(strategy = GenerationType.TABLE, generator = NAME + "_generator")
@TableGenerator(name = NAME + "_generator", table = NAME + "_tableseq")
private Long id;
@Basic
@ -1142,7 +1142,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithExplicitQualifiersWithTableGenerator {
public static final String NAME = "EntityWithExplicitQualifiersWithTableGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(strategy = GenerationType.TABLE, generator = NAME + "_generator")
@TableGenerator(name = NAME + "_generator", table = NAME + "_tableseq",
catalog = EXPLICIT_CATALOG, schema = EXPLICIT_SCHEMA)
private Long id;
@ -1155,7 +1155,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithDefaultQualifiersWithIncrementGenerator {
public static final String NAME = "EntityWithDefaultQualifiersWithIncrementGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(generator = NAME + "_generator")
@GenericGenerator(name = NAME + "_generator", strategy = "increment")
private Long id;
@Basic
@ -1167,7 +1167,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithExplicitQualifiersWithIncrementGenerator {
public static final String NAME = "EntityWithExplicitQualifiersWithIncrementGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(generator = NAME + "_generator")
@GenericGenerator(name = NAME + "_generator", strategy = "increment", parameters = {
@Parameter(name = "catalog", value = EXPLICIT_CATALOG),
@Parameter(name = "schema", value = EXPLICIT_SCHEMA)
@ -1181,7 +1181,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithDefaultQualifiersWithEnhancedSequenceGenerator {
public static final String NAME = "EntityWithDefaultQualifiersWithEnhancedSequenceGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(generator = NAME + "_generator")
@GenericGenerator(name = NAME + "_generator", strategy = "enhanced-sequence", parameters = {
@Parameter(name = "sequence_name", value = NAME + "_seq")
})
@ -1195,7 +1195,7 @@ public class DefaultCatalogAndSchemaTest {
public static class EntityWithExplicitQualifiersWithEnhancedSequenceGenerator {
public static final String NAME = "EntityWithExplicitQualifiersWithEnhancedSequenceGenerator";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = NAME + "_generator")
@GeneratedValue(generator = NAME + "_generator")
@GenericGenerator(name = NAME + "_generator", strategy = "enhanced-sequence", parameters = {
@Parameter(name = "sequence_name", value = NAME + "_seq"),
@Parameter(name = "catalog", value = EXPLICIT_CATALOG),

View File

@ -25,6 +25,7 @@ import org.hibernate.dialect.SybaseDialect;
import org.hibernate.generator.Generator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.id.uuid.UuidGenerator;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
@ -60,9 +61,8 @@ public class GeneratedValueTest {
assertEquals( UUID.class, entityBinding.getIdentifier().getType().getReturnedClass() );
KeyValue keyValue = entityBinding.getIdentifier();
Dialect dialect = metadata.getDatabase().getDialect();
final Generator generator1 = keyValue.createGenerator( dialect, (RootClass) entityBinding);
IdentifierGenerator generator = generator1 instanceof IdentifierGenerator ? (IdentifierGenerator) generator1 : null;
assertTyping( UUIDGenerator.class, generator );
final Generator generator = keyValue.createGenerator( dialect, (RootClass) entityBinding);
assertTyping( UuidGenerator.class, generator );
// now a functional test
SessionFactory sf = metadata.buildSessionFactory();

View File

@ -15,6 +15,8 @@ import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import org.hamcrest.MatcherAssert;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.CollectionIdJdbcTypeCode;
import org.hibernate.boot.Metadata;
@ -28,6 +30,7 @@ import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.id.enhanced.DatabaseStructure;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.uuid.UuidGenerator;
import org.hibernate.mapping.IdentifierBag;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
@ -131,12 +134,11 @@ public class AutoGenerationTypeTests {
.buildMetadata();
final PersistentClass entityBinding = metadata.getEntityBinding( Entity4.class.getName() );
final Property identifierProperty = entityBinding.getRootClass().getIdentifierProperty();
final KeyValue idMapping = entityBinding.getRootClass().getIdentifier();
Dialect dialect = new H2Dialect();
final Generator generator = idMapping.createGenerator( dialect, entityBinding.getRootClass());
final IdentifierGenerator idGenerator = generator instanceof IdentifierGenerator ? (IdentifierGenerator) generator : null;
assertThat( idGenerator, instanceOf( UUIDGenerator.class ) );
final Generator generator = idMapping.createGenerator( dialect, entityBinding.getRootClass(), identifierProperty );
MatcherAssert.assertThat( generator, instanceOf( UuidGenerator.class ) );
}
}

View File

@ -131,7 +131,7 @@ public class HiLoSequenceMismatchStrategyTest {
public static class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "hilo_sequence_generator")
@GeneratedValue(generator = "hilo_sequence_generator")
@GenericGenerator(name = "hilo_sequence_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
@Parameter(name = "sequence_name", value = sequenceName),
@Parameter(name = "initial_value", value = "1"),

View File

@ -26,7 +26,6 @@ public class ImplicitUuidGenerationTests {
@Test
@ServiceRegistry
@DomainModel(annotatedClasses = Book.class)
@FailureExpected( jiraKey = "HHH-18420", reason = "Implicit UUID generation continues to use UUIDGenerator for the time being" )
void verifyModel(ServiceRegistryScope registryScope, DomainModelScope domainModelScope) {
domainModelScope.withHierarchy( Book.class, (descriptor) -> {
Helper.verifyAlgorithm( registryScope, domainModelScope, descriptor, StandardRandomStrategy.class );

View File

@ -11,7 +11,6 @@ import org.hibernate.orm.test.mapping.identifier.uuid.Helper;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.hibernate.testing.orm.junit.SessionFactory;
@ -26,7 +25,6 @@ public class Implicit2UuidGenerationTests {
@Test
@ServiceRegistry
@DomainModel(annotatedClasses = Book.class)
@FailureExpected( jiraKey = "HHH-18420", reason = "Implicit UUID generation continues to use UUIDGenerator for the time being" )
void verifyModel(ServiceRegistryScope registryScope, DomainModelScope domainModelScope) {
domainModelScope.withHierarchy( Book.class, (descriptor) -> {
Helper.verifyAlgorithm( registryScope, domainModelScope, descriptor, StandardRandomStrategy.class );

View File

@ -26,7 +26,6 @@ public class Implicit3UuidGenerationTests {
@Test
@ServiceRegistry
@DomainModel(annotatedClasses = Book.class)
@FailureExpected( jiraKey = "HHH-18420", reason = "Implicit UUID generation continues to use UUIDGenerator for the time being" )
void verifyModel(ServiceRegistryScope registryScope, DomainModelScope domainModelScope) {
domainModelScope.withHierarchy( Book.class, (descriptor) -> {
Helper.verifyAlgorithm( registryScope, domainModelScope, descriptor, StandardRandomStrategy.class );

View File

@ -1 +0,0 @@
rootProject.name = 'local-build-plugins'