From a67cfd039ea7f924869e2cdf77d081d4fd3cc093 Mon Sep 17 00:00:00 2001 From: Gavin Date: Wed, 30 Nov 2022 16:16:34 +0100 Subject: [PATCH] HHH-15789 unify IdentifierGenerator with value generator stuff --- .../annotations/IdGeneratorType.java | 1 - .../annotations/SourceGeneration.java | 16 +- .../source/internal/hbm/ModelBinder.java | 84 +++-- .../org/hibernate/cfg/AnnotationBinder.java | 35 +- .../cfg/annotations/PropertyBinder.java | 212 ++++++------ .../engine/internal/Nullability.java | 20 +- .../org/hibernate/id/IdentifierGenerator.java | 2 +- .../spi/CustomIdGeneratorCreationContext.java | 11 +- .../org/hibernate/id/uuid/UuidGenerator.java | 11 +- .../internal/util/ReflectHelper.java | 12 + .../hibernate/mapping/GeneratorCreator.java | 15 + .../hibernate/mapping/PersistentClass.java | 10 + .../java/org/hibernate/mapping/Property.java | 9 +- .../java/org/hibernate/mapping/RootClass.java | 3 + .../org/hibernate/mapping/SimpleValue.java | 38 ++- .../metamodel/mapping/AttributeMapping.java | 2 +- .../mapping/GeneratedValueResolver.java | 20 +- .../internal/AbstractEmbeddableMapping.java | 1 - .../AbstractSingularAttributeMapping.java | 17 +- .../internal/BasicAttributeMapping.java | 12 +- ...criminatedAssociationAttributeMapping.java | 3 +- .../internal/EmbeddedAttributeMapping.java | 16 +- .../internal/GeneratedValuesProcessor.java | 61 ++-- .../mapping/internal/IdClassEmbeddable.java | 3 +- .../internal/MappingModelCreationHelper.java | 9 +- .../mapping/internal/NoValueGeneration.java | 41 --- .../internal/PluralAttributeMappingImpl.java | 6 +- .../internal/SimpleForeignKeyDescriptor.java | 2 - .../internal/ToOneAttributeMapping.java | 9 +- .../VirtualEmbeddedAttributeMapping.java | 13 +- .../entity/AbstractEntityPersister.java | 13 +- .../mutation/AbstractMutationCoordinator.java | 6 +- .../entity/mutation/InsertCoordinator.java | 30 +- .../mutation/UpdateCoordinatorStandard.java | 67 ++-- .../tuple/AbstractNonIdentifierAttribute.java | 5 - .../hibernate/tuple/AnnotationGenerator.java | 18 +- .../tuple/AnnotationValueGeneration.java | 34 +- .../tuple/BaselineAttributeInformation.java | 19 -- .../tuple/GeneratedValueGeneration.java | 8 +- .../org/hibernate/tuple/GenerationTiming.java | 6 +- .../tuple/GeneratorCreationContext.java | 26 ++ .../hibernate/tuple/IdentifierProperty.java | 1 - .../tuple/NonIdentifierAttribute.java | 2 - .../org/hibernate/tuple/PropertyFactory.java | 5 - .../org/hibernate/tuple/StandardProperty.java | 5 - .../hibernate/tuple/TenantIdGeneration.java | 22 +- .../hibernate/tuple/VmValueGeneration.java | 14 +- .../tuple/entity/EntityMetamodel.java | 308 ++++++++---------- .../org/hibernate/type/ComponentType.java | 8 +- .../temporals/GeneratedUuidTests.java | 8 +- .../metadata/AuditMetadataGenerator.java | 6 +- 51 files changed, 576 insertions(+), 729 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/mapping/GeneratorCreator.java delete mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NoValueGeneration.java create mode 100644 hibernate-core/src/main/java/org/hibernate/tuple/GeneratorCreationContext.java diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/IdGeneratorType.java b/hibernate-core/src/main/java/org/hibernate/annotations/IdGeneratorType.java index ab94fea0a5..3eed23eb72 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/IdGeneratorType.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/IdGeneratorType.java @@ -6,7 +6,6 @@ */ package org.hibernate.annotations; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/SourceGeneration.java b/hibernate-core/src/main/java/org/hibernate/annotations/SourceGeneration.java index d0d8319bc7..74b8625fe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/SourceGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/SourceGeneration.java @@ -13,13 +13,14 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.tuple.AnnotationGenerator; import org.hibernate.tuple.GenerationTiming; +import org.hibernate.tuple.GeneratorCreationContext; import org.hibernate.tuple.InMemoryGenerator; import org.hibernate.tuple.TimestampGenerators; import org.hibernate.tuple.ValueGenerator; import org.jboss.logging.Logger; +import java.lang.reflect.Member; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -44,22 +45,21 @@ import static org.hibernate.tuple.GenerationTiming.ALWAYS; @Deprecated(since = "6.2") @Internal public class SourceGeneration - implements AnnotationGenerator, InMemoryGenerator, ValueGenerator { + implements InMemoryGenerator, ValueGenerator { private static final CoreMessageLogger log = Logger.getMessageLogger( CoreMessageLogger.class, SourceGeneration.class.getName() ); - private Class propertyType; - private ValueGenerator valueGenerator; + private final Class propertyType; + private final ValueGenerator valueGenerator; - @Override - public void initialize(Source annotation, Class propertyType, String entityName, String propertyName) { - initialize( annotation, propertyType ); + public SourceGeneration(Source annotation, Member member, GeneratorCreationContext context) { + this( annotation, context.getProperty().getType().getReturnedClass() ); } - public void initialize(Source annotation, Class propertyType) { + public SourceGeneration(Source annotation, Class propertyType) { this.propertyType = propertyType; switch ( annotation.value() ) { case DB: diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index c14861a995..d8325fc50a 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -1001,32 +1001,14 @@ public class ModelBinder { context -> implicitNamingStrategy.determineBasicColumnName( versionAttributeSource ) ); - Property prop = new Property(); - prop.setValue( versionValue ); + Property property = new Property(); + property.setValue( versionValue ); bindProperty( sourceDocument, versionAttributeSource, - prop + property ); - // for version properties marked as being generated, make sure they are "always" - // generated; aka, "insert" is invalid; this is dis-allowed by the DTD, - // but just to make sure... - if ( prop.getValueGenerationStrategy() != null ) { - switch ( prop.getValueGenerationStrategy().getGenerationTiming() ) { - case INSERT: - throw new MappingException( - "'generated' attribute cannot be 'insert' for version/timestamp property", - sourceDocument.getOrigin() - ); - case UPDATE: - throw new MappingException( - "'generated' attribute cannot be 'update' for version/timestamp property", - sourceDocument.getOrigin() - ); - } - } - if ( versionAttributeSource.getUnsavedValue() != null ) { versionValue.setNullValue( versionAttributeSource.getUnsavedValue() ); } @@ -1034,14 +1016,13 @@ public class ModelBinder { versionValue.setNullValue( "undefined" ); } if ( versionAttributeSource.getSource().equals("db") ) { - SourceGeneration generation = new SourceGeneration(); - generation.initialize( DB_SOURCE, prop.getType().getReturnedClass() ); - prop.setValueGenerationStrategy( generation ); + property.setValueGenerationStrategy( + context -> new SourceGeneration( DB_SOURCE, property.getType().getReturnedClass() ) ); } - rootEntityDescriptor.setVersion( prop ); - rootEntityDescriptor.setDeclaredVersion( prop ); - rootEntityDescriptor.addProperty( prop ); + rootEntityDescriptor.setVersion( property ); + rootEntityDescriptor.setDeclaredVersion( property ); + rootEntityDescriptor.addProperty( property ); } private void bindEntityDiscriminator( @@ -2557,30 +2538,39 @@ public class ModelBinder { property.setLazy( singularAttributeSource.isBytecodeLazy() ); final GenerationTiming generationTiming = singularAttributeSource.getGenerationTiming(); - if ( generationTiming != null && generationTiming != GenerationTiming.NEVER ) { - // we had generation specified... - // HBM only supports "database generated values" - property.setValueGenerationStrategy( new GeneratedValueGeneration( generationTiming ) ); - - // generated properties can *never* be insertable... - if ( property.isInsertable() && generationTiming.includesInsert() ) { - log.debugf( - "Property [%s] specified %s generation, setting insertable to false : %s", - propertySource.getName(), - generationTiming.name(), + if ( generationTiming != null ) { + if ( (generationTiming == GenerationTiming.INSERT || generationTiming == GenerationTiming.UPDATE) + && property.getValue() instanceof SimpleValue + && ((SimpleValue) property.getValue()).isVersion() ) { + // this is enforced by DTD, but just make sure + throw new MappingException( + "'generated' attribute cannot be 'insert' or 'update' for version/timestamp property", mappingDocument.getOrigin() ); - property.setInsertable( false ); } + if ( generationTiming.isNotNever() ) { + property.setValueGenerationStrategy( context -> new GeneratedValueGeneration( generationTiming ) ); - // properties generated on update can never be updatable... - if ( property.isUpdateable() && generationTiming.includesUpdate() ) { - log.debugf( - "Property [%s] specified ALWAYS generation, setting updateable to false : %s", - propertySource.getName(), - mappingDocument.getOrigin() - ); - property.setUpdateable( false ); + // generated properties can *never* be insertable... + if ( property.isInsertable() && generationTiming.includesInsert() ) { + log.debugf( + "Property [%s] specified %s generation, setting insertable to false : %s", + propertySource.getName(), + generationTiming.name(), + mappingDocument.getOrigin() + ); + property.setInsertable( false ); + } + + // properties generated on update can never be updatable... + if ( property.isUpdateable() && generationTiming.includesUpdate() ) { + log.debugf( + "Property [%s] specified ALWAYS generation, setting updateable to false : %s", + propertySource.getName(), + mappingDocument.getOrigin() + ); + property.setUpdateable( false ); + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index ef6252fb43..5d5d753769 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -7,8 +7,6 @@ package org.hibernate.cfg; import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Member; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; @@ -19,7 +17,6 @@ import java.util.Map; import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; -import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.Cascade; @@ -72,14 +69,12 @@ import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.cfg.annotations.CollectionBinder; import org.hibernate.cfg.annotations.EntityBinder; -import org.hibernate.cfg.annotations.HCANNHelper; import org.hibernate.cfg.annotations.Nullability; import org.hibernate.cfg.annotations.PropertyBinder; import org.hibernate.cfg.annotations.QueryBinder; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.GenericsHelper; import org.hibernate.mapping.Any; @@ -157,6 +152,7 @@ import static org.hibernate.cfg.InheritanceState.getInheritanceStateOfSuperEntit import static org.hibernate.cfg.InheritanceState.getSuperclassInheritanceState; import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder; import static org.hibernate.cfg.annotations.HCANNHelper.findContainingAnnotation; +import static org.hibernate.cfg.annotations.PropertyBinder.identifierGeneratorCreator; import static org.hibernate.internal.CoreLogging.messageLogger; import static org.hibernate.mapping.Constraint.hashedName; import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY; @@ -1812,7 +1808,7 @@ public final class AnnotationBinder { final XProperty idProperty = inferredData.getProperty(); final Annotation generatorAnnotation = findContainingAnnotation( idProperty, IdGeneratorType.class ); if ( generatorAnnotation != null ) { - setCustomCreator( idValue, idProperty, generatorAnnotation ); + idValue.setCustomIdGeneratorCreator( identifierGeneratorCreator( idProperty, generatorAnnotation ) ); } else { final XClass entityClass = inferredData.getClassOrElement(); @@ -1827,33 +1823,6 @@ public final class AnnotationBinder { } } - private static void setCustomCreator(SimpleValue idValue, XProperty idProperty, Annotation generatorAnnotation) { - final Member underlyingMember = HCANNHelper.getUnderlyingMember( idProperty ); - final Class annotationType = generatorAnnotation.annotationType(); - final IdGeneratorType idGeneratorType = annotationType.getAnnotation( IdGeneratorType.class ); - assert idGeneratorType != null; - idValue.setCustomIdGeneratorCreator( creationContext -> { - final Class generatorClass = idGeneratorType.value(); - try { - return generatorClass - .getConstructor( annotationType, Member.class, CustomIdGeneratorCreationContext.class ) - .newInstance( generatorAnnotation, underlyingMember, creationContext ); - } - catch (NoSuchMethodException e) { - throw new HibernateException( - "Unable to find appropriate constructor for @IdGeneratorType handling : " + generatorClass.getName(), - e - ); - } - catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { - throw new HibernateException( - "Unable to invoke constructor for @IdGeneratorType handling : " + generatorClass.getName(), - e - ); - } - } ); - } - private static void createIdGenerator( SimpleValue idValue, Map classGenerators, diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java index 291d9da2aa..9bbbd9c4d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java @@ -14,7 +14,7 @@ import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.annotations.AttributeBinderType; -import org.hibernate.annotations.Generated; +import org.hibernate.annotations.IdGeneratorType; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.OptimisticLock; @@ -29,9 +29,13 @@ import org.hibernate.cfg.AnnotatedColumn; import org.hibernate.cfg.InheritanceState; import org.hibernate.cfg.PropertyHolder; import org.hibernate.cfg.PropertyPreloadedData; +import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.mapping.Collection; import org.hibernate.mapping.Component; +import org.hibernate.mapping.GeneratorCreator; +import org.hibernate.mapping.IdentifierGeneratorCreator; import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.Property; import org.hibernate.mapping.RootClass; @@ -43,13 +47,13 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.tuple.AnnotationGenerator; import org.hibernate.tuple.Generator; import org.hibernate.tuple.AttributeBinder; -import org.hibernate.tuple.InDatabaseGenerator; +import org.hibernate.tuple.GeneratorCreationContext; import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.ValueGeneration; -import org.hibernate.tuple.ValueGenerator; import org.jboss.logging.Logger; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; import java.util.Map; import static org.hibernate.cfg.BinderHelper.getMappedSuperclassOrNull; @@ -322,7 +326,7 @@ public class PropertyBinder { if ( this.property != null ) { if ( entityBinder != null ) { handleNaturalId( property ); - property.setValueGenerationStrategy( determineValueGenerationStrategy( this.property ) ); + property.setValueGenerationStrategy( getValueGenerationFromAnnotations( this.property ) ); } // HHH-4635 -- needed for dialect-specific property ordering property.setLob( this.property.isAnnotationPresent( Lob.class ) ); @@ -379,143 +383,137 @@ public class PropertyBinder { + "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" ); } if ( property.isAnnotationPresent(EmbeddedId.class) ) { - throw new AnnotationException("Property '" + qualify( holder.getPath(), name ) + throw new AnnotationException( "Property '" + qualify( holder.getPath(), name ) + "' is annotated '@OptimisticLock(excluded=true)' and '@EmbeddedId'" ); } } } - private Generator determineValueGenerationStrategy(XProperty property) { - Generator valueGeneration = getValueGenerationFromAnnotations( property ); - if ( valueGeneration == null ) { - return NoValueGeneration.INSTANCE; - } - if ( valueGeneration instanceof InDatabaseGenerator) { - // if we have an in-db generator, mark it as not insertable nor updatable - final boolean writable = ( (InDatabaseGenerator) valueGeneration ).writePropertyValue(); - insertable = writable; - updatable = writable; - } - return valueGeneration; - } - /** * Returns the value generation strategy for the given property, if any. */ - private Generator getValueGenerationFromAnnotations(XProperty property) { - Generator valueGeneration = null; + private GeneratorCreator getValueGenerationFromAnnotations(XProperty property) { + GeneratorCreator creator = null; for ( Annotation annotation : property.getAnnotations() ) { - final Generator candidate = getValueGenerationFromAnnotation( property, annotation ); + final GeneratorCreator candidate = generatorCreator( property, annotation ); if ( candidate != null ) { - if ( valueGeneration != null ) { + if ( creator != null ) { throw new AnnotationException( "Property '" + qualify( holder.getPath(), name ) + "' has multiple '@ValueGenerationType' annotations" ); } else { - valueGeneration = candidate; + creator = candidate; } } } - return valueGeneration; + return creator; } /** * In case the given annotation is a value generator annotation, the corresponding value generation strategy to be * applied to the given property is returned, {@code null} otherwise. - */ - private Generator getValueGenerationFromAnnotation( - XProperty property, - Annotation annotation) { - final ValueGenerationType generatorAnnotation = annotation.annotationType().getAnnotation( ValueGenerationType.class ); - if ( generatorAnnotation == null ) { - return null; - } - - final Generator valueGeneration = - instantiateAndInitializeValueGeneration( annotation, generatorAnnotation.generatedBy(), property ); - - if ( annotation.annotationType() == Generated.class && property.isAnnotationPresent(Version.class) ) { - switch ( valueGeneration.getGenerationTiming() ) { - case INSERT: - throw new AnnotationException("Property '" + qualify( holder.getPath(), name ) - + "' is annotated '@Generated(INSERT)' and '@Version' (use '@Generated(ALWAYS)' instead)" - - ); - case UPDATE: - throw new AnnotationException("Property '" + qualify( holder.getPath(), name ) - + "' is annotated '@Generated(UPDATE)' and '@Version' (use '@Generated(ALWAYS)' instead)" - - ); - } - } - - return valueGeneration; - } - - /** * Instantiates the given generator annotation type, initializing it with the given instance of the corresponding * generator annotation and the property's type. */ - private Generator instantiateAndInitializeValueGeneration( - A annotation, - Class generationType, - XProperty property) { + private GeneratorCreator generatorCreator(XProperty property, A annotation) { + final Member member = HCANNHelper.getUnderlyingMember( property ); + final Class annotationType = annotation.annotationType(); + final ValueGenerationType generatorAnnotation = annotationType.getAnnotation( ValueGenerationType.class ); + if ( generatorAnnotation == null ) { + return null; + } + return creationContext -> { + final Generator generator = + instantiateGenerator( + annotation, + member, + annotationType, + creationContext, + GeneratorCreationContext.class, + generatorAnnotation.generatedBy() + ); + callInitialize( annotation, member, creationContext, generator ); + checkVersionGenerationAlways( property, generator ); + return generator; + }; + } + public static IdentifierGeneratorCreator identifierGeneratorCreator(XProperty idProperty, Annotation annotation) { + final Member member = HCANNHelper.getUnderlyingMember( idProperty ); + final Class annotationType = annotation.annotationType(); + final IdGeneratorType idGeneratorType = annotationType.getAnnotation( IdGeneratorType.class ); + assert idGeneratorType != null; + return creationContext -> { + final IdentifierGenerator generator = + instantiateGenerator( + annotation, + member, + annotationType, + creationContext, + CustomIdGeneratorCreationContext.class, + idGeneratorType.value() + ); + callInitialize( annotation, member, creationContext, generator ); + return generator; + }; + } + + private static G instantiateGenerator( + Annotation annotation, + Member member, + Class annotationType, + C creationContext, + Class contextClass, + Class generatorClass) { try { - final Generator valueGeneration = generationType.newInstance(); - if ( valueGeneration instanceof AnnotationGenerator) { - // This will cause a CCE in case the generation type doesn't match the annotation type; As this would be - // a programming error of the generation type developer and thus should show up during testing, we don't - // check this explicitly; If required, this could be done e.g. using ClassMate - @SuppressWarnings("unchecked") - final AnnotationGenerator generation = (AnnotationGenerator) valueGeneration; - generation.initialize( - annotation, - buildingContext.getBootstrapContext().getReflectionManager().toClass( property.getType() ), - entityBinder.getPersistentClass().getEntityName(), - property.getName() - ); + try { + return generatorClass + .getConstructor( annotationType, Member.class, contextClass ) + .newInstance( annotation, member, creationContext); + } + catch (NoSuchMethodException ignore) { + try { + return generatorClass + .getConstructor( annotationType ) + .newInstance( annotation ); + } + catch (NoSuchMethodException i) { + return generatorClass.newInstance(); + } } - - return valueGeneration; } - catch (HibernateException e) { - throw e; - } - catch (Exception e) { - throw new AnnotationException( - "Exception occurred during processing of generator annotation: " + qualify( - holder.getPath(), - name - ), e + catch (InvocationTargetException|InstantiationException|IllegalAccessException e) { + throw new HibernateException( + "Could not instantiate generator of type '" + generatorClass.getName() + "'", + e ); } } - private static class NoValueGeneration implements ValueGeneration { - /** - * Singleton access - */ - public static final NoValueGeneration INSTANCE = new NoValueGeneration(); - - @Override - public GenerationTiming getGenerationTiming() { - return GenerationTiming.NEVER; + private static void callInitialize( + A annotation, + Member member, + GeneratorCreationContext creationContext, + Generator generator) { + if ( generator instanceof AnnotationGenerator) { + // This will cause a CCE in case the generation type doesn't match the annotation type; As this would be + // a programming error of the generation type developer and thus should show up during testing, we don't + // check this explicitly; If required, this could be done e.g. using ClassMate + @SuppressWarnings("unchecked") + final AnnotationGenerator generation = (AnnotationGenerator) generator; + generation.initialize( annotation, member, creationContext ); } + } - @Override - public ValueGenerator getValueGenerator() { - return null; - } - - @Override - public boolean referenceColumnInSql() { - return true; - } - - @Override - public String getDatabaseGeneratedReferencedColumnValue() { - return null; + private void checkVersionGenerationAlways(XProperty property, Generator generator) { + if ( property.isAnnotationPresent(Version.class) ) { + final GenerationTiming timing = generator.getGenerationTiming(); + if ( timing != GenerationTiming.ALWAYS ) { + throw new AnnotationException("Property '" + qualify( holder.getPath(), name ) + + "' is annotated '@Version' but has a value generator with timing " + timing.name() + + " (the value generation timing must be ALWAYS)" + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java index b8a2198df4..a58a355af8 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/Nullability.java @@ -14,8 +14,7 @@ import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.InMemoryGenerator; +import org.hibernate.tuple.Generator; import org.hibernate.type.CollectionType; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; @@ -74,7 +73,7 @@ public final class Nullability { /* * Algorithm * Check for any level one nullability breaks - * Look at non null components to + * Look at non-null components to * recursively check next level of nullability breaks * Look at Collections containing components to * recursively check next level of nullability breaks @@ -82,7 +81,7 @@ public final class Nullability { * * In the previous implementation, not-null stuffs where checked * filtering by level one only updatable - * or insertable columns. So setting a sub component as update="false" + * or insertable columns. So setting a subcomponent as update="false" * has no effect on not-null check if the main component had good checkability * In this implementation, we keep this feature. * However, I never see any documentation mentioning that, but it's for @@ -94,14 +93,13 @@ public final class Nullability { ? persister.getPropertyInsertability() : persister.getPropertyUpdateability(); final Type[] propertyTypes = persister.getPropertyTypes(); - final InMemoryGenerator[] inMemoryValueGenerationStrategies = - persister.getEntityMetamodel().getInMemoryValueGenerationStrategies(); + final Generator[] generators = persister.getEntityMetamodel().getGenerators(); for ( int i = 0; i < values.length; i++ ) { - if ( checkability[i] && - values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY && - GenerationTiming.NEVER == inMemoryValueGenerationStrategies[i].getGenerationTiming() ) { + if ( checkability[i] + && values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY + && !generated( generators[i] ) ) { final Object value = values[i]; if ( !nullability[i] && value == null ) { //check basic level one nullability @@ -130,6 +128,10 @@ public final class Nullability { } } + private static boolean generated(Generator generator) { + return generator != null && generator.getGenerationTiming().isNotNever(); + } + /** * check sub elements-nullability. Returns property path that break * nullability or null if none diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java index 2f62606b4a..c5573b74cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java @@ -55,7 +55,7 @@ public interface IdentifierGenerator extends InMemoryGenerator, ExportableProduc * identifier generator. * * @see org.hibernate.annotations.GenericGenerator#name() - * @see jakarta.persistence.GeneratedValue#generator(). + * @see jakarta.persistence.GeneratedValue#generator() */ String GENERATOR_NAME = "GENERATOR_NAME"; diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java b/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java index 66a462af10..dcb061f500 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java @@ -7,19 +7,12 @@ package org.hibernate.id.factory.spi; import org.hibernate.Incubating; -import org.hibernate.boot.model.relational.Database; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.mapping.RootClass; -import org.hibernate.service.ServiceRegistry; +import org.hibernate.tuple.GeneratorCreationContext; @Incubating -public interface CustomIdGeneratorCreationContext { +public interface CustomIdGeneratorCreationContext extends GeneratorCreationContext { IdentifierGeneratorFactory getIdentifierGeneratorFactory(); - Database getDatabase(); - ServiceRegistry getServiceRegistry(); - - String getDefaultCatalog(); - String getDefaultSchema(); - RootClass getRootClass(); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java index 7581f49348..d4489e1d69 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java @@ -6,9 +6,7 @@ */ package org.hibernate.id.uuid; -import java.lang.reflect.Field; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.util.UUID; import org.hibernate.HibernateException; @@ -19,6 +17,7 @@ import org.hibernate.type.descriptor.java.UUIDJavaType; import org.hibernate.type.descriptor.java.UUIDJavaType.ValueTransformer; import static org.hibernate.annotations.UuidGenerator.Style.TIME; +import static org.hibernate.internal.util.ReflectHelper.getPropertyType; /** * UUID-based IdentifierGenerator @@ -44,13 +43,7 @@ public class UuidGenerator implements StandardGenerator { generator = StandardRandomStrategy.INSTANCE; } - final Class propertyType; - if ( idMember instanceof Method ) { - propertyType = ( (Method) idMember ).getReturnType(); - } - else { - propertyType = ( (Field) idMember ).getType(); - } + final Class propertyType = getPropertyType( idMember ); if ( UUID.class.isAssignableFrom( propertyType ) ) { valueTransformer = UUIDJavaType.PassThroughTransformer.INSTANCE; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java index 03dddc7155..849121016f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java @@ -876,4 +876,16 @@ public final class ReflectHelper { } throw new UnsupportedOperationException( "Can't get java type class from type: " + type ); } + + public static Class getPropertyType(Member member) { + if (member instanceof Field) { + return ( (Field) member ).getType(); + } + else if (member instanceof Method) { + return ( (Method) member ).getReturnType(); + } + else { + throw new AssertionFailure("member should have been a method or field"); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/GeneratorCreator.java b/hibernate-core/src/main/java/org/hibernate/mapping/GeneratorCreator.java new file mode 100644 index 0000000000..87559c7e29 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/mapping/GeneratorCreator.java @@ -0,0 +1,15 @@ +/* + * 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.mapping; + +import org.hibernate.tuple.Generator; +import org.hibernate.tuple.GeneratorCreationContext; + +@FunctionalInterface +public interface GeneratorCreator { + Generator createGenerator(GeneratorCreationContext context); +} diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 8b76779dbd..b3c8cb4d44 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -46,7 +46,17 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl private static final Alias PK_ALIAS = new Alias( 15, "PK" ); + /** + * The magic value of {@link jakarta.persistence.DiscriminatorValue#value} + * which indicates that the subclass is distinguished by a null value of the + * discriminator column. + */ public static final String NULL_DISCRIMINATOR_MAPPING = "null"; + /** + * The magic value of {@link jakarta.persistence.DiscriminatorValue#value} + * which indicates that the subclass is distinguished by any non-null value + * of the discriminator column. + */ public static final String NOT_NULL_DISCRIMINATOR_MAPPING = "not null"; private final MetadataBuildingContext metadataBuildingContext; diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java index ac29099aea..25bd1dc065 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java @@ -27,7 +27,6 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy; import org.hibernate.property.access.spi.PropertyAccessStrategyResolver; import org.hibernate.property.access.spi.Setter; import org.hibernate.service.ServiceRegistry; -import org.hibernate.tuple.Generator; import org.hibernate.type.CompositeType; import org.hibernate.type.Type; @@ -45,7 +44,7 @@ public class Property implements Serializable, MetaAttributable { private boolean insertable = true; private boolean selectable = true; private boolean optimisticLocked = true; - private Generator generator; + private GeneratorCreator generator; private String propertyAccessorName; private PropertyAccessStrategy propertyAccessStrategy; private boolean lazy; @@ -216,11 +215,11 @@ public class Property implements Serializable, MetaAttributable { return insertable && value.hasAnyInsertableColumns(); } - public Generator getValueGenerationStrategy() { + public GeneratorCreator getValueGenerationStrategy() { return generator; } - public void setValueGenerationStrategy(Generator generator) { + public void setValueGenerationStrategy(GeneratorCreator generator) { this.generator = generator; } @@ -441,7 +440,7 @@ public class Property implements Serializable, MetaAttributable { public void setReturnedClassName(String returnedClassName) { this.returnedClassName = returnedClassName; } - + public Property copy() { final Property prop = new Property(); prop.setName( getName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java index 9a11790ad5..000e8bab59 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/RootClass.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Set; import org.hibernate.MappingException; +import org.hibernate.Remove; import org.hibernate.boot.Metadata; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.internal.CoreLogging; @@ -30,7 +31,9 @@ import org.hibernate.persister.entity.EntityPersister; public class RootClass extends PersistentClass implements TableOwner { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( RootClass.class ); + @Deprecated(since = "6.2") @Remove public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id"; + @Deprecated(since = "6.2") @Remove public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class"; private Property identifierProperty; diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java index 31046fd265..8d3c72d7aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java @@ -363,11 +363,9 @@ public abstract class SimpleValue implements KeyValue { private IdentifierGenerator identifierGenerator; /** - * Returns the cached identifierGenerator. - * - * @return IdentifierGenerator null if - * {@link #createIdentifierGenerator(IdentifierGeneratorFactory, Dialect, String, String, RootClass)} was never - * completed. + * Returns the cached {@link IdentifierGenerator}, or null if + * {@link #createIdentifierGenerator(IdentifierGeneratorFactory, Dialect, String, String, RootClass)} + * was never completed. * * @deprecated not used and no longer supported. */ @@ -434,6 +432,16 @@ public abstract class SimpleValue implements KeyValue { public RootClass getRootClass() { return rootClass; } + + @Override + public PersistentClass getPersistentClass() { + return rootClass; + } + + @Override + public Property getProperty() { + return rootClass.getIdentifierProperty(); + } }; identifierGenerator = customIdGeneratorCreator.createGenerator( creationContext ); @@ -761,14 +769,7 @@ public abstract class SimpleValue implements KeyValue { if ( className == null ) { throw new MappingException( "Attribute types for a dynamic entity must be explicitly specified: " + propertyName ); } - typeName = ReflectHelper.reflectedPropertyClass( - className, - propertyName, - getMetadata() - .getMetadataBuildingOptions() - .getServiceRegistry() - .getService( ClassLoaderService.class ) - ).getName(); + typeName = getClass( className, propertyName ).getName(); // todo : to fully support isNationalized here we need to do the process hinted at above // essentially, much of the logic from #buildAttributeConverterTypeAdapter wrt resolving // a (1) JdbcType, a (2) JavaType and dynamically building a BasicType @@ -780,6 +781,17 @@ public abstract class SimpleValue implements KeyValue { type = buildAttributeConverterTypeAdapter(); } + private Class getClass(String className, String propertyName) { + return ReflectHelper.reflectedPropertyClass( + className, + propertyName, + getMetadata() + .getMetadataBuildingOptions() + .getServiceRegistry() + .getService(ClassLoaderService.class) + ); + } + /** * Build a Hibernate Type that incorporates the JPA AttributeConverter. AttributeConverter works totally in * memory, meaning it converts between one Java representation (the entity attribute representation) and another diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java index d046b431a1..7fb12d8605 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java @@ -70,7 +70,7 @@ public interface AttributeMapping * * @apiNote Only relevant for non-id attributes */ - Generator getValueGeneration(); + Generator getGenerator(); @Override default EntityMappingType findContainingEntityMapping() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java index 43be9786ac..575d07445a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/GeneratedValueResolver.java @@ -21,26 +21,18 @@ import org.hibernate.tuple.InMemoryGenerator; @Incubating public interface GeneratedValueResolver { static GeneratedValueResolver from( - Generator valueGeneration, + Generator generator, GenerationTiming requestedTiming, int dbSelectionPosition) { - assert requestedTiming != GenerationTiming.NEVER; + assert requestedTiming.isNotNever(); - if ( valueGeneration == null || !valueGeneration.getGenerationTiming().includes( requestedTiming ) ) { + if ( generator == null || !generator.getGenerationTiming().includes( requestedTiming ) ) { return NoGeneratedValueResolver.INSTANCE; } - // todo (6.x) : incorporate `org.hibernate.tuple.InDatabaseValueGenerationStrategy` - // and `org.hibernate.tuple.InMemoryValueGenerationStrategy` from `EntityMetamodel`. - // this requires unification of the read and write (insert/update) aspects of - // value generation which we'll circle back to as we convert write operations to - // use the "runtime mapping" (`org.hibernate.metamodel.mapping`) model - else if ( valueGeneration.generatedByDatabase() ) { - // in-db generation (column-default, function, etc) - return new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition ); - } else { - InMemoryGenerator generation = (InMemoryGenerator) valueGeneration; - return new InMemoryGeneratedValueResolver( generation, requestedTiming ); + return generator.generatedByDatabase() + ? new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition ) // in-db generation (column-default, function, etc) + : new InMemoryGeneratedValueResolver( (InMemoryGenerator) generator, requestedTiming ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java index a12ae3924a..7bf8ab0270 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java @@ -156,7 +156,6 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType declaringType, original, original.getPropertyAccess(), - original.getValueGeneration(), selectableMapping.isInsertable(), selectableMapping.isUpdateable(), selectableMapping diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java index a53ea7019b..82a4a71cc4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractSingularAttributeMapping.java @@ -23,7 +23,6 @@ public abstract class AbstractSingularAttributeMapping implements SingularAttributeMapping { private final PropertyAccess propertyAccess; - private final Generator valueGeneration; public AbstractSingularAttributeMapping( String name, @@ -31,13 +30,9 @@ public abstract class AbstractSingularAttributeMapping AttributeMetadataAccess attributeMetadataAccess, FetchOptions mappedFetchOptions, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( name, attributeMetadataAccess, mappedFetchOptions, stateArrayPosition, declaringType ); this.propertyAccess = propertyAccess; - this.valueGeneration = valueGeneration != null - ? valueGeneration - : NoValueGeneration.INSTANCE; } public AbstractSingularAttributeMapping( @@ -47,13 +42,9 @@ public abstract class AbstractSingularAttributeMapping FetchTiming fetchTiming, FetchStyle fetchStyle, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( name, attributeMetadataAccess, fetchTiming, fetchStyle, stateArrayPosition, declaringType ); this.propertyAccess = propertyAccess; - this.valueGeneration = valueGeneration != null - ? valueGeneration - : NoValueGeneration.INSTANCE; } @Override @@ -62,7 +53,7 @@ public abstract class AbstractSingularAttributeMapping } @Override - public Generator getValueGeneration() { - return valueGeneration; + public Generator getGenerator() { + return findContainingEntityMapping().getEntityPersister().getEntityMetamodel().getGenerators()[getStateArrayPosition()]; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java index a8416e9183..2186d2f27d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java @@ -11,6 +11,7 @@ import java.util.function.BiConsumer; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.mapping.GeneratorCreator; import org.hibernate.mapping.IndexedConsumer; import org.hibernate.metamodel.mapping.AttributeMetadataAccess; import org.hibernate.metamodel.mapping.BasicValuedModelPart; @@ -36,7 +37,6 @@ import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.tuple.Generator; import org.hibernate.type.descriptor.java.JavaType; /** @@ -86,8 +86,7 @@ public class BasicAttributeMapping boolean updateable, JdbcMapping jdbcMapping, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( attributeName, stateArrayPosition, @@ -95,8 +94,7 @@ public class BasicAttributeMapping mappedFetchTiming, mappedFetchStyle, declaringType, - propertyAccess, - valueGeneration + propertyAccess ); this.navigableRole = navigableRole; this.tableExpression = tableExpression; @@ -126,7 +124,6 @@ public class BasicAttributeMapping ManagedMappingType declaringType, BasicValuedModelPart original, PropertyAccess propertyAccess, - Generator valueGeneration, boolean insertable, boolean updateable, SelectableMapping selectableMapping) { @@ -168,8 +165,7 @@ public class BasicAttributeMapping updateable, original.getJdbcMapping(), declaringType, - propertyAccess, - valueGeneration + propertyAccess ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java index ac4d9904dd..fad143d138 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationAttributeMapping.java @@ -85,8 +85,7 @@ public class DiscriminatedAssociationAttributeMapping fetchTiming, FetchStyle.SELECT, declaringType, - propertyAccess, - null + propertyAccess ); this.navigableRole = attributeRole; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index b499bbf858..4c11e65a66 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -54,7 +54,6 @@ import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.embeddable.EmbeddableValuedFetchable; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; -import org.hibernate.tuple.Generator; /** * @author Steve Ebersole @@ -79,8 +78,7 @@ public class EmbeddedAttributeMapping FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { this( name, navigableRole, @@ -92,8 +90,7 @@ public class EmbeddedAttributeMapping mappedFetchStyle, embeddableMappingType, declaringType, - propertyAccess, - valueGeneration + propertyAccess ); } @@ -108,8 +105,7 @@ public class EmbeddedAttributeMapping FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( name, stateArrayPosition, @@ -117,8 +113,7 @@ public class EmbeddedAttributeMapping mappedFetchTiming, mappedFetchStyle, declaringType, - propertyAccess, - valueGeneration + propertyAccess ); this.navigableRole = navigableRole; @@ -145,8 +140,7 @@ public class EmbeddedAttributeMapping keyDeclaringType, inverseModelPart instanceof PropertyBasedMapping ? ( (PropertyBasedMapping) inverseModelPart ).getPropertyAccess() : - null, - null + null ); this.navigableRole = inverseModelPart.getNavigableRole().getParent().append( inverseModelPart.getFetchableName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java index ce5ebc6937..dab308a0a9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java @@ -35,7 +35,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.InDatabaseGenerator; +import org.hibernate.tuple.Generator; /** * @author Steve Ebersole @@ -59,43 +59,9 @@ public class GeneratedValuesProcessor { // NOTE: we only care about db-generated values here. in-memory generation // is applied before the insert/update happens. - final List generatedValuesToSelect = new ArrayList<>(); - // todo (6.0): for now, we rely on the entity metamodel as composite attributes report GenerationTiming.NEVER - // even if they have attributes that would need generation - final InDatabaseGenerator[] inDatabaseValueGenerationStrategies = entityDescriptor.getEntityPersister() - .getEntityMetamodel() - .getInDatabaseValueGenerationStrategies(); - entityDescriptor.visitAttributeMappings( mapping -> { - final InDatabaseGenerator inDatabaseValueGenerationStrategy = - inDatabaseValueGenerationStrategies[ mapping.getStateArrayPosition() ]; - if ( inDatabaseValueGenerationStrategy.getGenerationTiming() == GenerationTiming.NEVER ) { - return; - } - final GeneratedValueResolver generatedValueResolver = new InDatabaseGeneratedValueResolver( - timing, - generatedValuesToSelect.size() - ); -// if ( attr.getValueGeneration().getGenerationTiming() == GenerationTiming.NEVER ) { -// return; -// } -// -// final GeneratedValueResolver generatedValueResolver = GeneratedValueResolver.from( -// attr.getValueGeneration(), -// timing, -// generatedValuesToSelect.size() -// ); -// -// //noinspection RedundantClassCall -// if ( ! InDatabaseGeneratedValueResolver.class.isInstance( generatedValueResolver ) ) { -// // again, we only care about in in-db generations here -// return; -// } - - // this attribute is generated for the timing we are processing... - valueDescriptors.add( new GeneratedValueDescriptor( generatedValueResolver, mapping ) ); - generatedValuesToSelect.add( mapping ); - }); - + // todo (6.0): for now, we rely on the entity metamodel as composite attributes report + // GenerationTiming.NEVER even if they have attributes that would need generation + final List generatedValuesToSelect = getGeneratedValues( entityDescriptor, timing ); if ( generatedValuesToSelect.isEmpty() ) { selectStatement = null; } @@ -114,6 +80,25 @@ public class GeneratedValuesProcessor { } } + private List getGeneratedValues(EntityMappingType entityDescriptor, GenerationTiming timing) { + final Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators(); + final List generatedValuesToSelect = new ArrayList<>(); + entityDescriptor.visitAttributeMappings( mapping -> { + final Generator generator = generators[ mapping.getStateArrayPosition() ]; + if ( generator != null + && generator.generatedByDatabase() + && generator.getGenerationTiming().isNotNever() ) { + // this attribute is generated for the timing we are processing... + valueDescriptors.add( new GeneratedValueDescriptor( + new InDatabaseGeneratedValueResolver( timing, generatedValuesToSelect.size() ), + mapping + ) ); + generatedValuesToSelect.add( mapping ); + } + } ); + return generatedValuesToSelect; + } + public void processGeneratedValues(Object entity, Object id, Object[] state, SharedSessionContractImplementor session) { if ( selectStatement == null ) { return; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java index 7431add30d..80bf487a4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java @@ -114,8 +114,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden FetchStyle.JOIN, this, identifiedEntityMapping, - propertyAccess, - null + propertyAccess ); final CompositeType idClassType = (CompositeType) idClassSource.getType(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index d9e125aeed..ff67841e42 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -287,8 +287,7 @@ public class MappingModelCreationHelper { updateable, attrType, declaringType, - propertyAccess, - bootProperty.getValueGenerationStrategy() + propertyAccess ); } @@ -333,8 +332,7 @@ public class MappingModelCreationHelper { FetchStyle.JOIN, attributeMappingType, declaringType, - propertyAccess, - bootProperty.getValueGenerationStrategy() + propertyAccess ); } else { @@ -349,8 +347,7 @@ public class MappingModelCreationHelper { FetchStyle.JOIN, attributeMappingType, declaringType, - propertyAccess, - bootProperty.getValueGenerationStrategy() + propertyAccess ); } }, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NoValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NoValueGeneration.java deleted file mode 100644 index a9af033d09..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NoValueGeneration.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.metamodel.mapping.internal; - -import org.hibernate.tuple.GenerationTiming; -import org.hibernate.tuple.ValueGeneration; -import org.hibernate.tuple.ValueGenerator; - -/** - * @author Steve Ebersole - */ -public class NoValueGeneration implements ValueGeneration { - /** - * Singleton access - */ - public static final NoValueGeneration INSTANCE = new NoValueGeneration(); - - @Override - public GenerationTiming getGenerationTiming() { - return GenerationTiming.NEVER; - } - - @Override - public ValueGenerator getValueGenerator() { - return (session, owner) -> null; - } - - @Override - public boolean referenceColumnInSql() { - return false; - } - - @Override - public String getDatabaseGeneratedReferencedColumnValue() { - return null; - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index dc86911869..fec24cb4ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -68,7 +68,7 @@ import org.hibernate.sql.results.graph.collection.internal.CollectionDomainResul import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch; -import org.hibernate.tuple.ValueGeneration; +import org.hibernate.tuple.Generator; import org.jboss.logging.Logger; @@ -325,9 +325,9 @@ public class PluralAttributeMappingImpl } @Override - public ValueGeneration getValueGeneration() { + public Generator getGenerator() { // can never be a generated value - return NoValueGeneration.INSTANCE; + return null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java index 2a0826e77b..9f854f603b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -80,7 +80,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa keyDeclaringType, keyModelPart, keyPropertyAccess, - NoValueGeneration.INSTANCE, insertable, updateable, keySelectableMapping @@ -146,7 +145,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa keyDeclaringType, targetModelPart, valueAccess, - NoValueGeneration.INSTANCE, keySelectableMapping.isInsertable(), keySelectableMapping.isUpdateable(), keySelectableMapping diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index 9371d10a8e..b1e9f824e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -201,9 +201,7 @@ public class ToOneAttributeMapping adjustFetchTiming( mappedFetchTiming, bootValue ), mappedFetchStyle, declaringType, - propertyAccess, - // can never be a generated value - NoValueGeneration.INSTANCE + propertyAccess ); this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name ); this.isNullable = bootValue.isNullable(); @@ -551,8 +549,7 @@ public class ToOneAttributeMapping original.getAttributeMetadataAccess(), original, declaringType, - original.getPropertyAccess(), - original.getValueGeneration() + original.getPropertyAccess() ); this.navigableRole = original.navigableRole; this.sqlAliasStem = original.sqlAliasStem; @@ -1404,7 +1401,7 @@ public class ToOneAttributeMapping return bidirectionalAttributeName != null && ( !( entityMappingType.getIdentifierMapping() instanceof SingleAttributeIdentifierMapping ) || !targetKeyPropertyNames.contains( - ( (SingleAttributeIdentifierMapping) entityMappingType.getIdentifierMapping() ).getAttributeName() + entityMappingType.getIdentifierMapping().getAttributeName() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java index 1601b28d72..879e73aba4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualEmbeddedAttributeMapping.java @@ -17,7 +17,6 @@ import org.hibernate.metamodel.mapping.VirtualModelPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.sql.ast.tree.from.TableGroupProducer; -import org.hibernate.tuple.Generator; /** * @author Christian Beikov @@ -35,8 +34,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( name, navigableRole, @@ -48,8 +46,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im mappedFetchStyle, embeddableMappingType, declaringType, - propertyAccess, - valueGeneration + propertyAccess ); } @@ -64,8 +61,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im FetchStyle mappedFetchStyle, EmbeddableMappingType embeddableMappingType, ManagedMappingType declaringType, - PropertyAccess propertyAccess, - Generator valueGeneration) { + PropertyAccess propertyAccess) { super( name, navigableRole, @@ -77,8 +73,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im mappedFetchStyle, embeddableMappingType, declaringType, - propertyAccess, - valueGeneration + propertyAccess ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index c012abab72..5f92bd9389 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -2766,16 +2766,17 @@ public abstract class AbstractEntityPersister hasColumns = true; } else { - final Generator valueGeneration = attributeMapping.getValueGeneration(); - if ( valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.generatedByDatabase() ) { - final InDatabaseGenerator generation = (InDatabaseGenerator) valueGeneration; - if ( generation.referenceColumnsInSql() ) { + final Generator generator = attributeMapping.getGenerator(); + if ( generator!=null + && generator.getGenerationTiming().includesUpdate() + && generator.generatedByDatabase() ) { + final InDatabaseGenerator databaseGenerator = (InDatabaseGenerator) generator; + if ( databaseGenerator.referenceColumnsInSql() ) { final Dialect dialect = getFactory().getJdbcServices().getDialect(); update.addColumns( getPropertyColumnNames(index), SINGLE_TRUE, - generation.getReferencedColumnValues(dialect) + databaseGenerator.getReferencedColumnValues(dialect) ); hasColumns = true; } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java index 53245b4afa..c42ad5cb21 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/AbstractMutationCoordinator.java @@ -92,10 +92,10 @@ public abstract class AbstractMutationCoordinator { void handleValueGeneration( AttributeMapping attributeMapping, MutationGroupBuilder mutationGroupBuilder, - InDatabaseGenerator valueGeneration) { + InDatabaseGenerator generator) { final Dialect dialect = factory.getJdbcServices().getDialect(); - final boolean writePropertyValue = valueGeneration.writePropertyValue(); - final String[] columnValues = writePropertyValue ? null : valueGeneration.getReferencedColumnValues( dialect ); + final boolean writePropertyValue = generator.writePropertyValue(); + final String[] columnValues = writePropertyValue ? null : generator.getReferencedColumnValues( dialect ); attributeMapping.forEachSelectable( (j, mapping) -> { final String tableName = entityPersister.physicalTableNameForMutation( mapping ); final ColumnValuesTableMutationBuilder tableUpdateBuilder = mutationGroupBuilder.findTableDetailsBuilder( tableName ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java index 9da4aa938e..df1797b97e 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java @@ -100,10 +100,13 @@ public class InsertCoordinator extends AbstractMutationCoordinator { protected void preInsertInMemoryValueGeneration(Object[] values, Object entity, SharedSessionContractImplementor session) { final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel(); if ( entityMetamodel.hasPreInsertGeneratedValues() ) { - final InMemoryGenerator[] strategies = entityMetamodel.getInMemoryValueGenerationStrategies(); - for ( int i = 0; i < strategies.length; i++ ) { - if ( strategies[i] != null && strategies[i].getGenerationTiming().includesInsert() ) { - values[i] = strategies[i].generate( session, entity, values[i] ); + final Generator[] generators = entityMetamodel.getGenerators(); + for ( int i = 0; i < generators.length; i++ ) { + Generator generator = generators[i]; + if ( generator != null + && !generator.generatedByDatabase() + && generator.getGenerationTiming().includesInsert() ) { + values[i] = ( (InMemoryGenerator) generator).generate( session, entity, values[i] ); entityPersister().setPropertyValue( entity, i, values[i] ); } } @@ -389,13 +392,9 @@ public class InsertCoordinator extends AbstractMutationCoordinator { final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex ); if ( !attributeInclusions[ attributeIndex ] ) { - final Generator valueGeneration = attributeMapping.getValueGeneration(); - if ( isValueGenerationInSql( valueGeneration ) ) { - handleValueGeneration( - attributeMapping, - insertGroupBuilder, - (InDatabaseGenerator) valueGeneration - ); + final Generator generator = attributeMapping.getGenerator(); + if ( isValueGenerationInSql( generator ) ) { + handleValueGeneration( attributeMapping, insertGroupBuilder, (InDatabaseGenerator) generator ); } continue; } @@ -436,9 +435,10 @@ public class InsertCoordinator extends AbstractMutationCoordinator { } ); } - private static boolean isValueGenerationInSql(Generator valueGeneration) { - return valueGeneration.getGenerationTiming().includesInsert() - && valueGeneration.generatedByDatabase() - && ( (InDatabaseGenerator) valueGeneration ).referenceColumnsInSql(); + private static boolean isValueGenerationInSql(Generator generator) { + return generator != null + && generator.getGenerationTiming().includesInsert() + && generator.generatedByDatabase() + && ( (InDatabaseGenerator) generator ).referenceColumnsInSql(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index fcae8bfa53..54bbc6135b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -223,13 +223,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } } - final InclusionChecker updateabilityChecker = (position, attribute) -> { - if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { - return true; - } - - return attributeUpdateability[ position ]; - }; + final InclusionChecker updateabilityChecker = + (position, attribute) -> isValueGenerationInSql( attribute.getGenerator() ) || attributeUpdateability[ position ]; final InclusionChecker dirtinessChecker = (position, attribute) -> { if ( !attributeUpdateability[ position ] ) { @@ -320,10 +315,19 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple } } - private static boolean isValueGenerationInSql(Generator valueGeneration) { - return valueGeneration.getGenerationTiming().includesUpdate() - && valueGeneration.generatedByDatabase() - && ( (InDatabaseGenerator) valueGeneration ).referenceColumnsInSql(); + private boolean isValueGenerationInSql(Generator generator) { + return generator != null + && generator.getGenerationTiming().includesUpdate() + && generator.generatedByDatabase() + && ((InDatabaseGenerator) generator).referenceColumnsInSql(); + } + + private boolean isValueGenerationInSqlNoWrite(Generator generator) { + return generator != null + && generator.getGenerationTiming().includesUpdate() + && generator.generatedByDatabase() + && ((InDatabaseGenerator) generator).referenceColumnsInSql() + && !((InDatabaseGenerator) generator).writePropertyValue(); } /** @@ -415,18 +419,20 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple Object[] newValues, SharedSessionContractImplementor session) { final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel(); - if ( ! entityMetamodel.hasPreUpdateGeneratedValues() ) { + if ( !entityMetamodel.hasPreUpdateGeneratedValues() ) { return EMPTY_INT_ARRAY; } - final InMemoryGenerator[] valueGenerationStrategies = entityMetamodel.getInMemoryValueGenerationStrategies(); - if ( valueGenerationStrategies.length != 0 ) { - final int[] fieldsPreUpdateNeeded = new int[valueGenerationStrategies.length]; + final Generator[] generators = entityMetamodel.getGenerators(); + if ( generators.length != 0 ) { + final int[] fieldsPreUpdateNeeded = new int[generators.length]; int count = 0; - for ( int i = 0; i < valueGenerationStrategies.length; i++ ) { - if ( valueGenerationStrategies[i] != null - && valueGenerationStrategies[i].getGenerationTiming().includesUpdate() ) { - newValues[i] = valueGenerationStrategies[i].generate( session, object, newValues[i] ); + for ( int i = 0; i < generators.length; i++ ) { + Generator generator = generators[i]; + if ( generator != null + && !generator.generatedByDatabase() + && generator.getGenerationTiming().includesUpdate() ) { + newValues[i] = ( (InMemoryGenerator) generator ).generate( session, object, newValues[i] ); entityPersister().setPropertyValue( object, i, newValues[i] ); fieldsPreUpdateNeeded[count++] = i; } @@ -673,9 +679,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple // apply the new values final boolean includeInSet; - final Generator valueGeneration = attributeMapping.getValueGeneration(); - if ( isValueGenerationInSql( valueGeneration ) - && !( (InDatabaseGenerator) valueGeneration ).writePropertyValue() ) { + if ( isValueGenerationInSqlNoWrite( attributeMapping.getGenerator() ) ) { // we applied `#getDatabaseGeneratedReferencedColumnValue` earlier includeInSet = false; } @@ -908,13 +912,9 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple if ( attributeAnalysis.includeInSet() ) { assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping ); - final Generator valueGeneration = attributeMapping.getValueGeneration(); - if ( isValueGenerationInSql( valueGeneration ) ) { - handleValueGeneration( - attributeMapping, - updateGroupBuilder, - (InDatabaseGenerator) valueGeneration - ); + final Generator generator = attributeMapping.getGenerator(); + if ( isValueGenerationInSql( generator ) ) { + handleValueGeneration( attributeMapping, updateGroupBuilder, (InDatabaseGenerator) generator ); } else if ( versionMapping != null && versionMapping.getVersionAttribute() == attributeMapping ) { @@ -1343,13 +1343,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple null, null, null, - (index,attribute) -> { - if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { - return true; - } - - return entityPersister().getPropertyUpdateability()[index]; - }, + (index,attribute) -> isValueGenerationInSql( attribute.getGenerator() ) + || entityPersister().getPropertyUpdateability()[index], (index,attribute) -> { final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle(); if ( optimisticLockStyle.isAll() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java index 466c77b067..cf0a186052 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AbstractNonIdentifierAttribute.java @@ -68,11 +68,6 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i return attributeInformation.isUpdateable(); } - @Override - public Generator getValueGenerationStrategy() { - return attributeInformation.getValueGenerationStrategy(); - } - @Override public boolean isNullable() { return attributeInformation.isNullable(); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationGenerator.java b/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationGenerator.java index 7edbf838cb..3c11ca4f18 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationGenerator.java @@ -7,12 +7,16 @@ package org.hibernate.tuple; import java.lang.annotation.Annotation; +import java.lang.reflect.Member; /** * A {@link Generator} based on a custom Java generator annotation type. * Every instance must implement either {@link InMemoryGenerator} or - * {@link InDatabaseGenerator}. + * {@link InDatabaseGenerator}. Implementing this interface is just a + * slightly more typesafe alternative to providing a constructor with + * the same signature as the method + * {@link #initialize(Annotation, Member, GeneratorCreationContext)}. * * @param The generator annotation type supported by an implementation * @@ -27,14 +31,12 @@ public interface AnnotationGenerator extends Generator { /** * Initializes this generation strategy for the given annotation instance. * - * @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the - * annotation's attribute values and store them in fields. - * @param propertyType the type of the property annotated with the generator annotation. Implementations may use - * the type to determine the right {@link ValueGenerator} to be applied. - * @param entityName the name of the entity to which the annotated property belongs - * @param propertyName the name of the annotated property + * @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the + * annotation's attribute values and store them in fields. + * @param member the Java member annotated with the generator annotation. + * @param context a {@link GeneratorCreationContext} * @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if * an implementation can't create a value for the given property type. */ - void initialize(A annotation, Class propertyType, String entityName, String propertyName); + void initialize(A annotation, Member member, GeneratorCreationContext context); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationValueGeneration.java index 64160f118d..022bad85e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/AnnotationValueGeneration.java @@ -6,7 +6,12 @@ */ package org.hibernate.tuple; +import org.hibernate.AssertionFailure; + import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; /** @@ -32,20 +37,19 @@ public interface AnnotationValueGeneration */ void initialize(A annotation, Class propertyType); - /** - * Initializes this generation strategy for the given annotation instance. - * - * @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the - * annotation's attribute values and store them in fields. - * @param propertyType the type of the property annotated with the generator annotation. Implementations may use - * the type to determine the right {@link ValueGenerator} to be applied. - * @param entityName the name of the entity to which the annotated property belongs - * @param propertyName the name of the annotated property - * - * @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if - * an implementation can't create a value for the given property type. - */ - default void initialize(A annotation, Class propertyType, String entityName, String propertyName) { - initialize( annotation, propertyType ); + default void initialize(A annotation, Member member, GeneratorCreationContext context) { + initialize( annotation, getPropertyType( member ) ); + } + + private static Class getPropertyType(Member member) { + if (member instanceof Field) { + return ((Field) member).getType(); + } + else if (member instanceof Method) { + return ((Method) member).getReturnType(); + } + else { + throw new AssertionFailure("member should have been a method or field"); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java index 4bbda6eceb..d51163f8c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/BaselineAttributeInformation.java @@ -16,19 +16,16 @@ public class BaselineAttributeInformation { private final boolean lazy; private final boolean insertable; private final boolean updateable; - private final Generator generator; private final boolean nullable; private final boolean dirtyCheckable; private final boolean versionable; private final CascadeStyle cascadeStyle; private final FetchMode fetchMode; - private boolean checkable; public BaselineAttributeInformation( boolean lazy, boolean insertable, boolean updateable, - Generator generator, boolean nullable, boolean dirtyCheckable, boolean versionable, @@ -37,7 +34,6 @@ public class BaselineAttributeInformation { this.lazy = lazy; this.insertable = insertable; this.updateable = updateable; - this.generator = generator; this.nullable = nullable; this.dirtyCheckable = dirtyCheckable; this.versionable = versionable; @@ -57,10 +53,6 @@ public class BaselineAttributeInformation { return updateable; } - public Generator getValueGenerationStrategy() { - return generator; - } - public boolean isNullable() { return nullable; } @@ -81,15 +73,10 @@ public class BaselineAttributeInformation { return fetchMode; } - public boolean isCheckable() { - return checkable; - } - public static class Builder { private boolean lazy; private boolean insertable; private boolean updateable; - private Generator generator; private boolean nullable; private boolean dirtyCheckable; private boolean versionable; @@ -111,11 +98,6 @@ public class BaselineAttributeInformation { return this; } - public Builder setValueGenerationStrategy(Generator generator) { - this.generator = generator; - return this; - } - public Builder setNullable(boolean nullable) { this.nullable = nullable; return this; @@ -146,7 +128,6 @@ public class BaselineAttributeInformation { lazy, insertable, updateable, - generator, nullable, dirtyCheckable, versionable, diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/GeneratedValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/GeneratedValueGeneration.java index 10cbe95536..7ec9f83f3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/GeneratedValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/GeneratedValueGeneration.java @@ -9,6 +9,8 @@ package org.hibernate.tuple; import org.hibernate.annotations.Generated; import org.hibernate.dialect.Dialect; +import java.lang.reflect.Member; + import static org.hibernate.internal.util.StringHelper.isEmpty; /** @@ -17,8 +19,7 @@ import static org.hibernate.internal.util.StringHelper.isEmpty; * @author Steve Ebersole * @author Gunnar Morling */ -public class GeneratedValueGeneration - implements AnnotationGenerator, InDatabaseGenerator { +public class GeneratedValueGeneration implements InDatabaseGenerator { private GenerationTiming timing; private boolean writable; @@ -31,8 +32,7 @@ public class GeneratedValueGeneration this.timing = timing; } - @Override - public void initialize(Generated annotation, Class propertyType, String entityName, String propertyName) { + public GeneratedValueGeneration(Generated annotation) { timing = annotation.value().getEquivalent(); sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() }; writable = annotation.writable() || sql != null; diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/GenerationTiming.java b/hibernate-core/src/main/java/org/hibernate/tuple/GenerationTiming.java index dbcaa24b92..3d80849d00 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/GenerationTiming.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/GenerationTiming.java @@ -88,7 +88,7 @@ public enum GenerationTiming { @Override public boolean includes(GenerationTiming timing) { - return timing != NEVER; + return timing.isNotNever(); } }; @@ -101,6 +101,10 @@ public enum GenerationTiming { */ public abstract boolean includesUpdate(); + public boolean isNotNever() { + return this != NEVER; + } + public abstract boolean includes(GenerationTiming timing); public static GenerationTiming parseFromName(String name) { diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/GeneratorCreationContext.java b/hibernate-core/src/main/java/org/hibernate/tuple/GeneratorCreationContext.java new file mode 100644 index 0000000000..6483e4c3b8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tuple/GeneratorCreationContext.java @@ -0,0 +1,26 @@ +/* + * 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.tuple; + +import org.hibernate.Incubating; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.service.ServiceRegistry; + +@Incubating +public interface GeneratorCreationContext { + Database getDatabase(); + ServiceRegistry getServiceRegistry(); + + String getDefaultCatalog(); + String getDefaultSchema(); + + PersistentClass getPersistentClass(); + + Property getProperty(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java index 9994bceb54..e354d38e1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/IdentifierProperty.java @@ -6,7 +6,6 @@ */ package org.hibernate.tuple; -import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.PostInsertIdentifierGenerator; import org.hibernate.type.Type; diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java index 70f0108911..d2eb59223c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/NonIdentifierAttribute.java @@ -19,8 +19,6 @@ public interface NonIdentifierAttribute extends Attribute { boolean isUpdateable(); - Generator getValueGenerationStrategy(); - boolean isNullable(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index 38e843ce83..c2c03d5f80 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -95,7 +95,6 @@ public final class PropertyFactory { .setLazy( lazy ) .setInsertable( property.isInsertable() ) .setUpdateable( property.isUpdateable() ) - .setValueGenerationStrategy( property.getValueGenerationStrategy() ) .setNullable( property.isOptional() ) .setDirtyCheckable( property.isUpdateable() && !lazy ) .setVersionable( property.isOptimisticLocked() ) @@ -166,7 +165,6 @@ public final class PropertyFactory { .setLazy( lazy ) .setInsertable( property.isInsertable() ) .setUpdateable( property.isUpdateable() ) - .setValueGenerationStrategy( property.getValueGenerationStrategy() ) .setNullable( property.isOptional() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setVersionable( property.isOptimisticLocked() ) @@ -186,7 +184,6 @@ public final class PropertyFactory { .setLazy( lazy ) .setInsertable( property.isInsertable() ) .setUpdateable( property.isUpdateable() ) - .setValueGenerationStrategy( property.getValueGenerationStrategy() ) .setNullable( property.isOptional() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setVersionable( property.isOptimisticLocked() ) @@ -208,7 +205,6 @@ public final class PropertyFactory { .setLazy( lazy ) .setInsertable( property.isInsertable() ) .setUpdateable( property.isUpdateable() ) - .setValueGenerationStrategy( property.getValueGenerationStrategy() ) .setNullable( property.isOptional() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setVersionable( property.isOptimisticLocked() ) @@ -269,7 +265,6 @@ public final class PropertyFactory { false, property.isInsertable(), property.isUpdateable(), - property.getValueGenerationStrategy(), property.isOptional(), alwaysDirtyCheck || property.isUpdateable(), property.isOptimisticLocked(), diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java index a7264bc410..7c56ef74bc 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/StandardProperty.java @@ -29,10 +29,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements * @param lazy Should this property be handled lazily? * @param insertable Is this property an insertable value? * @param updateable Is this property an updateable value? - * @param generator How (if) values for this attribute are generated * @param nullable Is this property a nullable value? - * @param checkable Is this property a checkable value? - * @param versionable Is this property a versionable value? * @param cascadeStyle The cascade style for this property's value. * @param fetchMode Any fetch mode defined for this property */ @@ -42,7 +39,6 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements boolean lazy, boolean insertable, boolean updateable, - Generator generator, boolean nullable, boolean checkable, boolean versionable, @@ -58,7 +54,6 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements .setLazy( lazy ) .setInsertable( insertable ) .setUpdateable( updateable ) - .setValueGenerationStrategy(generator) .setNullable( nullable ) .setDirtyCheckable( checkable ) .setVersionable( versionable ) diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/TenantIdGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/TenantIdGeneration.java index d00ae6be21..138661641c 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/TenantIdGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/TenantIdGeneration.java @@ -14,23 +14,25 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.descriptor.java.JavaType; +import java.lang.reflect.Member; + +import static org.hibernate.internal.util.ReflectHelper.getPropertyType; + /** * Value generation implementation for {@link TenantId}. * * @author Gavin King */ -public class TenantIdGeneration - implements AnnotationGenerator, InMemoryGenerator { +public class TenantIdGeneration implements InMemoryGenerator { - private String entityName; - private String propertyName; - private Class propertyType; + private final String entityName; + private final String propertyName; + private final Class propertyType; - @Override - public void initialize(TenantId annotation, Class propertyType, String entityName, String propertyName) { - this.entityName = entityName; - this.propertyName = propertyName; - this.propertyType = propertyType; + public TenantIdGeneration(TenantId annotation, Member member, GeneratorCreationContext context) { + entityName = context.getPersistentClass().getEntityName(); + propertyName = context.getProperty().getName(); + propertyType = getPropertyType( member ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java index 14fb6d72d5..c0c9e1e7fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java @@ -12,7 +12,8 @@ import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.annotations.GeneratorType; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.util.ReflectHelper; + +import static org.hibernate.internal.util.ReflectHelper.getDefaultConstructor; /** * An {@link AnnotationValueGeneration} which delegates to a {@link ValueGenerator}. @@ -20,14 +21,13 @@ import org.hibernate.internal.util.ReflectHelper; * @author Gunnar Morling */ public class VmValueGeneration - implements AnnotationGenerator, InMemoryGenerator { + implements InMemoryGenerator { - private GenerationTiming generationTiming; - ValueGenerator generator; + private final GenerationTiming generationTiming; + private final ValueGenerator generator; - @Override - public void initialize(GeneratorType annotation, Class propertyType, String entityName, String propertyName) { - Constructor> constructor = ReflectHelper.getDefaultConstructor(annotation.type()); + public VmValueGeneration(GeneratorType annotation) { + Constructor> constructor = getDefaultConstructor( annotation.type() ); try { generator = constructor.newInstance(); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 9d1297858e..5267475a29 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -18,6 +18,7 @@ import java.util.Set; import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper; @@ -29,19 +30,21 @@ import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.mapping.Component; +import org.hibernate.mapping.GeneratorCreator; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.Subclass; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.spi.PersisterCreationContext; +import org.hibernate.service.ServiceRegistry; import org.hibernate.tuple.Generator; +import org.hibernate.tuple.GeneratorCreationContext; import org.hibernate.tuple.InDatabaseGenerator; import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.IdentifierProperty; @@ -100,8 +103,7 @@ public class EntityMetamodel implements Serializable { private final boolean hasInsertGeneratedValues; private final boolean hasUpdateGeneratedValues; - private final InMemoryGenerator[] inMemoryValueGenerationStrategies; - private final InDatabaseGenerator[] inDatabaseValueGenerationStrategies; + private final Generator[] generators; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private final Map propertyIndexes = new HashMap<>(); @@ -211,8 +213,7 @@ public class EntityMetamodel implements Serializable { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - this.inMemoryValueGenerationStrategies = new InMemoryGenerator[propertySpan]; - this.inDatabaseValueGenerationStrategies = new InDatabaseGenerator[propertySpan]; + this.generators = new Generator[propertySpan]; boolean foundPreInsertGeneratedValues = false; boolean foundPreUpdateGeneratedValues = false; @@ -300,42 +301,44 @@ public class EntityMetamodel implements Serializable { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - GenerationStrategyPair pair = buildGenerationStrategyPair( sessionFactory, prop ); - inMemoryValueGenerationStrategies[i] = pair.getInMemoryStrategy(); - inDatabaseValueGenerationStrategies[i] = pair.getInDatabaseStrategy(); - - if ( pair.getInMemoryStrategy() != null - && !pair.getInMemoryStrategy().generatedByDatabase() ) { - final GenerationTiming timing = pair.getInMemoryStrategy().getGenerationTiming(); + final Generator generator = buildGenerator( persistentClass, prop, creationContext ); + generators[i] = generator; + if ( generator != null ) { + if ( generatedWithNoParameter( generator ) ) { + propertyInsertability[i] = false; + propertyUpdateability[i] = false; + } // we have some level of generation indicated - switch ( timing ) { + switch ( generator.getGenerationTiming() ) { case INSERT: - foundPreInsertGeneratedValues = true; + if ( generator.generatedByDatabase() ) { + foundPostInsertGeneratedValues = true; + } + else { + foundPreInsertGeneratedValues = true; + } break; case UPDATE: - foundPreUpdateGeneratedValues = true; + if ( generator.generatedByDatabase() ) { + foundPostUpdateGeneratedValues = true; + } + else { + foundPreUpdateGeneratedValues = true; + } break; case ALWAYS: - foundPreInsertGeneratedValues = true; - foundPreUpdateGeneratedValues = true; - break; - } - } - if ( pair.getInDatabaseStrategy() != null - && pair.getInDatabaseStrategy().generatedByDatabase() ) { - switch ( pair.getInDatabaseStrategy().getGenerationTiming() ) { - case INSERT: - foundPostInsertGeneratedValues = true; - break; - case UPDATE: - foundPostUpdateGeneratedValues = true; - break; - case ALWAYS: - foundPostInsertGeneratedValues = true; - foundPostUpdateGeneratedValues = true; + if ( generator.generatedByDatabase() ) { + foundPostInsertGeneratedValues = true; + foundPostUpdateGeneratedValues = true; + } + else { + foundPreInsertGeneratedValues = true; + foundPreUpdateGeneratedValues = true; + } break; } } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( attribute.isLazy() ) { @@ -452,89 +455,79 @@ public class EntityMetamodel implements Serializable { entityNameByInheritanceClassMap = CollectionHelper.toSmallMap( entityNameByInheritanceClassMapLocal ); } - private static GenerationStrategyPair buildGenerationStrategyPair( - final SessionFactoryImplementor sessionFactory, - final Property mappingProperty) { - final Generator valueGeneration = mappingProperty.getValueGenerationStrategy(); - if ( valueGeneration != null && valueGeneration.getGenerationTiming() != GenerationTiming.NEVER ) { - // the property is generated in full. build the generation strategy pair. - if ( !valueGeneration.generatedByDatabase() ) { - // in-memory generation - return new GenerationStrategyPair( (InMemoryGenerator) valueGeneration ); - } - else { - // in-db generation - return new GenerationStrategyPair( (InDatabaseGenerator) valueGeneration ); - } - } - else if ( mappingProperty.getValue() instanceof Component ) { - final CompositeGenerationStrategyPairBuilder builder = new CompositeGenerationStrategyPairBuilder( - mappingProperty, - sessionFactory.getJdbcServices().getDialect() - ); - interpretPartialCompositeValueGeneration( sessionFactory, (Component) mappingProperty.getValue(), builder ); - return builder.buildPair(); - } - - return NO_GEN_PAIR; + private static boolean generatedWithNoParameter(Generator generator) { + return generator.generatedByDatabase() + && !((InDatabaseGenerator) generator).writePropertyValue(); } - private static final GenerationStrategyPair NO_GEN_PAIR = new GenerationStrategyPair(); - - private static void interpretPartialCompositeValueGeneration( - SessionFactoryImplementor sessionFactory, - Component composite, - CompositeGenerationStrategyPairBuilder builder) { - for ( Property property : composite.getProperties() ) { - builder.addPair( buildGenerationStrategyPair( sessionFactory, property ) ); + private static Generator buildGenerator( + final PersistentClass persistentClass, + final Property mappingProperty, + final RuntimeModelCreationContext context) { + final GeneratorCreator generatorCreator = mappingProperty.getValueGenerationStrategy(); + if ( generatorCreator != null ) { + final Generator generator = createGenerator( persistentClass, mappingProperty, context, generatorCreator ); + if ( generator.getGenerationTiming().isNotNever() ) { + return generator; + } } + if ( mappingProperty.getValue() instanceof Component ) { + Dialect dialect = context.getSessionFactory().getJdbcServices().getDialect(); + final CompositeGeneratorBuilder builder = new CompositeGeneratorBuilder( mappingProperty, dialect ); + final Component component = (Component) mappingProperty.getValue(); + for ( Property property : component.getProperties() ) { + builder.addPair( createGenerator( null, property, context, property.getValueGenerationStrategy() ) ); + } + return builder.build(); + } + return null; } - public static class GenerationStrategyPair { - private final InMemoryGenerator inMemoryStrategy; - private final InDatabaseGenerator inDatabaseStrategy; - - public GenerationStrategyPair() { - this( NoInMemoryGenerator.INSTANCE, NoInDatabaseGenerator.INSTANCE ); + private static Generator createGenerator( + PersistentClass persistentClass, + Property mappingProperty, + RuntimeModelCreationContext context, + GeneratorCreator generatorCreator) { + if ( generatorCreator == null ) { + return null; } + return generatorCreator.createGenerator( + new GeneratorCreationContext() { + @Override + public Database getDatabase() { + return context.getMetadata().getDatabase(); + } - public GenerationStrategyPair(InMemoryGenerator inMemoryStrategy) { - this( inMemoryStrategy, NoInDatabaseGenerator.INSTANCE ); - } + @Override + public ServiceRegistry getServiceRegistry() { + return context.getBootstrapContext().getServiceRegistry(); + } - public GenerationStrategyPair(InDatabaseGenerator inDatabaseStrategy) { - this( NoInMemoryGenerator.INSTANCE, inDatabaseStrategy ); - } + @Override + public String getDefaultCatalog() { + return context.getSessionFactory().getSessionFactoryOptions().getDefaultCatalog(); + } - public GenerationStrategyPair( - InMemoryGenerator inMemoryStrategy, - InDatabaseGenerator inDatabaseStrategy) { - // perform some normalization. Also check that only one (if any) strategy is specified - if ( inMemoryStrategy == null ) { - inMemoryStrategy = NoInMemoryGenerator.INSTANCE; - } - if ( inDatabaseStrategy == null ) { - inDatabaseStrategy = NoInDatabaseGenerator.INSTANCE; - } + @Override + public String getDefaultSchema() { + return context.getSessionFactory().getSessionFactoryOptions().getDefaultSchema(); + } - if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER - && inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { - throw new ValueGenerationStrategyException( - "in-memory and in-database value generation are mutually exclusive" - ); - } + @Override + public PersistentClass getPersistentClass() { + return persistentClass; + } - this.inMemoryStrategy = inMemoryStrategy; - this.inDatabaseStrategy = inDatabaseStrategy; - } + @Override + public Property getProperty() { + return mappingProperty; + } + } + ); + } - public InMemoryGenerator getInMemoryStrategy() { - return inMemoryStrategy; - } - - public InDatabaseGenerator getInDatabaseStrategy() { - return inDatabaseStrategy; - } + public Generator[] getGenerators() { + return generators; } public static class ValueGenerationStrategyException extends HibernateException { @@ -543,7 +536,7 @@ public class EntityMetamodel implements Serializable { } } - private static class CompositeGenerationStrategyPairBuilder { + private static class CompositeGeneratorBuilder { private final Property mappingProperty; private final Dialect dialect; @@ -552,18 +545,28 @@ public class EntityMetamodel implements Serializable { private List inDatabaseStrategies; - public CompositeGenerationStrategyPairBuilder(Property mappingProperty, Dialect dialect) { + public CompositeGeneratorBuilder(Property mappingProperty, Dialect dialect) { this.mappingProperty = mappingProperty; this.dialect = dialect; } - public void addPair(GenerationStrategyPair generationStrategyPair) { - add( generationStrategyPair.getInMemoryStrategy() ); - add( generationStrategyPair.getInDatabaseStrategy() ); + public void addPair(Generator generator) { + if ( generator != null ) { + if ( generator.generatedByDatabase() ) { + if ( generator instanceof InDatabaseGenerator ) { + add( (InDatabaseGenerator) generator ); + } + } + else { + if ( generator instanceof InMemoryGenerator ) { + add( (InMemoryGenerator) generator ); + } + } + } } private void add(InMemoryGenerator inMemoryStrategy) { - if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { + if ( inMemoryStrategy.getGenerationTiming().isNotNever() ) { hadInMemoryGeneration = true; } } @@ -574,12 +577,12 @@ public class EntityMetamodel implements Serializable { } inDatabaseStrategies.add( inDatabaseStrategy ); - if ( inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { + if ( inDatabaseStrategy.getGenerationTiming().isNotNever() ) { hadInDatabaseGeneration = true; } } - public GenerationStrategyPair buildPair() { + public Generator build() { if ( hadInMemoryGeneration && hadInDatabaseGeneration ) { throw new ValueGenerationStrategyException( "Composite attribute [" + mappingProperty.getName() + "] contained both in-memory" @@ -593,7 +596,7 @@ public class EntityMetamodel implements Serializable { else if ( hadInDatabaseGeneration ) { final Component composite = (Component) mappingProperty.getValue(); - // we need the numbers to match up so we can properly handle 'referenced sql column values' + // we need the numbers to match up so that we can properly handle 'referenced sql column values' if ( inDatabaseStrategies.size() != composite.getPropertySpan() ) { throw new ValueGenerationStrategyException( "Internal error : mismatch between number of collected in-db generation strategies" + @@ -658,60 +661,23 @@ public class EntityMetamodel implements Serializable { } // then use the aggregated values to build the InDatabaseValueGenerationStrategy - return new GenerationStrategyPair( - new InDatabaseGeneratorImpl( timing, referenceColumns, columnValues ) - ); + return new InDatabaseGeneratorImpl( timing, referenceColumns, columnValues ); } else { - return NO_GEN_PAIR; + return new Generator() { + @Override + public GenerationTiming getGenerationTiming() { + return GenerationTiming.NEVER; + } + @Override + public boolean generatedByDatabase() { + return false; + } + }; } } } - private static class NoInMemoryGenerator implements InMemoryGenerator { - /** - * Singleton access - */ - public static final NoInMemoryGenerator INSTANCE = new NoInMemoryGenerator(); - - @Override - public GenerationTiming getGenerationTiming() { - return GenerationTiming.NEVER; - } - - @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { - return null; - } - } - - private static class NoInDatabaseGenerator implements InDatabaseGenerator { - /** - * Singleton access - */ - public static final NoInDatabaseGenerator INSTANCE = new NoInDatabaseGenerator(); - - @Override - public GenerationTiming getGenerationTiming() { - return GenerationTiming.NEVER; - } - - @Override - public boolean referenceColumnsInSql() { - return true; - } - - @Override - public String[] getReferencedColumnValues(Dialect dialect) { - return null; - } - - @Override - public boolean writePropertyValue() { - return true; - } - } - private static class InDatabaseGeneratorImpl implements InDatabaseGenerator { private final GenerationTiming timing; private final boolean referenceColumnInSql; @@ -769,18 +735,18 @@ public class EntityMetamodel implements Serializable { // Assumptions: // * That code checks that there is a natural identifier before making this call, so we assume the same here // * That code assumes a non-composite natural-id, so we assume the same here - final InDatabaseGenerator strategy = inDatabaseValueGenerationStrategies[ naturalIdPropertyNumbers[0] ]; - return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; + final Generator strategy = generators[ naturalIdPropertyNumbers[0] ]; + return strategy != null && strategy.getGenerationTiming().isNotNever(); } public boolean isVersionGeneratedByDatabase() { - final InDatabaseGenerator strategy = inDatabaseValueGenerationStrategies[ versionPropertyIndex ]; - return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; + final Generator strategy = generators[ versionPropertyIndex ]; + return strategy != null && strategy.getGenerationTiming().isNotNever() && strategy.generatedByDatabase(); } public boolean isVersionGeneratedInMemory() { - final InMemoryGenerator strategy = inMemoryValueGenerationStrategies[ versionPropertyIndex ]; - return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; + final Generator strategy = generators[ versionPropertyIndex ]; + return strategy != null && strategy.getGenerationTiming().isNotNever() && !strategy.generatedByDatabase(); } public int[] getNaturalIdentifierProperties() { @@ -1054,16 +1020,8 @@ public class EntityMetamodel implements Serializable { return hasUpdateGeneratedValues; } - public InMemoryGenerator[] getInMemoryValueGenerationStrategies() { - return inMemoryValueGenerationStrategies; - } - - public InDatabaseGenerator[] getInDatabaseValueGenerationStrategies() { - return inDatabaseValueGenerationStrategies; - } - /** - * Whether or not this class can be lazy (ie intercepted) + * Whether this class can be lazy (ie intercepted) */ public boolean isInstrumented() { return bytecodeEnhancementMetadata.isEnhancedForLazyLoading(); diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index d64f54c554..1ea58e99b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -19,6 +19,7 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.PropertyNotFoundException; +import org.hibernate.Remove; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.CascadeStyle; @@ -38,7 +39,6 @@ import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.tuple.Generator; import org.hibernate.tuple.PropertyFactory; import org.hibernate.tuple.StandardProperty; -import org.hibernate.tuple.ValueGeneration; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.CompositeTypeImplementor; import org.hibernate.usertype.CompositeUserType; @@ -53,7 +53,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen private final String[] propertyNames; private final Type[] propertyTypes; - private final Generator[] propertyValueGenerationStrategies; private final boolean[] propertyNullability; private final int[] originalPropertyOrder; protected final int propertySpan; @@ -76,7 +75,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen this.originalPropertyOrder = originalPropertyOrder; this.propertyNames = new String[propertySpan]; this.propertyTypes = new Type[propertySpan]; - this.propertyValueGenerationStrategies = new ValueGeneration[propertySpan]; this.propertyNullability = new boolean[propertySpan]; this.cascade = new CascadeStyle[propertySpan]; this.joinedFetch = new FetchMode[propertySpan]; @@ -93,7 +91,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( !prop.isNullable() ) { hasNotNullProperty = true; } - this.propertyValueGenerationStrategies[i] = prop.getValueGenerationStrategy(); i++; } @@ -438,8 +435,9 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen return propertyTypes; } + @Deprecated(since = "6.2") @Remove public Generator[] getPropertyValueGenerationStrategies() { - return propertyValueGenerationStrategies; + return null; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java index e465ca47c8..711e185b50 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java @@ -84,12 +84,10 @@ public class GeneratedUuidTests { //end::mapping-generated-custom-ex2[] //tag::mapping-generated-custom-ex3[] - public static class UuidValueGeneration - implements AnnotationGenerator, InMemoryGenerator { - private GenerationTiming timing; + public static class UuidValueGeneration implements InMemoryGenerator { + private final GenerationTiming timing; - @Override - public void initialize(GeneratedUuidValue annotation, Class propertyType, String entityName, String propertyName) { + public UuidValueGeneration(GeneratedUuidValue annotation) { timing = annotation.timing(); } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java index 01248533ce..b2b9de8bc9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/AuditMetadataGenerator.java @@ -30,10 +30,10 @@ import org.hibernate.envers.internal.entities.mapper.CompositeMapperBuilder; import org.hibernate.envers.internal.entities.mapper.ExtendedPropertyMapper; import org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper; import org.hibernate.envers.internal.entities.mapper.SubclassPropertyMapper; +import org.hibernate.mapping.GeneratorCreator; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.SyntheticProperty; -import org.hibernate.tuple.Generator; import org.hibernate.tuple.GeneratedValueGeneration; import org.jboss.logging.Logger; @@ -119,7 +119,8 @@ public final class AuditMetadataGenerator extends AbstractMetadataGenerator { private boolean isPropertyInsertable(Property property) { if ( !property.isInsertable() ) { - final Generator generation = property.getValueGenerationStrategy(); + // TODO: this is now broken by changes to generators + final GeneratorCreator generation = property.getValueGenerationStrategy(); if ( generation instanceof GeneratedValueGeneration ) { final GeneratedValueGeneration valueGeneration = (GeneratedValueGeneration) generation; if ( valueGeneration.getGenerationTiming().includesInsert() ) { @@ -142,7 +143,6 @@ public final class AuditMetadataGenerator extends AbstractMetadataGenerator { return true; } - @SuppressWarnings("unchecked") private void createJoins(PersistentClass persistentClass, JoinAwarePersistentEntity entity, ClassAuditingData auditingData) { final Iterator joins = persistentClass.getJoinIterator(); final Map joinElements = new HashMap<>();