HHH-15789 unify IdentifierGenerator with value generator stuff

This commit is contained in:
Gavin 2022-11-30 16:16:34 +01:00 committed by Gavin King
parent 1bd082bd3e
commit a67cfd039e
51 changed files with 576 additions and 729 deletions

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.annotations; package org.hibernate.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;

View File

@ -13,13 +13,14 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.tuple.AnnotationGenerator;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.GeneratorCreationContext;
import org.hibernate.tuple.InMemoryGenerator; import org.hibernate.tuple.InMemoryGenerator;
import org.hibernate.tuple.TimestampGenerators; import org.hibernate.tuple.TimestampGenerators;
import org.hibernate.tuple.ValueGenerator; import org.hibernate.tuple.ValueGenerator;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.lang.reflect.Member;
import java.sql.CallableStatement; import java.sql.CallableStatement;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -44,22 +45,21 @@ import static org.hibernate.tuple.GenerationTiming.ALWAYS;
@Deprecated(since = "6.2") @Deprecated(since = "6.2")
@Internal @Internal
public class SourceGeneration public class SourceGeneration
implements AnnotationGenerator<Source>, InMemoryGenerator, ValueGenerator<Object> { implements InMemoryGenerator, ValueGenerator<Object> {
private static final CoreMessageLogger log = Logger.getMessageLogger( private static final CoreMessageLogger log = Logger.getMessageLogger(
CoreMessageLogger.class, CoreMessageLogger.class,
SourceGeneration.class.getName() SourceGeneration.class.getName()
); );
private Class<?> propertyType; private final Class<?> propertyType;
private ValueGenerator<?> valueGenerator; private final ValueGenerator<?> valueGenerator;
@Override public SourceGeneration(Source annotation, Member member, GeneratorCreationContext context) {
public void initialize(Source annotation, Class<?> propertyType, String entityName, String propertyName) { this( annotation, context.getProperty().getType().getReturnedClass() );
initialize( annotation, propertyType );
} }
public void initialize(Source annotation, Class<?> propertyType) { public SourceGeneration(Source annotation, Class<?> propertyType) {
this.propertyType = propertyType; this.propertyType = propertyType;
switch ( annotation.value() ) { switch ( annotation.value() ) {
case DB: case DB:

View File

@ -1001,32 +1001,14 @@ public class ModelBinder {
context -> implicitNamingStrategy.determineBasicColumnName( versionAttributeSource ) context -> implicitNamingStrategy.determineBasicColumnName( versionAttributeSource )
); );
Property prop = new Property(); Property property = new Property();
prop.setValue( versionValue ); property.setValue( versionValue );
bindProperty( bindProperty(
sourceDocument, sourceDocument,
versionAttributeSource, 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 ) { if ( versionAttributeSource.getUnsavedValue() != null ) {
versionValue.setNullValue( versionAttributeSource.getUnsavedValue() ); versionValue.setNullValue( versionAttributeSource.getUnsavedValue() );
} }
@ -1034,14 +1016,13 @@ public class ModelBinder {
versionValue.setNullValue( "undefined" ); versionValue.setNullValue( "undefined" );
} }
if ( versionAttributeSource.getSource().equals("db") ) { if ( versionAttributeSource.getSource().equals("db") ) {
SourceGeneration generation = new SourceGeneration(); property.setValueGenerationStrategy(
generation.initialize( DB_SOURCE, prop.getType().getReturnedClass() ); context -> new SourceGeneration( DB_SOURCE, property.getType().getReturnedClass() ) );
prop.setValueGenerationStrategy( generation );
} }
rootEntityDescriptor.setVersion( prop ); rootEntityDescriptor.setVersion( property );
rootEntityDescriptor.setDeclaredVersion( prop ); rootEntityDescriptor.setDeclaredVersion( property );
rootEntityDescriptor.addProperty( prop ); rootEntityDescriptor.addProperty( property );
} }
private void bindEntityDiscriminator( private void bindEntityDiscriminator(
@ -2557,30 +2538,39 @@ public class ModelBinder {
property.setLazy( singularAttributeSource.isBytecodeLazy() ); property.setLazy( singularAttributeSource.isBytecodeLazy() );
final GenerationTiming generationTiming = singularAttributeSource.getGenerationTiming(); final GenerationTiming generationTiming = singularAttributeSource.getGenerationTiming();
if ( generationTiming != null && generationTiming != GenerationTiming.NEVER ) { if ( generationTiming != null ) {
// we had generation specified... if ( (generationTiming == GenerationTiming.INSERT || generationTiming == GenerationTiming.UPDATE)
// HBM only supports "database generated values" && property.getValue() instanceof SimpleValue
property.setValueGenerationStrategy( new GeneratedValueGeneration( generationTiming ) ); && ((SimpleValue) property.getValue()).isVersion() ) {
// this is enforced by DTD, but just make sure
// generated properties can *never* be insertable... throw new MappingException(
if ( property.isInsertable() && generationTiming.includesInsert() ) { "'generated' attribute cannot be 'insert' or 'update' for version/timestamp property",
log.debugf(
"Property [%s] specified %s generation, setting insertable to false : %s",
propertySource.getName(),
generationTiming.name(),
mappingDocument.getOrigin() mappingDocument.getOrigin()
); );
property.setInsertable( false );
} }
if ( generationTiming.isNotNever() ) {
property.setValueGenerationStrategy( context -> new GeneratedValueGeneration( generationTiming ) );
// properties generated on update can never be updatable... // generated properties can *never* be insertable...
if ( property.isUpdateable() && generationTiming.includesUpdate() ) { if ( property.isInsertable() && generationTiming.includesInsert() ) {
log.debugf( log.debugf(
"Property [%s] specified ALWAYS generation, setting updateable to false : %s", "Property [%s] specified %s generation, setting insertable to false : %s",
propertySource.getName(), propertySource.getName(),
mappingDocument.getOrigin() generationTiming.name(),
); mappingDocument.getOrigin()
property.setUpdateable( false ); );
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 );
}
} }
} }
} }

View File

@ -7,8 +7,6 @@
package org.hibernate.cfg; package org.hibernate.cfg;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
@ -19,7 +17,6 @@ import java.util.Map;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.TimeZoneStorageStrategy;
import org.hibernate.annotations.Cascade; import org.hibernate.annotations.Cascade;
@ -72,14 +69,12 @@ import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.CollectionBinder; import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.cfg.annotations.EntityBinder; import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.cfg.annotations.Nullability; import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.cfg.annotations.PropertyBinder; import org.hibernate.cfg.annotations.PropertyBinder;
import org.hibernate.cfg.annotations.QueryBinder; import org.hibernate.cfg.annotations.QueryBinder;
import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.GenericsHelper; import org.hibernate.internal.util.GenericsHelper;
import org.hibernate.mapping.Any; 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.InheritanceState.getSuperclassInheritanceState;
import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder; import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.cfg.annotations.HCANNHelper.findContainingAnnotation; 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.internal.CoreLogging.messageLogger;
import static org.hibernate.mapping.Constraint.hashedName; import static org.hibernate.mapping.Constraint.hashedName;
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY; import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
@ -1812,7 +1808,7 @@ public final class AnnotationBinder {
final XProperty idProperty = inferredData.getProperty(); final XProperty idProperty = inferredData.getProperty();
final Annotation generatorAnnotation = findContainingAnnotation( idProperty, IdGeneratorType.class ); final Annotation generatorAnnotation = findContainingAnnotation( idProperty, IdGeneratorType.class );
if ( generatorAnnotation != null ) { if ( generatorAnnotation != null ) {
setCustomCreator( idValue, idProperty, generatorAnnotation ); idValue.setCustomIdGeneratorCreator( identifierGeneratorCreator( idProperty, generatorAnnotation ) );
} }
else { else {
final XClass entityClass = inferredData.getClassOrElement(); 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<? extends Annotation> annotationType = generatorAnnotation.annotationType();
final IdGeneratorType idGeneratorType = annotationType.getAnnotation( IdGeneratorType.class );
assert idGeneratorType != null;
idValue.setCustomIdGeneratorCreator( creationContext -> {
final Class<? extends IdentifierGenerator> 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( private static void createIdGenerator(
SimpleValue idValue, SimpleValue idValue,
Map<String, IdentifierGeneratorDefinition> classGenerators, Map<String, IdentifierGeneratorDefinition> classGenerators,

View File

@ -14,7 +14,7 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.annotations.AttributeBinderType; import org.hibernate.annotations.AttributeBinderType;
import org.hibernate.annotations.Generated; import org.hibernate.annotations.IdGeneratorType;
import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.OptimisticLock;
@ -29,9 +29,13 @@ import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.InheritanceState; import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder; import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyPreloadedData; 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.internal.CoreMessageLogger;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.IdentifierGeneratorCreator;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass; 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.AnnotationGenerator;
import org.hibernate.tuple.Generator; import org.hibernate.tuple.Generator;
import org.hibernate.tuple.AttributeBinder; import org.hibernate.tuple.AttributeBinder;
import org.hibernate.tuple.InDatabaseGenerator; import org.hibernate.tuple.GeneratorCreationContext;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.ValueGenerator;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.util.Map; import java.util.Map;
import static org.hibernate.cfg.BinderHelper.getMappedSuperclassOrNull; import static org.hibernate.cfg.BinderHelper.getMappedSuperclassOrNull;
@ -322,7 +326,7 @@ public class PropertyBinder {
if ( this.property != null ) { if ( this.property != null ) {
if ( entityBinder != null ) { if ( entityBinder != null ) {
handleNaturalId( property ); handleNaturalId( property );
property.setValueGenerationStrategy( determineValueGenerationStrategy( this.property ) ); property.setValueGenerationStrategy( getValueGenerationFromAnnotations( this.property ) );
} }
// HHH-4635 -- needed for dialect-specific property ordering // HHH-4635 -- needed for dialect-specific property ordering
property.setLob( this.property.isAnnotationPresent( Lob.class ) ); property.setLob( this.property.isAnnotationPresent( Lob.class ) );
@ -379,143 +383,137 @@ public class PropertyBinder {
+ "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" ); + "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" );
} }
if ( property.isAnnotationPresent(EmbeddedId.class) ) { 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'" ); + "' 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. * Returns the value generation strategy for the given property, if any.
*/ */
private Generator getValueGenerationFromAnnotations(XProperty property) { private GeneratorCreator getValueGenerationFromAnnotations(XProperty property) {
Generator valueGeneration = null; GeneratorCreator creator = null;
for ( Annotation annotation : property.getAnnotations() ) { for ( Annotation annotation : property.getAnnotations() ) {
final Generator candidate = getValueGenerationFromAnnotation( property, annotation ); final GeneratorCreator candidate = generatorCreator( property, annotation );
if ( candidate != null ) { if ( candidate != null ) {
if ( valueGeneration != null ) { if ( creator != null ) {
throw new AnnotationException( "Property '" + qualify( holder.getPath(), name ) throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
+ "' has multiple '@ValueGenerationType' annotations" ); + "' has multiple '@ValueGenerationType' annotations" );
} }
else { 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 * 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. * 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 * Instantiates the given generator annotation type, initializing it with the given instance of the corresponding
* generator annotation and the property's type. * generator annotation and the property's type.
*/ */
private <A extends Annotation> Generator instantiateAndInitializeValueGeneration( private <A extends Annotation> GeneratorCreator generatorCreator(XProperty property, A annotation) {
A annotation, final Member member = HCANNHelper.getUnderlyingMember( property );
Class<? extends Generator> generationType, final Class<? extends Annotation> annotationType = annotation.annotationType();
XProperty property) { 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<? extends Annotation> 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 <C, G extends Generator> G instantiateGenerator(
Annotation annotation,
Member member,
Class<? extends Annotation> annotationType,
C creationContext,
Class<C> contextClass,
Class<? extends G> generatorClass) {
try { try {
final Generator valueGeneration = generationType.newInstance(); try {
if ( valueGeneration instanceof AnnotationGenerator) { return generatorClass
// This will cause a CCE in case the generation type doesn't match the annotation type; As this would be .getConstructor( annotationType, Member.class, contextClass )
// a programming error of the generation type developer and thus should show up during testing, we don't .newInstance( annotation, member, creationContext);
// check this explicitly; If required, this could be done e.g. using ClassMate }
@SuppressWarnings("unchecked") catch (NoSuchMethodException ignore) {
final AnnotationGenerator<A> generation = (AnnotationGenerator<A>) valueGeneration; try {
generation.initialize( return generatorClass
annotation, .getConstructor( annotationType )
buildingContext.getBootstrapContext().getReflectionManager().toClass( property.getType() ), .newInstance( annotation );
entityBinder.getPersistentClass().getEntityName(), }
property.getName() catch (NoSuchMethodException i) {
); return generatorClass.newInstance();
}
} }
return valueGeneration;
} }
catch (HibernateException e) { catch (InvocationTargetException|InstantiationException|IllegalAccessException e) {
throw e; throw new HibernateException(
} "Could not instantiate generator of type '" + generatorClass.getName() + "'",
catch (Exception e) { e
throw new AnnotationException(
"Exception occurred during processing of generator annotation: " + qualify(
holder.getPath(),
name
), e
); );
} }
} }
private static class NoValueGeneration implements ValueGeneration { private static <A extends Annotation> void callInitialize(
/** A annotation,
* Singleton access Member member,
*/ GeneratorCreationContext creationContext,
public static final NoValueGeneration INSTANCE = new NoValueGeneration(); Generator generator) {
if ( generator instanceof AnnotationGenerator) {
@Override // This will cause a CCE in case the generation type doesn't match the annotation type; As this would be
public GenerationTiming getGenerationTiming() { // a programming error of the generation type developer and thus should show up during testing, we don't
return GenerationTiming.NEVER; // check this explicitly; If required, this could be done e.g. using ClassMate
@SuppressWarnings("unchecked")
final AnnotationGenerator<A> generation = (AnnotationGenerator<A>) generator;
generation.initialize( annotation, member, creationContext );
} }
}
@Override private void checkVersionGenerationAlways(XProperty property, Generator generator) {
public ValueGenerator<?> getValueGenerator() { if ( property.isAnnotationPresent(Version.class) ) {
return null; final GenerationTiming timing = generator.getGenerationTiming();
} if ( timing != GenerationTiming.ALWAYS ) {
throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
@Override + "' is annotated '@Version' but has a value generator with timing " + timing.name()
public boolean referenceColumnInSql() { + " (the value generation timing must be ALWAYS)"
return true; );
} }
@Override
public String getDatabaseGeneratedReferencedColumnValue() {
return null;
} }
} }

View File

@ -14,8 +14,7 @@ import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.Generator;
import org.hibernate.tuple.InMemoryGenerator;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -74,7 +73,7 @@ public final class Nullability {
/* /*
* Algorithm * Algorithm
* Check for any level one nullability breaks * 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 * recursively check next level of nullability breaks
* Look at Collections containing components to * Look at Collections containing components to
* recursively check next level of nullability breaks * recursively check next level of nullability breaks
@ -82,7 +81,7 @@ public final class Nullability {
* *
* In the previous implementation, not-null stuffs where checked * In the previous implementation, not-null stuffs where checked
* filtering by level one only updatable * 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 * has no effect on not-null check if the main component had good checkability
* In this implementation, we keep this feature. * In this implementation, we keep this feature.
* However, I never see any documentation mentioning that, but it's for * However, I never see any documentation mentioning that, but it's for
@ -94,14 +93,13 @@ public final class Nullability {
? persister.getPropertyInsertability() ? persister.getPropertyInsertability()
: persister.getPropertyUpdateability(); : persister.getPropertyUpdateability();
final Type[] propertyTypes = persister.getPropertyTypes(); final Type[] propertyTypes = persister.getPropertyTypes();
final InMemoryGenerator[] inMemoryValueGenerationStrategies = final Generator[] generators = persister.getEntityMetamodel().getGenerators();
persister.getEntityMetamodel().getInMemoryValueGenerationStrategies();
for ( int i = 0; i < values.length; i++ ) { for ( int i = 0; i < values.length; i++ ) {
if ( checkability[i] && if ( checkability[i]
values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY && && values[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY
GenerationTiming.NEVER == inMemoryValueGenerationStrategies[i].getGenerationTiming() ) { && !generated( generators[i] ) ) {
final Object value = values[i]; final Object value = values[i];
if ( !nullability[i] && value == null ) { if ( !nullability[i] && value == null ) {
//check basic level one nullability //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 * check sub elements-nullability. Returns property path that break
* nullability or null if none * nullability or null if none

View File

@ -55,7 +55,7 @@ public interface IdentifierGenerator extends InMemoryGenerator, ExportableProduc
* identifier generator. * identifier generator.
* *
* @see org.hibernate.annotations.GenericGenerator#name() * @see org.hibernate.annotations.GenericGenerator#name()
* @see jakarta.persistence.GeneratedValue#generator(). * @see jakarta.persistence.GeneratedValue#generator()
*/ */
String GENERATOR_NAME = "GENERATOR_NAME"; String GENERATOR_NAME = "GENERATOR_NAME";

View File

@ -7,19 +7,12 @@
package org.hibernate.id.factory.spi; package org.hibernate.id.factory.spi;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.mapping.RootClass; import org.hibernate.mapping.RootClass;
import org.hibernate.service.ServiceRegistry; import org.hibernate.tuple.GeneratorCreationContext;
@Incubating @Incubating
public interface CustomIdGeneratorCreationContext { public interface CustomIdGeneratorCreationContext extends GeneratorCreationContext {
IdentifierGeneratorFactory getIdentifierGeneratorFactory(); IdentifierGeneratorFactory getIdentifierGeneratorFactory();
Database getDatabase();
ServiceRegistry getServiceRegistry();
String getDefaultCatalog();
String getDefaultSchema();
RootClass getRootClass(); RootClass getRootClass();
} }

View File

@ -6,9 +6,7 @@
*/ */
package org.hibernate.id.uuid; package org.hibernate.id.uuid;
import java.lang.reflect.Field;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.UUID; import java.util.UUID;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -19,6 +17,7 @@ import org.hibernate.type.descriptor.java.UUIDJavaType;
import org.hibernate.type.descriptor.java.UUIDJavaType.ValueTransformer; import org.hibernate.type.descriptor.java.UUIDJavaType.ValueTransformer;
import static org.hibernate.annotations.UuidGenerator.Style.TIME; import static org.hibernate.annotations.UuidGenerator.Style.TIME;
import static org.hibernate.internal.util.ReflectHelper.getPropertyType;
/** /**
* UUID-based IdentifierGenerator * UUID-based IdentifierGenerator
@ -44,13 +43,7 @@ public class UuidGenerator implements StandardGenerator {
generator = StandardRandomStrategy.INSTANCE; generator = StandardRandomStrategy.INSTANCE;
} }
final Class<?> propertyType; final Class<?> propertyType = getPropertyType( idMember );
if ( idMember instanceof Method ) {
propertyType = ( (Method) idMember ).getReturnType();
}
else {
propertyType = ( (Field) idMember ).getType();
}
if ( UUID.class.isAssignableFrom( propertyType ) ) { if ( UUID.class.isAssignableFrom( propertyType ) ) {
valueTransformer = UUIDJavaType.PassThroughTransformer.INSTANCE; valueTransformer = UUIDJavaType.PassThroughTransformer.INSTANCE;

View File

@ -876,4 +876,16 @@ public final class ReflectHelper {
} }
throw new UnsupportedOperationException( "Can't get java type class from type: " + type ); 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");
}
}
} }

View File

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

View File

@ -46,7 +46,17 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl
private static final Alias PK_ALIAS = new Alias( 15, "PK" ); 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"; 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"; public static final String NOT_NULL_DISCRIMINATOR_MAPPING = "not null";
private final MetadataBuildingContext metadataBuildingContext; private final MetadataBuildingContext metadataBuildingContext;

View File

@ -27,7 +27,6 @@ import org.hibernate.property.access.spi.PropertyAccessStrategy;
import org.hibernate.property.access.spi.PropertyAccessStrategyResolver; import org.hibernate.property.access.spi.PropertyAccessStrategyResolver;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.tuple.Generator;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -45,7 +44,7 @@ public class Property implements Serializable, MetaAttributable {
private boolean insertable = true; private boolean insertable = true;
private boolean selectable = true; private boolean selectable = true;
private boolean optimisticLocked = true; private boolean optimisticLocked = true;
private Generator generator; private GeneratorCreator generator;
private String propertyAccessorName; private String propertyAccessorName;
private PropertyAccessStrategy propertyAccessStrategy; private PropertyAccessStrategy propertyAccessStrategy;
private boolean lazy; private boolean lazy;
@ -216,11 +215,11 @@ public class Property implements Serializable, MetaAttributable {
return insertable && value.hasAnyInsertableColumns(); return insertable && value.hasAnyInsertableColumns();
} }
public Generator getValueGenerationStrategy() { public GeneratorCreator getValueGenerationStrategy() {
return generator; return generator;
} }
public void setValueGenerationStrategy(Generator generator) { public void setValueGenerationStrategy(GeneratorCreator generator) {
this.generator = generator; this.generator = generator;
} }

View File

@ -12,6 +12,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.Remove;
import org.hibernate.boot.Metadata; import org.hibernate.boot.Metadata;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
@ -30,7 +31,9 @@ import org.hibernate.persister.entity.EntityPersister;
public class RootClass extends PersistentClass implements TableOwner { public class RootClass extends PersistentClass implements TableOwner {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( RootClass.class ); private static final CoreMessageLogger LOG = CoreLogging.messageLogger( RootClass.class );
@Deprecated(since = "6.2") @Remove
public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id"; public static final String DEFAULT_IDENTIFIER_COLUMN_NAME = "id";
@Deprecated(since = "6.2") @Remove
public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class"; public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "class";
private Property identifierProperty; private Property identifierProperty;

View File

@ -363,11 +363,9 @@ public abstract class SimpleValue implements KeyValue {
private IdentifierGenerator identifierGenerator; private IdentifierGenerator identifierGenerator;
/** /**
* Returns the cached identifierGenerator. * Returns the cached {@link IdentifierGenerator}, or null if
* * {@link #createIdentifierGenerator(IdentifierGeneratorFactory, Dialect, String, String, RootClass)}
* @return IdentifierGenerator null if * was never completed.
* {@link #createIdentifierGenerator(IdentifierGeneratorFactory, Dialect, String, String, RootClass)} was never
* completed.
* *
* @deprecated not used and no longer supported. * @deprecated not used and no longer supported.
*/ */
@ -434,6 +432,16 @@ public abstract class SimpleValue implements KeyValue {
public RootClass getRootClass() { public RootClass getRootClass() {
return rootClass; return rootClass;
} }
@Override
public PersistentClass getPersistentClass() {
return rootClass;
}
@Override
public Property getProperty() {
return rootClass.getIdentifierProperty();
}
}; };
identifierGenerator = customIdGeneratorCreator.createGenerator( creationContext ); identifierGenerator = customIdGeneratorCreator.createGenerator( creationContext );
@ -761,14 +769,7 @@ public abstract class SimpleValue implements KeyValue {
if ( className == null ) { if ( className == null ) {
throw new MappingException( "Attribute types for a dynamic entity must be explicitly specified: " + propertyName ); throw new MappingException( "Attribute types for a dynamic entity must be explicitly specified: " + propertyName );
} }
typeName = ReflectHelper.reflectedPropertyClass( typeName = getClass( className, propertyName ).getName();
className,
propertyName,
getMetadata()
.getMetadataBuildingOptions()
.getServiceRegistry()
.getService( ClassLoaderService.class )
).getName();
// todo : to fully support isNationalized here we need to do the process hinted at above // todo : to fully support isNationalized here we need to do the process hinted at above
// essentially, much of the logic from #buildAttributeConverterTypeAdapter wrt resolving // essentially, much of the logic from #buildAttributeConverterTypeAdapter wrt resolving
// a (1) JdbcType, a (2) JavaType and dynamically building a BasicType // a (1) JdbcType, a (2) JavaType and dynamically building a BasicType
@ -780,6 +781,17 @@ public abstract class SimpleValue implements KeyValue {
type = buildAttributeConverterTypeAdapter(); 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 * 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 * memory, meaning it converts between one Java representation (the entity attribute representation) and another

View File

@ -70,7 +70,7 @@ public interface AttributeMapping
* *
* @apiNote Only relevant for non-id attributes * @apiNote Only relevant for non-id attributes
*/ */
Generator getValueGeneration(); Generator getGenerator();
@Override @Override
default EntityMappingType findContainingEntityMapping() { default EntityMappingType findContainingEntityMapping() {

View File

@ -21,26 +21,18 @@ import org.hibernate.tuple.InMemoryGenerator;
@Incubating @Incubating
public interface GeneratedValueResolver { public interface GeneratedValueResolver {
static GeneratedValueResolver from( static GeneratedValueResolver from(
Generator valueGeneration, Generator generator,
GenerationTiming requestedTiming, GenerationTiming requestedTiming,
int dbSelectionPosition) { 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; 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 { else {
InMemoryGenerator generation = (InMemoryGenerator) valueGeneration; return generator.generatedByDatabase()
return new InMemoryGeneratedValueResolver( generation, requestedTiming ); ? new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition ) // in-db generation (column-default, function, etc)
: new InMemoryGeneratedValueResolver( (InMemoryGenerator) generator, requestedTiming );
} }
} }

View File

@ -156,7 +156,6 @@ public abstract class AbstractEmbeddableMapping implements EmbeddableMappingType
declaringType, declaringType,
original, original,
original.getPropertyAccess(), original.getPropertyAccess(),
original.getValueGeneration(),
selectableMapping.isInsertable(), selectableMapping.isInsertable(),
selectableMapping.isUpdateable(), selectableMapping.isUpdateable(),
selectableMapping selectableMapping

View File

@ -23,7 +23,6 @@ public abstract class AbstractSingularAttributeMapping
implements SingularAttributeMapping { implements SingularAttributeMapping {
private final PropertyAccess propertyAccess; private final PropertyAccess propertyAccess;
private final Generator valueGeneration;
public AbstractSingularAttributeMapping( public AbstractSingularAttributeMapping(
String name, String name,
@ -31,13 +30,9 @@ public abstract class AbstractSingularAttributeMapping
AttributeMetadataAccess attributeMetadataAccess, AttributeMetadataAccess attributeMetadataAccess,
FetchOptions mappedFetchOptions, FetchOptions mappedFetchOptions,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( name, attributeMetadataAccess, mappedFetchOptions, stateArrayPosition, declaringType ); super( name, attributeMetadataAccess, mappedFetchOptions, stateArrayPosition, declaringType );
this.propertyAccess = propertyAccess; this.propertyAccess = propertyAccess;
this.valueGeneration = valueGeneration != null
? valueGeneration
: NoValueGeneration.INSTANCE;
} }
public AbstractSingularAttributeMapping( public AbstractSingularAttributeMapping(
@ -47,13 +42,9 @@ public abstract class AbstractSingularAttributeMapping
FetchTiming fetchTiming, FetchTiming fetchTiming,
FetchStyle fetchStyle, FetchStyle fetchStyle,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( name, attributeMetadataAccess, fetchTiming, fetchStyle, stateArrayPosition, declaringType ); super( name, attributeMetadataAccess, fetchTiming, fetchStyle, stateArrayPosition, declaringType );
this.propertyAccess = propertyAccess; this.propertyAccess = propertyAccess;
this.valueGeneration = valueGeneration != null
? valueGeneration
: NoValueGeneration.INSTANCE;
} }
@Override @Override
@ -62,7 +53,7 @@ public abstract class AbstractSingularAttributeMapping
} }
@Override @Override
public Generator getValueGeneration() { public Generator getGenerator() {
return valueGeneration; return findContainingEntityMapping().getEntityPersister().getEntityMetamodel().getGenerators()[getStateArrayPosition()];
} }
} }

View File

@ -11,6 +11,7 @@ import java.util.function.BiConsumer;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.IndexedConsumer; import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.metamodel.mapping.AttributeMetadataAccess; import org.hibernate.metamodel.mapping.AttributeMetadataAccess;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; 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.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.tuple.Generator;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
/** /**
@ -86,8 +86,7 @@ public class BasicAttributeMapping
boolean updateable, boolean updateable,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( super(
attributeName, attributeName,
stateArrayPosition, stateArrayPosition,
@ -95,8 +94,7 @@ public class BasicAttributeMapping
mappedFetchTiming, mappedFetchTiming,
mappedFetchStyle, mappedFetchStyle,
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
this.navigableRole = navigableRole; this.navigableRole = navigableRole;
this.tableExpression = tableExpression; this.tableExpression = tableExpression;
@ -126,7 +124,6 @@ public class BasicAttributeMapping
ManagedMappingType declaringType, ManagedMappingType declaringType,
BasicValuedModelPart original, BasicValuedModelPart original,
PropertyAccess propertyAccess, PropertyAccess propertyAccess,
Generator valueGeneration,
boolean insertable, boolean insertable,
boolean updateable, boolean updateable,
SelectableMapping selectableMapping) { SelectableMapping selectableMapping) {
@ -168,8 +165,7 @@ public class BasicAttributeMapping
updateable, updateable,
original.getJdbcMapping(), original.getJdbcMapping(),
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
} }

View File

@ -85,8 +85,7 @@ public class DiscriminatedAssociationAttributeMapping
fetchTiming, fetchTiming,
FetchStyle.SELECT, FetchStyle.SELECT,
declaringType, declaringType,
propertyAccess, propertyAccess
null
); );
this.navigableRole = attributeRole; this.navigableRole = attributeRole;

View File

@ -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.EmbeddableValuedFetchable;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl;
import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.tuple.Generator;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -79,8 +78,7 @@ public class EmbeddedAttributeMapping
FetchStyle mappedFetchStyle, FetchStyle mappedFetchStyle,
EmbeddableMappingType embeddableMappingType, EmbeddableMappingType embeddableMappingType,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
this( this(
name, name,
navigableRole, navigableRole,
@ -92,8 +90,7 @@ public class EmbeddedAttributeMapping
mappedFetchStyle, mappedFetchStyle,
embeddableMappingType, embeddableMappingType,
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
} }
@ -108,8 +105,7 @@ public class EmbeddedAttributeMapping
FetchStyle mappedFetchStyle, FetchStyle mappedFetchStyle,
EmbeddableMappingType embeddableMappingType, EmbeddableMappingType embeddableMappingType,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( super(
name, name,
stateArrayPosition, stateArrayPosition,
@ -117,8 +113,7 @@ public class EmbeddedAttributeMapping
mappedFetchTiming, mappedFetchTiming,
mappedFetchStyle, mappedFetchStyle,
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
this.navigableRole = navigableRole; this.navigableRole = navigableRole;
@ -145,8 +140,7 @@ public class EmbeddedAttributeMapping
keyDeclaringType, keyDeclaringType,
inverseModelPart instanceof PropertyBasedMapping ? inverseModelPart instanceof PropertyBasedMapping ?
( (PropertyBasedMapping) inverseModelPart ).getPropertyAccess() : ( (PropertyBasedMapping) inverseModelPart ).getPropertyAccess() :
null, null
null
); );
this.navigableRole = inverseModelPart.getNavigableRole().getParent().append( inverseModelPart.getFetchableName() ); this.navigableRole = inverseModelPart.getNavigableRole().getParent().append( inverseModelPart.getFetchableName() );

View File

@ -35,7 +35,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.InDatabaseGenerator; import org.hibernate.tuple.Generator;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -59,43 +59,9 @@ public class GeneratedValuesProcessor {
// NOTE: we only care about db-generated values here. in-memory generation // NOTE: we only care about db-generated values here. in-memory generation
// is applied before the insert/update happens. // is applied before the insert/update happens.
final List<AttributeMapping> generatedValuesToSelect = new ArrayList<>(); // todo (6.0): for now, we rely on the entity metamodel as composite attributes report
// todo (6.0): for now, we rely on the entity metamodel as composite attributes report GenerationTiming.NEVER // GenerationTiming.NEVER even if they have attributes that would need generation
// even if they have attributes that would need generation final List<AttributeMapping> generatedValuesToSelect = getGeneratedValues( entityDescriptor, timing );
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 );
});
if ( generatedValuesToSelect.isEmpty() ) { if ( generatedValuesToSelect.isEmpty() ) {
selectStatement = null; selectStatement = null;
} }
@ -114,6 +80,25 @@ public class GeneratedValuesProcessor {
} }
} }
private List<AttributeMapping> getGeneratedValues(EntityMappingType entityDescriptor, GenerationTiming timing) {
final Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators();
final List<AttributeMapping> 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) { public void processGeneratedValues(Object entity, Object id, Object[] state, SharedSessionContractImplementor session) {
if ( selectStatement == null ) { if ( selectStatement == null ) {
return; return;

View File

@ -114,8 +114,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
FetchStyle.JOIN, FetchStyle.JOIN,
this, this,
identifiedEntityMapping, identifiedEntityMapping,
propertyAccess, propertyAccess
null
); );
final CompositeType idClassType = (CompositeType) idClassSource.getType(); final CompositeType idClassType = (CompositeType) idClassSource.getType();

View File

@ -287,8 +287,7 @@ public class MappingModelCreationHelper {
updateable, updateable,
attrType, attrType,
declaringType, declaringType,
propertyAccess, propertyAccess
bootProperty.getValueGenerationStrategy()
); );
} }
@ -333,8 +332,7 @@ public class MappingModelCreationHelper {
FetchStyle.JOIN, FetchStyle.JOIN,
attributeMappingType, attributeMappingType,
declaringType, declaringType,
propertyAccess, propertyAccess
bootProperty.getValueGenerationStrategy()
); );
} }
else { else {
@ -349,8 +347,7 @@ public class MappingModelCreationHelper {
FetchStyle.JOIN, FetchStyle.JOIN,
attributeMappingType, attributeMappingType,
declaringType, declaringType,
propertyAccess, propertyAccess
bootProperty.getValueGenerationStrategy()
); );
} }
}, },

View File

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

View File

@ -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.DelayedCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch;
import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.Generator;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -325,9 +325,9 @@ public class PluralAttributeMappingImpl
} }
@Override @Override
public ValueGeneration getValueGeneration() { public Generator getGenerator() {
// can never be a generated value // can never be a generated value
return NoValueGeneration.INSTANCE; return null;
} }
@Override @Override

View File

@ -80,7 +80,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
keyDeclaringType, keyDeclaringType,
keyModelPart, keyModelPart,
keyPropertyAccess, keyPropertyAccess,
NoValueGeneration.INSTANCE,
insertable, insertable,
updateable, updateable,
keySelectableMapping keySelectableMapping
@ -146,7 +145,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
keyDeclaringType, keyDeclaringType,
targetModelPart, targetModelPart,
valueAccess, valueAccess,
NoValueGeneration.INSTANCE,
keySelectableMapping.isInsertable(), keySelectableMapping.isInsertable(),
keySelectableMapping.isUpdateable(), keySelectableMapping.isUpdateable(),
keySelectableMapping keySelectableMapping

View File

@ -201,9 +201,7 @@ public class ToOneAttributeMapping
adjustFetchTiming( mappedFetchTiming, bootValue ), adjustFetchTiming( mappedFetchTiming, bootValue ),
mappedFetchStyle, mappedFetchStyle,
declaringType, declaringType,
propertyAccess, propertyAccess
// can never be a generated value
NoValueGeneration.INSTANCE
); );
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name ); this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( name );
this.isNullable = bootValue.isNullable(); this.isNullable = bootValue.isNullable();
@ -551,8 +549,7 @@ public class ToOneAttributeMapping
original.getAttributeMetadataAccess(), original.getAttributeMetadataAccess(),
original, original,
declaringType, declaringType,
original.getPropertyAccess(), original.getPropertyAccess()
original.getValueGeneration()
); );
this.navigableRole = original.navigableRole; this.navigableRole = original.navigableRole;
this.sqlAliasStem = original.sqlAliasStem; this.sqlAliasStem = original.sqlAliasStem;
@ -1404,7 +1401,7 @@ public class ToOneAttributeMapping
return bidirectionalAttributeName != null && ( return bidirectionalAttributeName != null && (
!( entityMappingType.getIdentifierMapping() instanceof SingleAttributeIdentifierMapping ) !( entityMappingType.getIdentifierMapping() instanceof SingleAttributeIdentifierMapping )
|| !targetKeyPropertyNames.contains( || !targetKeyPropertyNames.contains(
( (SingleAttributeIdentifierMapping) entityMappingType.getIdentifierMapping() ).getAttributeName() entityMappingType.getIdentifierMapping().getAttributeName()
) )
); );
} }

View File

@ -17,7 +17,6 @@ import org.hibernate.metamodel.mapping.VirtualModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.tuple.Generator;
/** /**
* @author Christian Beikov * @author Christian Beikov
@ -35,8 +34,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im
FetchStyle mappedFetchStyle, FetchStyle mappedFetchStyle,
EmbeddableMappingType embeddableMappingType, EmbeddableMappingType embeddableMappingType,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( super(
name, name,
navigableRole, navigableRole,
@ -48,8 +46,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im
mappedFetchStyle, mappedFetchStyle,
embeddableMappingType, embeddableMappingType,
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
} }
@ -64,8 +61,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im
FetchStyle mappedFetchStyle, FetchStyle mappedFetchStyle,
EmbeddableMappingType embeddableMappingType, EmbeddableMappingType embeddableMappingType,
ManagedMappingType declaringType, ManagedMappingType declaringType,
PropertyAccess propertyAccess, PropertyAccess propertyAccess) {
Generator valueGeneration) {
super( super(
name, name,
navigableRole, navigableRole,
@ -77,8 +73,7 @@ public class VirtualEmbeddedAttributeMapping extends EmbeddedAttributeMapping im
mappedFetchStyle, mappedFetchStyle,
embeddableMappingType, embeddableMappingType,
declaringType, declaringType,
propertyAccess, propertyAccess
valueGeneration
); );
} }

View File

@ -2766,16 +2766,17 @@ public abstract class AbstractEntityPersister
hasColumns = true; hasColumns = true;
} }
else { else {
final Generator valueGeneration = attributeMapping.getValueGeneration(); final Generator generator = attributeMapping.getGenerator();
if ( valueGeneration.getGenerationTiming().includesUpdate() if ( generator!=null
&& valueGeneration.generatedByDatabase() ) { && generator.getGenerationTiming().includesUpdate()
final InDatabaseGenerator generation = (InDatabaseGenerator) valueGeneration; && generator.generatedByDatabase() ) {
if ( generation.referenceColumnsInSql() ) { final InDatabaseGenerator databaseGenerator = (InDatabaseGenerator) generator;
if ( databaseGenerator.referenceColumnsInSql() ) {
final Dialect dialect = getFactory().getJdbcServices().getDialect(); final Dialect dialect = getFactory().getJdbcServices().getDialect();
update.addColumns( update.addColumns(
getPropertyColumnNames(index), getPropertyColumnNames(index),
SINGLE_TRUE, SINGLE_TRUE,
generation.getReferencedColumnValues(dialect) databaseGenerator.getReferencedColumnValues(dialect)
); );
hasColumns = true; hasColumns = true;
} }

View File

@ -92,10 +92,10 @@ public abstract class AbstractMutationCoordinator {
void handleValueGeneration( void handleValueGeneration(
AttributeMapping attributeMapping, AttributeMapping attributeMapping,
MutationGroupBuilder mutationGroupBuilder, MutationGroupBuilder mutationGroupBuilder,
InDatabaseGenerator valueGeneration) { InDatabaseGenerator generator) {
final Dialect dialect = factory.getJdbcServices().getDialect(); final Dialect dialect = factory.getJdbcServices().getDialect();
final boolean writePropertyValue = valueGeneration.writePropertyValue(); final boolean writePropertyValue = generator.writePropertyValue();
final String[] columnValues = writePropertyValue ? null : valueGeneration.getReferencedColumnValues( dialect ); final String[] columnValues = writePropertyValue ? null : generator.getReferencedColumnValues( dialect );
attributeMapping.forEachSelectable( (j, mapping) -> { attributeMapping.forEachSelectable( (j, mapping) -> {
final String tableName = entityPersister.physicalTableNameForMutation( mapping ); final String tableName = entityPersister.physicalTableNameForMutation( mapping );
final ColumnValuesTableMutationBuilder tableUpdateBuilder = mutationGroupBuilder.findTableDetailsBuilder( tableName ); final ColumnValuesTableMutationBuilder tableUpdateBuilder = mutationGroupBuilder.findTableDetailsBuilder( tableName );

View File

@ -100,10 +100,13 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
protected void preInsertInMemoryValueGeneration(Object[] values, Object entity, SharedSessionContractImplementor session) { protected void preInsertInMemoryValueGeneration(Object[] values, Object entity, SharedSessionContractImplementor session) {
final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel(); final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel();
if ( entityMetamodel.hasPreInsertGeneratedValues() ) { if ( entityMetamodel.hasPreInsertGeneratedValues() ) {
final InMemoryGenerator[] strategies = entityMetamodel.getInMemoryValueGenerationStrategies(); final Generator[] generators = entityMetamodel.getGenerators();
for ( int i = 0; i < strategies.length; i++ ) { for ( int i = 0; i < generators.length; i++ ) {
if ( strategies[i] != null && strategies[i].getGenerationTiming().includesInsert() ) { Generator generator = generators[i];
values[i] = strategies[i].generate( session, entity, values[i] ); if ( generator != null
&& !generator.generatedByDatabase()
&& generator.getGenerationTiming().includesInsert() ) {
values[i] = ( (InMemoryGenerator) generator).generate( session, entity, values[i] );
entityPersister().setPropertyValue( entity, i, values[i] ); entityPersister().setPropertyValue( entity, i, values[i] );
} }
} }
@ -389,13 +392,9 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex ); final AttributeMapping attributeMapping = attributeMappings.get( attributeIndex );
if ( !attributeInclusions[ attributeIndex ] ) { if ( !attributeInclusions[ attributeIndex ] ) {
final Generator valueGeneration = attributeMapping.getValueGeneration(); final Generator generator = attributeMapping.getGenerator();
if ( isValueGenerationInSql( valueGeneration ) ) { if ( isValueGenerationInSql( generator ) ) {
handleValueGeneration( handleValueGeneration( attributeMapping, insertGroupBuilder, (InDatabaseGenerator) generator );
attributeMapping,
insertGroupBuilder,
(InDatabaseGenerator) valueGeneration
);
} }
continue; continue;
} }
@ -436,9 +435,10 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
} ); } );
} }
private static boolean isValueGenerationInSql(Generator valueGeneration) { private static boolean isValueGenerationInSql(Generator generator) {
return valueGeneration.getGenerationTiming().includesInsert() return generator != null
&& valueGeneration.generatedByDatabase() && generator.getGenerationTiming().includesInsert()
&& ( (InDatabaseGenerator) valueGeneration ).referenceColumnsInSql(); && generator.generatedByDatabase()
&& ( (InDatabaseGenerator) generator ).referenceColumnsInSql();
} }
} }

View File

@ -223,13 +223,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
} }
} }
final InclusionChecker updateabilityChecker = (position, attribute) -> { final InclusionChecker updateabilityChecker =
if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { (position, attribute) -> isValueGenerationInSql( attribute.getGenerator() ) || attributeUpdateability[ position ];
return true;
}
return attributeUpdateability[ position ];
};
final InclusionChecker dirtinessChecker = (position, attribute) -> { final InclusionChecker dirtinessChecker = (position, attribute) -> {
if ( !attributeUpdateability[ position ] ) { if ( !attributeUpdateability[ position ] ) {
@ -320,10 +315,19 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
} }
} }
private static boolean isValueGenerationInSql(Generator valueGeneration) { private boolean isValueGenerationInSql(Generator generator) {
return valueGeneration.getGenerationTiming().includesUpdate() return generator != null
&& valueGeneration.generatedByDatabase() && generator.getGenerationTiming().includesUpdate()
&& ( (InDatabaseGenerator) valueGeneration ).referenceColumnsInSql(); && 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, Object[] newValues,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel(); final EntityMetamodel entityMetamodel = entityPersister().getEntityMetamodel();
if ( ! entityMetamodel.hasPreUpdateGeneratedValues() ) { if ( !entityMetamodel.hasPreUpdateGeneratedValues() ) {
return EMPTY_INT_ARRAY; return EMPTY_INT_ARRAY;
} }
final InMemoryGenerator[] valueGenerationStrategies = entityMetamodel.getInMemoryValueGenerationStrategies(); final Generator[] generators = entityMetamodel.getGenerators();
if ( valueGenerationStrategies.length != 0 ) { if ( generators.length != 0 ) {
final int[] fieldsPreUpdateNeeded = new int[valueGenerationStrategies.length]; final int[] fieldsPreUpdateNeeded = new int[generators.length];
int count = 0; int count = 0;
for ( int i = 0; i < valueGenerationStrategies.length; i++ ) { for ( int i = 0; i < generators.length; i++ ) {
if ( valueGenerationStrategies[i] != null Generator generator = generators[i];
&& valueGenerationStrategies[i].getGenerationTiming().includesUpdate() ) { if ( generator != null
newValues[i] = valueGenerationStrategies[i].generate( session, object, newValues[i] ); && !generator.generatedByDatabase()
&& generator.getGenerationTiming().includesUpdate() ) {
newValues[i] = ( (InMemoryGenerator) generator ).generate( session, object, newValues[i] );
entityPersister().setPropertyValue( object, i, newValues[i] ); entityPersister().setPropertyValue( object, i, newValues[i] );
fieldsPreUpdateNeeded[count++] = i; fieldsPreUpdateNeeded[count++] = i;
} }
@ -673,9 +679,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
// apply the new values // apply the new values
final boolean includeInSet; final boolean includeInSet;
final Generator valueGeneration = attributeMapping.getValueGeneration(); if ( isValueGenerationInSqlNoWrite( attributeMapping.getGenerator() ) ) {
if ( isValueGenerationInSql( valueGeneration )
&& !( (InDatabaseGenerator) valueGeneration ).writePropertyValue() ) {
// we applied `#getDatabaseGeneratedReferencedColumnValue` earlier // we applied `#getDatabaseGeneratedReferencedColumnValue` earlier
includeInSet = false; includeInSet = false;
} }
@ -908,13 +912,9 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
if ( attributeAnalysis.includeInSet() ) { if ( attributeAnalysis.includeInSet() ) {
assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping ); assert updateValuesAnalysis.tablesNeedingUpdate.contains( tableMapping );
final Generator valueGeneration = attributeMapping.getValueGeneration(); final Generator generator = attributeMapping.getGenerator();
if ( isValueGenerationInSql( valueGeneration ) ) { if ( isValueGenerationInSql( generator ) ) {
handleValueGeneration( handleValueGeneration( attributeMapping, updateGroupBuilder, (InDatabaseGenerator) generator );
attributeMapping,
updateGroupBuilder,
(InDatabaseGenerator) valueGeneration
);
} }
else if ( versionMapping != null else if ( versionMapping != null
&& versionMapping.getVersionAttribute() == attributeMapping ) { && versionMapping.getVersionAttribute() == attributeMapping ) {
@ -1343,13 +1343,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
null, null,
null, null,
null, null,
(index,attribute) -> { (index,attribute) -> isValueGenerationInSql( attribute.getGenerator() )
if ( isValueGenerationInSql( attribute.getValueGeneration() ) ) { || entityPersister().getPropertyUpdateability()[index],
return true;
}
return entityPersister().getPropertyUpdateability()[index];
},
(index,attribute) -> { (index,attribute) -> {
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle(); final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle();
if ( optimisticLockStyle.isAll() ) { if ( optimisticLockStyle.isAll() ) {

View File

@ -68,11 +68,6 @@ public abstract class AbstractNonIdentifierAttribute extends AbstractAttribute i
return attributeInformation.isUpdateable(); return attributeInformation.isUpdateable();
} }
@Override
public Generator getValueGenerationStrategy() {
return attributeInformation.getValueGenerationStrategy();
}
@Override @Override
public boolean isNullable() { public boolean isNullable() {
return attributeInformation.isNullable(); return attributeInformation.isNullable();

View File

@ -7,12 +7,16 @@
package org.hibernate.tuple; package org.hibernate.tuple;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
/** /**
* A {@link Generator} based on a custom Java generator annotation type. * A {@link Generator} based on a custom Java generator annotation type.
* Every instance must implement either {@link InMemoryGenerator} or * 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 <A> The generator annotation type supported by an implementation * @param <A> The generator annotation type supported by an implementation
* *
@ -27,14 +31,12 @@ public interface AnnotationGenerator<A extends Annotation> extends Generator {
/** /**
* Initializes this generation strategy for the given annotation instance. * Initializes this generation strategy for the given annotation instance.
* *
* @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the * @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the
* annotation's attribute values and store them in fields. * annotation's attribute values and store them in fields.
* @param propertyType the type of the property annotated with the generator annotation. Implementations may use * @param member the Java member annotated with the generator annotation.
* the type to determine the right {@link ValueGenerator} to be applied. * @param context a {@link GeneratorCreationContext}
* @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 * @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. * 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);
} }

View File

@ -6,7 +6,12 @@
*/ */
package org.hibernate.tuple; package org.hibernate.tuple;
import org.hibernate.AssertionFailure;
import java.lang.annotation.Annotation; 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<A extends Annotation>
*/ */
void initialize(A annotation, Class<?> propertyType); void initialize(A annotation, Class<?> propertyType);
/** default void initialize(A annotation, Member member, GeneratorCreationContext context) {
* Initializes this generation strategy for the given annotation instance. initialize( annotation, getPropertyType( member ) );
* }
* @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the
* annotation's attribute values and store them in fields. private static Class<?> getPropertyType(Member member) {
* @param propertyType the type of the property annotated with the generator annotation. Implementations may use if (member instanceof Field) {
* the type to determine the right {@link ValueGenerator} to be applied. return ((Field) member).getType();
* @param entityName the name of the entity to which the annotated property belongs }
* @param propertyName the name of the annotated property else if (member instanceof Method) {
* return ((Method) member).getReturnType();
* @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. else {
*/ throw new AssertionFailure("member should have been a method or field");
default void initialize(A annotation, Class<?> propertyType, String entityName, String propertyName) { }
initialize( annotation, propertyType );
} }
} }

View File

@ -16,19 +16,16 @@ public class BaselineAttributeInformation {
private final boolean lazy; private final boolean lazy;
private final boolean insertable; private final boolean insertable;
private final boolean updateable; private final boolean updateable;
private final Generator generator;
private final boolean nullable; private final boolean nullable;
private final boolean dirtyCheckable; private final boolean dirtyCheckable;
private final boolean versionable; private final boolean versionable;
private final CascadeStyle cascadeStyle; private final CascadeStyle cascadeStyle;
private final FetchMode fetchMode; private final FetchMode fetchMode;
private boolean checkable;
public BaselineAttributeInformation( public BaselineAttributeInformation(
boolean lazy, boolean lazy,
boolean insertable, boolean insertable,
boolean updateable, boolean updateable,
Generator generator,
boolean nullable, boolean nullable,
boolean dirtyCheckable, boolean dirtyCheckable,
boolean versionable, boolean versionable,
@ -37,7 +34,6 @@ public class BaselineAttributeInformation {
this.lazy = lazy; this.lazy = lazy;
this.insertable = insertable; this.insertable = insertable;
this.updateable = updateable; this.updateable = updateable;
this.generator = generator;
this.nullable = nullable; this.nullable = nullable;
this.dirtyCheckable = dirtyCheckable; this.dirtyCheckable = dirtyCheckable;
this.versionable = versionable; this.versionable = versionable;
@ -57,10 +53,6 @@ public class BaselineAttributeInformation {
return updateable; return updateable;
} }
public Generator getValueGenerationStrategy() {
return generator;
}
public boolean isNullable() { public boolean isNullable() {
return nullable; return nullable;
} }
@ -81,15 +73,10 @@ public class BaselineAttributeInformation {
return fetchMode; return fetchMode;
} }
public boolean isCheckable() {
return checkable;
}
public static class Builder { public static class Builder {
private boolean lazy; private boolean lazy;
private boolean insertable; private boolean insertable;
private boolean updateable; private boolean updateable;
private Generator generator;
private boolean nullable; private boolean nullable;
private boolean dirtyCheckable; private boolean dirtyCheckable;
private boolean versionable; private boolean versionable;
@ -111,11 +98,6 @@ public class BaselineAttributeInformation {
return this; return this;
} }
public Builder setValueGenerationStrategy(Generator generator) {
this.generator = generator;
return this;
}
public Builder setNullable(boolean nullable) { public Builder setNullable(boolean nullable) {
this.nullable = nullable; this.nullable = nullable;
return this; return this;
@ -146,7 +128,6 @@ public class BaselineAttributeInformation {
lazy, lazy,
insertable, insertable,
updateable, updateable,
generator,
nullable, nullable,
dirtyCheckable, dirtyCheckable,
versionable, versionable,

View File

@ -9,6 +9,8 @@ package org.hibernate.tuple;
import org.hibernate.annotations.Generated; import org.hibernate.annotations.Generated;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import java.lang.reflect.Member;
import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isEmpty;
/** /**
@ -17,8 +19,7 @@ import static org.hibernate.internal.util.StringHelper.isEmpty;
* @author Steve Ebersole * @author Steve Ebersole
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class GeneratedValueGeneration public class GeneratedValueGeneration implements InDatabaseGenerator {
implements AnnotationGenerator<Generated>, InDatabaseGenerator {
private GenerationTiming timing; private GenerationTiming timing;
private boolean writable; private boolean writable;
@ -31,8 +32,7 @@ public class GeneratedValueGeneration
this.timing = timing; this.timing = timing;
} }
@Override public GeneratedValueGeneration(Generated annotation) {
public void initialize(Generated annotation, Class<?> propertyType, String entityName, String propertyName) {
timing = annotation.value().getEquivalent(); timing = annotation.value().getEquivalent();
sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() }; sql = isEmpty( annotation.sql() ) ? null : new String[] { annotation.sql() };
writable = annotation.writable() || sql != null; writable = annotation.writable() || sql != null;

View File

@ -88,7 +88,7 @@ public enum GenerationTiming {
@Override @Override
public boolean includes(GenerationTiming timing) { public boolean includes(GenerationTiming timing) {
return timing != NEVER; return timing.isNotNever();
} }
}; };
@ -101,6 +101,10 @@ public enum GenerationTiming {
*/ */
public abstract boolean includesUpdate(); public abstract boolean includesUpdate();
public boolean isNotNever() {
return this != NEVER;
}
public abstract boolean includes(GenerationTiming timing); public abstract boolean includes(GenerationTiming timing);
public static GenerationTiming parseFromName(String name) { public static GenerationTiming parseFromName(String name) {

View File

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

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.tuple; package org.hibernate.tuple;
import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator; import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.type.Type; import org.hibernate.type.Type;

View File

@ -19,8 +19,6 @@ public interface NonIdentifierAttribute extends Attribute {
boolean isUpdateable(); boolean isUpdateable();
Generator getValueGenerationStrategy();
boolean isNullable(); boolean isNullable();
/** /**

View File

@ -95,7 +95,6 @@ public final class PropertyFactory {
.setLazy( lazy ) .setLazy( lazy )
.setInsertable( property.isInsertable() ) .setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() ) .setUpdateable( property.isUpdateable() )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() ) .setNullable( property.isOptional() )
.setDirtyCheckable( property.isUpdateable() && !lazy ) .setDirtyCheckable( property.isUpdateable() && !lazy )
.setVersionable( property.isOptimisticLocked() ) .setVersionable( property.isOptimisticLocked() )
@ -166,7 +165,6 @@ public final class PropertyFactory {
.setLazy( lazy ) .setLazy( lazy )
.setInsertable( property.isInsertable() ) .setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() ) .setUpdateable( property.isUpdateable() )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() ) .setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() ) .setVersionable( property.isOptimisticLocked() )
@ -186,7 +184,6 @@ public final class PropertyFactory {
.setLazy( lazy ) .setLazy( lazy )
.setInsertable( property.isInsertable() ) .setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() ) .setUpdateable( property.isUpdateable() )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() ) .setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() ) .setVersionable( property.isOptimisticLocked() )
@ -208,7 +205,6 @@ public final class PropertyFactory {
.setLazy( lazy ) .setLazy( lazy )
.setInsertable( property.isInsertable() ) .setInsertable( property.isInsertable() )
.setUpdateable( property.isUpdateable() ) .setUpdateable( property.isUpdateable() )
.setValueGenerationStrategy( property.getValueGenerationStrategy() )
.setNullable( property.isOptional() ) .setNullable( property.isOptional() )
.setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() ) .setDirtyCheckable( alwaysDirtyCheck || property.isUpdateable() )
.setVersionable( property.isOptimisticLocked() ) .setVersionable( property.isOptimisticLocked() )
@ -269,7 +265,6 @@ public final class PropertyFactory {
false, false,
property.isInsertable(), property.isInsertable(),
property.isUpdateable(), property.isUpdateable(),
property.getValueGenerationStrategy(),
property.isOptional(), property.isOptional(),
alwaysDirtyCheck || property.isUpdateable(), alwaysDirtyCheck || property.isUpdateable(),
property.isOptimisticLocked(), property.isOptimisticLocked(),

View File

@ -29,10 +29,7 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
* @param lazy Should this property be handled lazily? * @param lazy Should this property be handled lazily?
* @param insertable Is this property an insertable value? * @param insertable Is this property an insertable value?
* @param updateable Is this property an updateable 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 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 cascadeStyle The cascade style for this property's value.
* @param fetchMode Any fetch mode defined for this property * @param fetchMode Any fetch mode defined for this property
*/ */
@ -42,7 +39,6 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
boolean lazy, boolean lazy,
boolean insertable, boolean insertable,
boolean updateable, boolean updateable,
Generator generator,
boolean nullable, boolean nullable,
boolean checkable, boolean checkable,
boolean versionable, boolean versionable,
@ -58,7 +54,6 @@ public class StandardProperty extends AbstractNonIdentifierAttribute implements
.setLazy( lazy ) .setLazy( lazy )
.setInsertable( insertable ) .setInsertable( insertable )
.setUpdateable( updateable ) .setUpdateable( updateable )
.setValueGenerationStrategy(generator)
.setNullable( nullable ) .setNullable( nullable )
.setDirtyCheckable( checkable ) .setDirtyCheckable( checkable )
.setVersionable( versionable ) .setVersionable( versionable )

View File

@ -14,23 +14,25 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.JavaType; 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}. * Value generation implementation for {@link TenantId}.
* *
* @author Gavin King * @author Gavin King
*/ */
public class TenantIdGeneration public class TenantIdGeneration implements InMemoryGenerator {
implements AnnotationGenerator<TenantId>, InMemoryGenerator {
private String entityName; private final String entityName;
private String propertyName; private final String propertyName;
private Class<?> propertyType; private final Class<?> propertyType;
@Override public TenantIdGeneration(TenantId annotation, Member member, GeneratorCreationContext context) {
public void initialize(TenantId annotation, Class<?> propertyType, String entityName, String propertyName) { entityName = context.getPersistentClass().getEntityName();
this.entityName = entityName; propertyName = context.getProperty().getName();
this.propertyName = propertyName; propertyType = getPropertyType( member );
this.propertyType = propertyType;
} }
@Override @Override

View File

@ -12,7 +12,8 @@ import org.hibernate.HibernateException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.annotations.GeneratorType; import org.hibernate.annotations.GeneratorType;
import org.hibernate.engine.spi.SharedSessionContractImplementor; 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}. * An {@link AnnotationValueGeneration} which delegates to a {@link ValueGenerator}.
@ -20,14 +21,13 @@ import org.hibernate.internal.util.ReflectHelper;
* @author Gunnar Morling * @author Gunnar Morling
*/ */
public class VmValueGeneration public class VmValueGeneration
implements AnnotationGenerator<GeneratorType>, InMemoryGenerator { implements InMemoryGenerator {
private GenerationTiming generationTiming; private final GenerationTiming generationTiming;
ValueGenerator<?> generator; private final ValueGenerator<?> generator;
@Override public VmValueGeneration(GeneratorType annotation) {
public void initialize(GeneratorType annotation, Class<?> propertyType, String entityName, String propertyName) { Constructor<? extends ValueGenerator<?>> constructor = getDefaultConstructor( annotation.type() );
Constructor<? extends ValueGenerator<?>> constructor = ReflectHelper.getDefaultConstructor(annotation.type());
try { try {
generator = constructor.newInstance(); generator = constructor.newInstance();
} }

View File

@ -18,6 +18,7 @@ import java.util.Set;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper; 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.CascadeStyles;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Subclass;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tuple.Generator; import org.hibernate.tuple.Generator;
import org.hibernate.tuple.GeneratorCreationContext;
import org.hibernate.tuple.InDatabaseGenerator; import org.hibernate.tuple.InDatabaseGenerator;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.IdentifierProperty; import org.hibernate.tuple.IdentifierProperty;
@ -100,8 +103,7 @@ public class EntityMetamodel implements Serializable {
private final boolean hasInsertGeneratedValues; private final boolean hasInsertGeneratedValues;
private final boolean hasUpdateGeneratedValues; private final boolean hasUpdateGeneratedValues;
private final InMemoryGenerator[] inMemoryValueGenerationStrategies; private final Generator[] generators;
private final InDatabaseGenerator[] inDatabaseValueGenerationStrategies;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final Map<String, Integer> propertyIndexes = new HashMap<>(); private final Map<String, Integer> propertyIndexes = new HashMap<>();
@ -211,8 +213,7 @@ public class EntityMetamodel implements Serializable {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.inMemoryValueGenerationStrategies = new InMemoryGenerator[propertySpan]; this.generators = new Generator[propertySpan];
this.inDatabaseValueGenerationStrategies = new InDatabaseGenerator[propertySpan];
boolean foundPreInsertGeneratedValues = false; boolean foundPreInsertGeneratedValues = false;
boolean foundPreUpdateGeneratedValues = false; boolean foundPreUpdateGeneratedValues = false;
@ -300,42 +301,44 @@ public class EntityMetamodel implements Serializable {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GenerationStrategyPair pair = buildGenerationStrategyPair( sessionFactory, prop ); final Generator generator = buildGenerator( persistentClass, prop, creationContext );
inMemoryValueGenerationStrategies[i] = pair.getInMemoryStrategy(); generators[i] = generator;
inDatabaseValueGenerationStrategies[i] = pair.getInDatabaseStrategy(); if ( generator != null ) {
if ( generatedWithNoParameter( generator ) ) {
if ( pair.getInMemoryStrategy() != null propertyInsertability[i] = false;
&& !pair.getInMemoryStrategy().generatedByDatabase() ) { propertyUpdateability[i] = false;
final GenerationTiming timing = pair.getInMemoryStrategy().getGenerationTiming(); }
// we have some level of generation indicated // we have some level of generation indicated
switch ( timing ) { switch ( generator.getGenerationTiming() ) {
case INSERT: case INSERT:
foundPreInsertGeneratedValues = true; if ( generator.generatedByDatabase() ) {
foundPostInsertGeneratedValues = true;
}
else {
foundPreInsertGeneratedValues = true;
}
break; break;
case UPDATE: case UPDATE:
foundPreUpdateGeneratedValues = true; if ( generator.generatedByDatabase() ) {
foundPostUpdateGeneratedValues = true;
}
else {
foundPreUpdateGeneratedValues = true;
}
break; break;
case ALWAYS: case ALWAYS:
foundPreInsertGeneratedValues = true; if ( generator.generatedByDatabase() ) {
foundPreUpdateGeneratedValues = true; foundPostInsertGeneratedValues = true;
break; foundPostUpdateGeneratedValues = true;
} }
} else {
if ( pair.getInDatabaseStrategy() != null foundPreInsertGeneratedValues = true;
&& pair.getInDatabaseStrategy().generatedByDatabase() ) { foundPreUpdateGeneratedValues = true;
switch ( pair.getInDatabaseStrategy().getGenerationTiming() ) { }
case INSERT:
foundPostInsertGeneratedValues = true;
break;
case UPDATE:
foundPostUpdateGeneratedValues = true;
break;
case ALWAYS:
foundPostInsertGeneratedValues = true;
foundPostUpdateGeneratedValues = true;
break; break;
} }
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( attribute.isLazy() ) { if ( attribute.isLazy() ) {
@ -452,89 +455,79 @@ public class EntityMetamodel implements Serializable {
entityNameByInheritanceClassMap = CollectionHelper.toSmallMap( entityNameByInheritanceClassMapLocal ); entityNameByInheritanceClassMap = CollectionHelper.toSmallMap( entityNameByInheritanceClassMapLocal );
} }
private static GenerationStrategyPair buildGenerationStrategyPair( private static boolean generatedWithNoParameter(Generator generator) {
final SessionFactoryImplementor sessionFactory, return generator.generatedByDatabase()
final Property mappingProperty) { && !((InDatabaseGenerator) generator).writePropertyValue();
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 final GenerationStrategyPair NO_GEN_PAIR = new GenerationStrategyPair(); private static Generator buildGenerator(
final PersistentClass persistentClass,
private static void interpretPartialCompositeValueGeneration( final Property mappingProperty,
SessionFactoryImplementor sessionFactory, final RuntimeModelCreationContext context) {
Component composite, final GeneratorCreator generatorCreator = mappingProperty.getValueGenerationStrategy();
CompositeGenerationStrategyPairBuilder builder) { if ( generatorCreator != null ) {
for ( Property property : composite.getProperties() ) { final Generator generator = createGenerator( persistentClass, mappingProperty, context, generatorCreator );
builder.addPair( buildGenerationStrategyPair( sessionFactory, property ) ); 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 static Generator createGenerator(
private final InMemoryGenerator inMemoryStrategy; PersistentClass persistentClass,
private final InDatabaseGenerator inDatabaseStrategy; Property mappingProperty,
RuntimeModelCreationContext context,
public GenerationStrategyPair() { GeneratorCreator generatorCreator) {
this( NoInMemoryGenerator.INSTANCE, NoInDatabaseGenerator.INSTANCE ); if ( generatorCreator == null ) {
return null;
} }
return generatorCreator.createGenerator(
new GeneratorCreationContext() {
@Override
public Database getDatabase() {
return context.getMetadata().getDatabase();
}
public GenerationStrategyPair(InMemoryGenerator inMemoryStrategy) { @Override
this( inMemoryStrategy, NoInDatabaseGenerator.INSTANCE ); public ServiceRegistry getServiceRegistry() {
} return context.getBootstrapContext().getServiceRegistry();
}
public GenerationStrategyPair(InDatabaseGenerator inDatabaseStrategy) { @Override
this( NoInMemoryGenerator.INSTANCE, inDatabaseStrategy ); public String getDefaultCatalog() {
} return context.getSessionFactory().getSessionFactoryOptions().getDefaultCatalog();
}
public GenerationStrategyPair( @Override
InMemoryGenerator inMemoryStrategy, public String getDefaultSchema() {
InDatabaseGenerator inDatabaseStrategy) { return context.getSessionFactory().getSessionFactoryOptions().getDefaultSchema();
// 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;
}
if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER @Override
&& inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { public PersistentClass getPersistentClass() {
throw new ValueGenerationStrategyException( return persistentClass;
"in-memory and in-database value generation are mutually exclusive" }
);
}
this.inMemoryStrategy = inMemoryStrategy; @Override
this.inDatabaseStrategy = inDatabaseStrategy; public Property getProperty() {
} return mappingProperty;
}
}
);
}
public InMemoryGenerator getInMemoryStrategy() { public Generator[] getGenerators() {
return inMemoryStrategy; return generators;
}
public InDatabaseGenerator getInDatabaseStrategy() {
return inDatabaseStrategy;
}
} }
public static class ValueGenerationStrategyException extends HibernateException { 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 Property mappingProperty;
private final Dialect dialect; private final Dialect dialect;
@ -552,18 +545,28 @@ public class EntityMetamodel implements Serializable {
private List<InDatabaseGenerator> inDatabaseStrategies; private List<InDatabaseGenerator> inDatabaseStrategies;
public CompositeGenerationStrategyPairBuilder(Property mappingProperty, Dialect dialect) { public CompositeGeneratorBuilder(Property mappingProperty, Dialect dialect) {
this.mappingProperty = mappingProperty; this.mappingProperty = mappingProperty;
this.dialect = dialect; this.dialect = dialect;
} }
public void addPair(GenerationStrategyPair generationStrategyPair) { public void addPair(Generator generator) {
add( generationStrategyPair.getInMemoryStrategy() ); if ( generator != null ) {
add( generationStrategyPair.getInDatabaseStrategy() ); if ( generator.generatedByDatabase() ) {
if ( generator instanceof InDatabaseGenerator ) {
add( (InDatabaseGenerator) generator );
}
}
else {
if ( generator instanceof InMemoryGenerator ) {
add( (InMemoryGenerator) generator );
}
}
}
} }
private void add(InMemoryGenerator inMemoryStrategy) { private void add(InMemoryGenerator inMemoryStrategy) {
if ( inMemoryStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { if ( inMemoryStrategy.getGenerationTiming().isNotNever() ) {
hadInMemoryGeneration = true; hadInMemoryGeneration = true;
} }
} }
@ -574,12 +577,12 @@ public class EntityMetamodel implements Serializable {
} }
inDatabaseStrategies.add( inDatabaseStrategy ); inDatabaseStrategies.add( inDatabaseStrategy );
if ( inDatabaseStrategy.getGenerationTiming() != GenerationTiming.NEVER ) { if ( inDatabaseStrategy.getGenerationTiming().isNotNever() ) {
hadInDatabaseGeneration = true; hadInDatabaseGeneration = true;
} }
} }
public GenerationStrategyPair buildPair() { public Generator build() {
if ( hadInMemoryGeneration && hadInDatabaseGeneration ) { if ( hadInMemoryGeneration && hadInDatabaseGeneration ) {
throw new ValueGenerationStrategyException( throw new ValueGenerationStrategyException(
"Composite attribute [" + mappingProperty.getName() + "] contained both in-memory" "Composite attribute [" + mappingProperty.getName() + "] contained both in-memory"
@ -593,7 +596,7 @@ public class EntityMetamodel implements Serializable {
else if ( hadInDatabaseGeneration ) { else if ( hadInDatabaseGeneration ) {
final Component composite = (Component) mappingProperty.getValue(); 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() ) { if ( inDatabaseStrategies.size() != composite.getPropertySpan() ) {
throw new ValueGenerationStrategyException( throw new ValueGenerationStrategyException(
"Internal error : mismatch between number of collected in-db generation strategies" + "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 // then use the aggregated values to build the InDatabaseValueGenerationStrategy
return new GenerationStrategyPair( return new InDatabaseGeneratorImpl( timing, referenceColumns, columnValues );
new InDatabaseGeneratorImpl( timing, referenceColumns, columnValues )
);
} }
else { 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 static class InDatabaseGeneratorImpl implements InDatabaseGenerator {
private final GenerationTiming timing; private final GenerationTiming timing;
private final boolean referenceColumnInSql; private final boolean referenceColumnInSql;
@ -769,18 +735,18 @@ public class EntityMetamodel implements Serializable {
// Assumptions: // Assumptions:
// * That code checks that there is a natural identifier before making this call, so we assume the same here // * 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 // * That code assumes a non-composite natural-id, so we assume the same here
final InDatabaseGenerator strategy = inDatabaseValueGenerationStrategies[ naturalIdPropertyNumbers[0] ]; final Generator strategy = generators[ naturalIdPropertyNumbers[0] ];
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; return strategy != null && strategy.getGenerationTiming().isNotNever();
} }
public boolean isVersionGeneratedByDatabase() { public boolean isVersionGeneratedByDatabase() {
final InDatabaseGenerator strategy = inDatabaseValueGenerationStrategies[ versionPropertyIndex ]; final Generator strategy = generators[ versionPropertyIndex ];
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; return strategy != null && strategy.getGenerationTiming().isNotNever() && strategy.generatedByDatabase();
} }
public boolean isVersionGeneratedInMemory() { public boolean isVersionGeneratedInMemory() {
final InMemoryGenerator strategy = inMemoryValueGenerationStrategies[ versionPropertyIndex ]; final Generator strategy = generators[ versionPropertyIndex ];
return strategy != null && strategy.getGenerationTiming() != GenerationTiming.NEVER; return strategy != null && strategy.getGenerationTiming().isNotNever() && !strategy.generatedByDatabase();
} }
public int[] getNaturalIdentifierProperties() { public int[] getNaturalIdentifierProperties() {
@ -1054,16 +1020,8 @@ public class EntityMetamodel implements Serializable {
return hasUpdateGeneratedValues; 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() { public boolean isInstrumented() {
return bytecodeEnhancementMetadata.isEnhancedForLazyLoading(); return bytecodeEnhancementMetadata.isEnhancedForLazyLoading();

View File

@ -19,6 +19,7 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.PropertyNotFoundException; import org.hibernate.PropertyNotFoundException;
import org.hibernate.Remove;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.engine.spi.CascadeStyle; 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.Generator;
import org.hibernate.tuple.PropertyFactory; import org.hibernate.tuple.PropertyFactory;
import org.hibernate.tuple.StandardProperty; import org.hibernate.tuple.StandardProperty;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.CompositeTypeImplementor; import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.usertype.CompositeUserType; import org.hibernate.usertype.CompositeUserType;
@ -53,7 +53,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
private final String[] propertyNames; private final String[] propertyNames;
private final Type[] propertyTypes; private final Type[] propertyTypes;
private final Generator[] propertyValueGenerationStrategies;
private final boolean[] propertyNullability; private final boolean[] propertyNullability;
private final int[] originalPropertyOrder; private final int[] originalPropertyOrder;
protected final int propertySpan; protected final int propertySpan;
@ -76,7 +75,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
this.originalPropertyOrder = originalPropertyOrder; this.originalPropertyOrder = originalPropertyOrder;
this.propertyNames = new String[propertySpan]; this.propertyNames = new String[propertySpan];
this.propertyTypes = new Type[propertySpan]; this.propertyTypes = new Type[propertySpan];
this.propertyValueGenerationStrategies = new ValueGeneration[propertySpan];
this.propertyNullability = new boolean[propertySpan]; this.propertyNullability = new boolean[propertySpan];
this.cascade = new CascadeStyle[propertySpan]; this.cascade = new CascadeStyle[propertySpan];
this.joinedFetch = new FetchMode[propertySpan]; this.joinedFetch = new FetchMode[propertySpan];
@ -93,7 +91,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
if ( !prop.isNullable() ) { if ( !prop.isNullable() ) {
hasNotNullProperty = true; hasNotNullProperty = true;
} }
this.propertyValueGenerationStrategies[i] = prop.getValueGenerationStrategy();
i++; i++;
} }
@ -438,8 +435,9 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return propertyTypes; return propertyTypes;
} }
@Deprecated(since = "6.2") @Remove
public Generator[] getPropertyValueGenerationStrategies() { public Generator[] getPropertyValueGenerationStrategies() {
return propertyValueGenerationStrategies; return null;
} }
@Override @Override

View File

@ -84,12 +84,10 @@ public class GeneratedUuidTests {
//end::mapping-generated-custom-ex2[] //end::mapping-generated-custom-ex2[]
//tag::mapping-generated-custom-ex3[] //tag::mapping-generated-custom-ex3[]
public static class UuidValueGeneration public static class UuidValueGeneration implements InMemoryGenerator {
implements AnnotationGenerator<GeneratedUuidValue>, InMemoryGenerator { private final GenerationTiming timing;
private GenerationTiming timing;
@Override public UuidValueGeneration(GeneratedUuidValue annotation) {
public void initialize(GeneratedUuidValue annotation, Class<?> propertyType, String entityName, String propertyName) {
timing = annotation.timing(); timing = annotation.timing();
} }

View File

@ -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.ExtendedPropertyMapper;
import org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper; import org.hibernate.envers.internal.entities.mapper.MultiPropertyMapper;
import org.hibernate.envers.internal.entities.mapper.SubclassPropertyMapper; import org.hibernate.envers.internal.entities.mapper.SubclassPropertyMapper;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.SyntheticProperty; import org.hibernate.mapping.SyntheticProperty;
import org.hibernate.tuple.Generator;
import org.hibernate.tuple.GeneratedValueGeneration; import org.hibernate.tuple.GeneratedValueGeneration;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -119,7 +119,8 @@ public final class AuditMetadataGenerator extends AbstractMetadataGenerator {
private boolean isPropertyInsertable(Property property) { private boolean isPropertyInsertable(Property property) {
if ( !property.isInsertable() ) { 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 ) { if ( generation instanceof GeneratedValueGeneration ) {
final GeneratedValueGeneration valueGeneration = (GeneratedValueGeneration) generation; final GeneratedValueGeneration valueGeneration = (GeneratedValueGeneration) generation;
if ( valueGeneration.getGenerationTiming().includesInsert() ) { if ( valueGeneration.getGenerationTiming().includesInsert() ) {
@ -142,7 +143,6 @@ public final class AuditMetadataGenerator extends AbstractMetadataGenerator {
return true; return true;
} }
@SuppressWarnings("unchecked")
private void createJoins(PersistentClass persistentClass, JoinAwarePersistentEntity entity, ClassAuditingData auditingData) { private void createJoins(PersistentClass persistentClass, JoinAwarePersistentEntity entity, ClassAuditingData auditingData) {
final Iterator<org.hibernate.mapping.Join> joins = persistentClass.getJoinIterator(); final Iterator<org.hibernate.mapping.Join> joins = persistentClass.getJoinIterator();
final Map<org.hibernate.mapping.Join, Join> joinElements = new HashMap<>(); final Map<org.hibernate.mapping.Join, Join> joinElements = new HashMap<>();