diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java index 305c3aa670..340fb81da2 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractSaveEventListener.java @@ -103,6 +103,7 @@ public abstract class AbstractSaveEventListener implements CallbackRegistryCo final EntityPersister persister = source.getEntityPersister( entityName, entity ); final Generator generator = persister.getGenerator(); final boolean generatedOnExecution = generator.generatedOnExecution( entity, source ); + final boolean generatedBeforeExecution = generator.generatedBeforeExecution( entity, source ); final Object generatedId; if ( generatedOnExecution ) { // the id gets generated by the database @@ -114,7 +115,7 @@ public abstract class AbstractSaveEventListener implements CallbackRegistryCo // the @PrePersist callback to happen first generatedId = null; } - else { + else if ( generatedBeforeExecution ) { // go ahead and generate id, and then set it to // the entity instance, so it will be available // to the entity in the @PrePersist callback @@ -124,6 +125,11 @@ public abstract class AbstractSaveEventListener implements CallbackRegistryCo } persister.setIdentifier( entity, generatedId, source ); } + else { + // the generator is refusing to generate anything + // so use the identifier currently assigned + generatedId = persister.getIdentifier( entity, source ); + } final boolean delayIdentityInserts = !source.isTransactionInProgress() && !requiresImmediateIdAccess diff --git a/hibernate-core/src/main/java/org/hibernate/generator/Assigned.java b/hibernate-core/src/main/java/org/hibernate/generator/Assigned.java index dc81e0fe6a..3920b2ce76 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/Assigned.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/Assigned.java @@ -5,6 +5,7 @@ package org.hibernate.generator; import org.hibernate.Internal; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import java.util.EnumSet; @@ -29,6 +30,16 @@ public class Assigned implements Generator { return false; } + @Override + public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { + return false; + } + + @Override + public boolean generatedBeforeExecution(Object entity, SharedSessionContractImplementor session) { + return false; + } + @Override public boolean allowAssignedIdentifiers() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/generator/Generator.java b/hibernate-core/src/main/java/org/hibernate/generator/Generator.java index 568140f05e..4dbb61df3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/Generator.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/Generator.java @@ -32,9 +32,10 @@ import static org.hibernate.generator.EventType.UPDATE; * SQL {@code select}, though in certain cases this additional round trip may be avoided. * An important example is id generation using an identity column. * - * A Generator may implement both interfaces and determine the timing of ID generation at runtime. - * Furthermore, this condition can be based on the state of the owner entity, see - * {@link #generatedOnExecution(Object, SharedSessionContractImplementor) generatedOnExecution}. + * A {@code Generator} may implement both interfaces and determine the timing of identifier + * generation at runtime. Furthermore, this condition can be based on the state of the owner entity, + * see {@link #generatedOnExecution(Object, SharedSessionContractImplementor) generatedOnExecution} and + * {@link #generatedBeforeExecution(Object, SharedSessionContractImplementor) generatedBeforeExecution}. *

* Generically, a generator may be integrated with the program using the meta-annotation * {@link org.hibernate.annotations.ValueGenerationType}, which associates the generator with @@ -95,20 +96,16 @@ public interface Generator extends Serializable { boolean generatedOnExecution(); /** - * Determines if the property value is generated when a row is written to the database, - * or in Java code that executes before the row is written. + * Determines if the property value is generated when a row is written to the database. *

- * Defaults to {@link #generatedOnExecution()}, but can be overloaded allowing conditional - * value generation timing (on/before execution) based on the current state of the owner entity. - * Note that a generator must implement both {@link BeforeExecutionGenerator} and - * {@link OnExecutionGenerator} to achieve this behavior. + * Defaults to {@link #generatedOnExecution()}, but may be overridden to allow conditional + * on-execution value generation based on the current state of the owner entity. * * @param entity The instance of the entity owning the attribute for which we are generating a value. * @param session The session from which the request originates. * * @return {@code true} if the value is generated by the database as a side effect of - * the execution of an {@code insert} or {@code update} statement, or false if - * it is generated in Java code before the statement is executed via JDBC. + * the execution of an {@code insert} or {@code update} statement. * * @see #generatedOnExecution() * @see BeforeExecutionGenerator @@ -120,6 +117,30 @@ public interface Generator extends Serializable { return generatedOnExecution(); } + /** + * Determines if the property value is generated before in Java code that executes before + * the row is written. + *

+ * Defaults to {@link #generatedOnExecution() !generatedOnExecution()}, but may be overridden + * to allow conditional before-execution value generation based on the current state of the + * owner entity. + * + * @param entity The instance of the entity owning the attribute for which we are generating a value. + * @param session The session from which the request originates. + * + * @return {@code true} if the value is generated in Java code before the statement is + * executed via JDBC. + * + * @see #generatedOnExecution() + * @see BeforeExecutionGenerator + * @see OnExecutionGenerator + * + * @since 7.0 + */ + default boolean generatedBeforeExecution(Object entity, SharedSessionContractImplementor session) { + return !generatedOnExecution(); + } + /** * The {@linkplain EventType event types} for which this generator should be called * to produce a new value. diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java index 7719e0ed6a..cf96f527f3 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/GeneratedGeneration.java @@ -8,7 +8,6 @@ import org.hibernate.AnnotationException; import org.hibernate.annotations.Generated; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.EventType; import org.hibernate.generator.OnExecutionGenerator; import org.hibernate.persister.entity.EntityPersister; @@ -27,7 +26,7 @@ import static org.hibernate.internal.util.StringHelper.isEmpty; * @author Steve Ebersole * @author Gunnar Morling */ -public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutionGenerator { +public class GeneratedGeneration implements OnExecutionGenerator { private final EnumSet eventTypes; private final boolean writable; @@ -72,11 +71,6 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio return writable; } - @Override - public boolean generatedOnExecution() { - return true; - } - @Override public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { if ( writable ) { @@ -90,13 +84,6 @@ public class GeneratedGeneration implements OnExecutionGenerator, BeforeExecutio } } - @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { - final EntityPersister entityPersister = session.getEntityPersister( null, owner ); - assert entityPersister.getGenerator() == this; - return entityPersister.getIdentifier( owner, session ); - } - @Override public boolean allowAssignedIdentifiers() { return writable; diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 377dbe06ac..e6414b7f0f 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -146,32 +146,47 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen } } final Generator generator = persister.getGenerator(); - if ( !generator.generatedOnExecution( entity, this ) ) { - if ( generator.generatesOnInsert() ) { - id = ( (BeforeExecutionGenerator) generator).generate( this, entity, null, INSERT ); + if ( generator.generatedBeforeExecution( entity, this ) ) { + if ( !generator.generatesOnInsert() ) { + throw new IdentifierGenerationException( "Identifier generator must generate on insert" ); + } + id = ( (BeforeExecutionGenerator) generator).generate( this, entity, null, INSERT ); + if ( firePreInsert(entity, id, state, persister) ) { + return id; } else { - id = persister.getIdentifier( entity, this ); - if ( id == null ) { - throw new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ); - } + getInterceptor().onInsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + persister.getInsertCoordinator().insert( entity, id, state, this ); + persister.setIdentifier( entity, id, this ); + } + } + else if ( generator.generatedOnExecution( entity, this ) ) { + if ( !generator.generatesOnInsert() ) { + throw new IdentifierGenerationException( "Identifier generator must generate on insert" ); + } + if ( firePreInsert(entity, null, state, persister) ) { + return null; + } + else { + getInterceptor().onInsert( entity, null, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + final GeneratedValues generatedValues = persister.getInsertCoordinator().insert( entity, state, this ); + id = castNonNull( generatedValues ).getGeneratedValue( persister.getIdentifierMapping() ); + persister.setIdentifier( entity, id, this ); + } + } + else { // assigned identifier + id = persister.getIdentifier( entity, this ); + if ( id == null ) { + throw new IdentifierGenerationException( "Identifier of entity '" + persister.getEntityName() + "' must be manually assigned before calling 'insert()'" ); } if ( firePreInsert(entity, id, state, persister) ) { return id; } - getInterceptor().onInsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); - persister.getInsertCoordinator().insert( entity, id, state, this ); - } - else { - if ( firePreInsert(entity, null, state, persister) ) { - return null; + else { + getInterceptor().onInsert( entity, id, state, persister.getPropertyNames(), persister.getPropertyTypes() ); + persister.getInsertCoordinator().insert( entity, id, state, this ); } - getInterceptor() - .onInsert( entity, null, state, persister.getPropertyNames(), persister.getPropertyTypes() ); - final GeneratedValues generatedValues = persister.getInsertCoordinator().insert( entity, state, this ); - id = castNonNull( generatedValues ).getGeneratedValue( persister.getIdentifierMapping() ); } - persister.setIdentifier( entity, id, this ); forEachOwnedCollection( entity, id, persister, (descriptor, collection) -> { descriptor.recreate( collection, id, this); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java index 4086c65f16..89c873f729 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java @@ -776,7 +776,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable @Override public Object execute(SharedSessionContractImplementor session, Object incomingObject) { - if ( !generator.generatedOnExecution( incomingObject, session ) ) { + if ( generator.generatedBeforeExecution( incomingObject, session ) ) { return generator.generate( session, incomingObject, null, INSERT ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinatorStandard.java index 444e95a584..ce084ce6ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinatorStandard.java @@ -140,7 +140,7 @@ public class InsertCoordinatorStandard extends AbstractMutationCoordinator imple final Generator generator = generators[i]; if ( generator != null && generator.generatesOnInsert() - && !generator.generatedOnExecution( entity, session ) ) { + && generator.generatedBeforeExecution( entity, session ) ) { values[i] = ( (BeforeExecutionGenerator) generator ).generate( session, entity, values[i], INSERT ); persister.setPropertyValue( entity, i, values[i] ); foundStateDependentGenerator = foundStateDependentGenerator || generator.generatedOnExecution(); @@ -407,7 +407,7 @@ public class InsertCoordinatorStandard extends AbstractMutationCoordinator imple else { final Generator generator = attributeMapping.getGenerator(); if ( isValueGenerated( generator ) ) { - if ( session != null && !generator.generatedOnExecution( object, session ) ) { + if ( session != null && generator.generatedBeforeExecution( object, session ) ) { attributeInclusions[attributeIndex] = true; attributeMapping.forEachInsertable( insertGroupBuilder ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java index c538e9436c..c7796072c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/UpdateCoordinatorStandard.java @@ -540,7 +540,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple final Generator generator = generators[i]; if ( generator != null && generator.generatesOnUpdate() - && !generator.generatedOnExecution( object, session ) ) { + && generator.generatedBeforeExecution( object, session ) ) { newValues[i] = ( (BeforeExecutionGenerator) generator ).generate( session, object, newValues[i], UPDATE ); entityPersister().setPropertyValue( object, i, newValues[i] ); fieldsPreUpdateNeeded[count++] = i; @@ -667,7 +667,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple final Generator generator = attributeMapping.getGenerator(); final boolean generated = isValueGenerated( generator ); - final boolean needsDynamicUpdate = generated && session != null && !generator.generatedOnExecution( entity, session ); + final boolean needsDynamicUpdate = + generated && session != null && generator.generatedBeforeExecution( entity, session ); final boolean generatedInSql = generated && isValueGenerationInSql( generator, dialect ); if ( generatedInSql && !needsDynamicUpdate && !( (OnExecutionGenerator) generator ).writePropertyValue() ) { analysis.registerValueGeneratedInSqlNoWrite(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/MixedTimingGeneratorsTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/MixedTimingGeneratorsTest.java index 341a28805c..0a143cda34 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/MixedTimingGeneratorsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/MixedTimingGeneratorsTest.java @@ -306,8 +306,13 @@ public class MixedTimingGeneratorsTest { } @Override - public boolean generatedOnExecution(Object owner, SharedSessionContractImplementor session) { - return !( (RandomEntity) owner ).getName().contains( "random" ); + public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { + return !generatedBeforeExecution( entity, session ); + } + + @Override + public boolean generatedBeforeExecution(Object entity, SharedSessionContractImplementor session) { + return ( (RandomEntity) entity ).getName().contains( "random" ); } } @@ -333,8 +338,13 @@ public class MixedTimingGeneratorsTest { } @Override - public boolean generatedOnExecution(Object owner, SharedSessionContractImplementor session) { - return !( (StringGeneratedEntity) owner ).getName().contains( "generated" ); + public boolean generatedOnExecution(Object entity, SharedSessionContractImplementor session) { + return !generatedBeforeExecution( entity, session ); + } + + @Override + public boolean generatedBeforeExecution(Object entity, SharedSessionContractImplementor session) { + return ( (StringGeneratedEntity) entity ).getName().contains( "generated" ); } @Override