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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -876,4 +876,16 @@ public final class ReflectHelper {
}
throw new UnsupportedOperationException( "Can't get java type class from type: " + type );
}
public static Class<?> getPropertyType(Member member) {
if (member instanceof Field) {
return ( (Field) member ).getType();
}
else if (member instanceof Method) {
return ( (Method) member ).getReturnType();
}
else {
throw new AssertionFailure("member should have been a method or field");
}
}
}

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" );
/**
* The magic value of {@link jakarta.persistence.DiscriminatorValue#value}
* which indicates that the subclass is distinguished by a null value of the
* discriminator column.
*/
public static final String NULL_DISCRIMINATOR_MAPPING = "null";
/**
* The magic value of {@link jakarta.persistence.DiscriminatorValue#value}
* which indicates that the subclass is distinguished by any non-null value
* of the discriminator column.
*/
public static final String NOT_NULL_DISCRIMINATOR_MAPPING = "not null";
private final MetadataBuildingContext metadataBuildingContext;

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

View File

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

View File

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

View File

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

View File

@ -21,26 +21,18 @@ import org.hibernate.tuple.InMemoryGenerator;
@Incubating
public interface GeneratedValueResolver {
static GeneratedValueResolver from(
Generator valueGeneration,
Generator generator,
GenerationTiming requestedTiming,
int dbSelectionPosition) {
assert requestedTiming != GenerationTiming.NEVER;
assert requestedTiming.isNotNever();
if ( valueGeneration == null || !valueGeneration.getGenerationTiming().includes( requestedTiming ) ) {
if ( generator == null || !generator.getGenerationTiming().includes( requestedTiming ) ) {
return NoGeneratedValueResolver.INSTANCE;
}
// todo (6.x) : incorporate `org.hibernate.tuple.InDatabaseValueGenerationStrategy`
// and `org.hibernate.tuple.InMemoryValueGenerationStrategy` from `EntityMetamodel`.
// this requires unification of the read and write (insert/update) aspects of
// value generation which we'll circle back to as we convert write operations to
// use the "runtime mapping" (`org.hibernate.metamodel.mapping`) model
else if ( valueGeneration.generatedByDatabase() ) {
// in-db generation (column-default, function, etc)
return new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition );
}
else {
InMemoryGenerator generation = (InMemoryGenerator) valueGeneration;
return new InMemoryGeneratedValueResolver( generation, requestedTiming );
return generator.generatedByDatabase()
? new InDatabaseGeneratedValueResolver( requestedTiming, dbSelectionPosition ) // in-db generation (column-default, function, etc)
: new InMemoryGeneratedValueResolver( (InMemoryGenerator) generator, requestedTiming );
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.InDatabaseGenerator;
import org.hibernate.tuple.Generator;
/**
* @author Steve Ebersole
@ -59,43 +59,9 @@ public class GeneratedValuesProcessor {
// NOTE: we only care about db-generated values here. in-memory generation
// is applied before the insert/update happens.
final List<AttributeMapping> generatedValuesToSelect = new ArrayList<>();
// todo (6.0): for now, we rely on the entity metamodel as composite attributes report GenerationTiming.NEVER
// even if they have attributes that would need generation
final InDatabaseGenerator[] inDatabaseValueGenerationStrategies = entityDescriptor.getEntityPersister()
.getEntityMetamodel()
.getInDatabaseValueGenerationStrategies();
entityDescriptor.visitAttributeMappings( mapping -> {
final InDatabaseGenerator inDatabaseValueGenerationStrategy =
inDatabaseValueGenerationStrategies[ mapping.getStateArrayPosition() ];
if ( inDatabaseValueGenerationStrategy.getGenerationTiming() == GenerationTiming.NEVER ) {
return;
}
final GeneratedValueResolver generatedValueResolver = new InDatabaseGeneratedValueResolver(
timing,
generatedValuesToSelect.size()
);
// if ( attr.getValueGeneration().getGenerationTiming() == GenerationTiming.NEVER ) {
// return;
// }
//
// final GeneratedValueResolver generatedValueResolver = GeneratedValueResolver.from(
// attr.getValueGeneration(),
// timing,
// generatedValuesToSelect.size()
// );
//
// //noinspection RedundantClassCall
// if ( ! InDatabaseGeneratedValueResolver.class.isInstance( generatedValueResolver ) ) {
// // again, we only care about in in-db generations here
// return;
// }
// this attribute is generated for the timing we are processing...
valueDescriptors.add( new GeneratedValueDescriptor( generatedValueResolver, mapping ) );
generatedValuesToSelect.add( mapping );
});
// todo (6.0): for now, we rely on the entity metamodel as composite attributes report
// GenerationTiming.NEVER even if they have attributes that would need generation
final List<AttributeMapping> generatedValuesToSelect = getGeneratedValues( entityDescriptor, timing );
if ( generatedValuesToSelect.isEmpty() ) {
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) {
if ( selectStatement == null ) {
return;

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,12 +7,16 @@
package org.hibernate.tuple;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
/**
* A {@link Generator} based on a custom Java generator annotation type.
* Every instance must implement either {@link InMemoryGenerator} or
* {@link InDatabaseGenerator}.
* {@link InDatabaseGenerator}. Implementing this interface is just a
* slightly more typesafe alternative to providing a constructor with
* the same signature as the method
* {@link #initialize(Annotation, Member, GeneratorCreationContext)}.
*
* @param <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.
*
* @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the
* annotation's attribute values and store them in fields.
* @param propertyType the type of the property annotated with the generator annotation. Implementations may use
* the type to determine the right {@link ValueGenerator} to be applied.
* @param entityName the name of the entity to which the annotated property belongs
* @param propertyName the name of the annotated property
* @param annotation an instance of the strategy's annotation type. Typically, implementations will retrieve the
* annotation's attribute values and store them in fields.
* @param member the Java member annotated with the generator annotation.
* @param context a {@link GeneratorCreationContext}
* @throws org.hibernate.HibernateException in case an error occurred during initialization, e.g. if
* an implementation can't create a value for the given property type.
*/
void initialize(A annotation, Class<?> propertyType, String entityName, String propertyName);
void initialize(A annotation, Member member, GeneratorCreationContext context);
}

View File

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

View File

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

View File

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

View File

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

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;
import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.type.Type;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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