diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/AbstractEntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/AbstractEntityInsertAction.java index e94f1a83c8..219277727e 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/AbstractEntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/AbstractEntityInsertAction.java @@ -11,13 +11,11 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.engine.internal.Nullability; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.event.spi.EventSource; import org.hibernate.metamodel.mapping.NaturalIdMapping; @@ -26,6 +24,8 @@ import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.EntityPersister; +import static org.hibernate.engine.internal.Versioning.getVersion; + /** * A base class for entity insert actions. * @@ -127,7 +127,7 @@ public abstract class AbstractEntityInsertAction extends EntityAction { */ public final void makeEntityManaged() { nullifyTransientReferencesIfNotAlready(); - final Object version = Versioning.getVersion( getState(), getPersister() ); + final Object version = getVersion( getState(), getPersister() ); final PersistenceContext persistenceContextInternal = getSession().getPersistenceContextInternal(); persistenceContextInternal.addEntity( getInstance(), diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index dfb737e582..b53db7adae 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -12,7 +12,6 @@ import org.hibernate.cache.CacheException; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.entry.CacheEntry; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionEventListenerManager; @@ -33,6 +32,8 @@ import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.TypeHelper; +import static org.hibernate.engine.internal.Versioning.getVersion; + /** * The action for performing entity updates. */ @@ -239,7 +240,7 @@ public class EntityUpdateAction extends EntityAction { // have the entity entry doAfterTransactionCompletion post-update processing, passing it the // update state and the new version (if one). if ( persister.isVersionPropertyGenerated() ) { - nextVersion = Versioning.getVersion( state, persister ); + nextVersion = getVersion( state, persister ); } entry.postUpdate( instance, state, nextVersion ); } diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/ValueGenerationType.java b/hibernate-core/src/main/java/org/hibernate/annotations/ValueGenerationType.java index 79201744ca..80a5f4286a 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/ValueGenerationType.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/ValueGenerationType.java @@ -47,14 +47,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * property to be generated when any SQL statement to {@code insert} or * {@code update} the entity is executed. *

- * Every generator annotation type has an {@link Generator} - * implementation which is responsible for generating values. It must be either: + * Every generator annotation type has an {@link Generator} implementation which + * is responsible for generating values. It must be either: *

* A generator annotation may have members, which are used to configure the * generation strategy, when the strategy instance in initialized via diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentIdentifierBag.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentIdentifierBag.java index e0250a3119..2e9ec3c643 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentIdentifierBag.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/PersistentIdentifierBag.java @@ -23,6 +23,8 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.type.Type; +import static org.hibernate.generator.EventType.INSERT; + /** * An {@code IdentifierBag} implements "bag" semantics more efficiently than * a regular {@code Bag} by adding a synthetic identifier column to the @@ -368,7 +370,7 @@ public class PersistentIdentifierBag extends AbstractPersistentCollection final Integer loc = i++; if ( !identifiers.containsKey( loc ) ) { //TODO: native ids - final Object id = persister.getGenerator().generate( getSession(), entry, null ); + final Object id = persister.getGenerator().generate( getSession(), entry, null, INSERT ); identifiers.put( loc, id ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/Versioning.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/Versioning.java index 9594317b12..dc7a6ace32 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/Versioning.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/Versioning.java @@ -6,6 +6,7 @@ */ package org.hibernate.engine.internal; +import org.hibernate.Remove; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.metamodel.mapping.EntityVersionMapping; @@ -14,6 +15,9 @@ import org.hibernate.type.descriptor.java.VersionJavaType; import org.jboss.logging.Logger; +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.generator.EventType.UPDATE; + /** * Utilities for dealing with optimistic locking values. * @@ -39,7 +43,7 @@ public final class Versioning { * @param session The originating session * @return The initial optimistic locking value */ - private static Object seed(EntityVersionMapping versionMapping, SharedSessionContractImplementor session) { + public static Object seed(EntityVersionMapping versionMapping, SharedSessionContractImplementor session) { final Object seed = versionMapping.getJavaType().seed( versionMapping.getLength(), versionMapping.getPrecision(), @@ -50,10 +54,40 @@ public final class Versioning { return seed; } + + /** + * Create an initial optimistic locking value using the for the version property + * if required using the {@link org.hibernate.generator.Generator} contract + * and inject it into the snapshot state. + * + * @param fields The current snapshot state + * @param persister The persister of the versioned entity + * @param entity The entity instance + * @param session The originating session + * @return True if we injected a new version value into the fields array; false + * otherwise. + */ + public static boolean seedVersion( + Object entity, + Object[] fields, + EntityPersister persister, + SharedSessionContractImplementor session) { + final int versionProperty = persister.getVersionProperty(); + final Object initialVersion = fields[versionProperty]; + if ( isNullInitialVersion( initialVersion ) ) { + fields[versionProperty] = persister.getVersionGenerator().generate( session, entity, initialVersion, INSERT ); + return true; + } + else { + LOG.tracev( "Using initial version: {0}", initialVersion ); + return false; + } + } + /** * Create an initial optimistic locking value according the {@link VersionJavaType} - * contract for the version property if required and inject it into - * the snapshot state. + * contract for the version property if required and inject it into the + * snapshot state. * * @param fields The current snapshot state * @param versionProperty The index of the version property @@ -61,28 +95,60 @@ public final class Versioning { * @param session The originating session * @return True if we injected a new version value into the fields array; false * otherwise. + * + * @deprecated Use {@link #seedVersion(Object, Object[], EntityPersister, SharedSessionContractImplementor)} */ + @Deprecated(since = "6.2") @Remove public static boolean seedVersion( Object[] fields, int versionProperty, EntityVersionMapping versionMapping, SharedSessionContractImplementor session) { final Object initialVersion = fields[versionProperty]; - if ( - initialVersion==null || - // This next bit is to allow for both unsaved-value="negative" - // and for "older" behavior where version number did not get - // seeded if it was already set in the object - // TODO: shift it into unsaved-value strategy - ( (initialVersion instanceof Number) && ( (Number) initialVersion ).longValue()<0 ) - ) { + if ( isNullInitialVersion( initialVersion ) ) { fields[versionProperty] = seed( versionMapping, session ); return true; } - LOG.tracev( "Using initial version: {0}", initialVersion ); - return false; + else { + LOG.tracev( "Using initial version: {0}", initialVersion ); + return false; + } } + /** + * Determines if the value of the assigned version property should be considered + * a "null" value, that is, if it is literally {@code null}, or if it is a negative + * integer. + * + * @param initialVersion The value initially assigned to a version property + * @return {@code} if the value shoudl be considered null for this purpose + */ + public static boolean isNullInitialVersion(Object initialVersion) { + return initialVersion == null + || // This next bit is to allow for both unsaved-value="negative" + // and for "older" behavior where version number did not get + // seeded if it was already set in the object + // TODO: shift it into unsaved-value strategy + initialVersion instanceof Number && ((Number) initialVersion).longValue() < 0; + } + + /** + * Generate the next increment in the optimistic locking value according the + * {@link org.hibernate.generator.Generator} contract for the version property. + * + * @param entity The entity instance + * @param currentVersion The current version + * @param persister The persister of the versioned entity + * @param session The originating session + * @return The incremented optimistic locking value. + */ + public static Object incrementVersion( + Object entity, + Object currentVersion, + EntityPersister persister, + SharedSessionContractImplementor session) { + return persister.getVersionGenerator().generate( session, entity, currentVersion, UPDATE ); + } /** * Generate the next increment in the optimistic locking value according @@ -121,10 +187,9 @@ public final class Versioning { * @param persister The entity persister */ public static void setVersion(Object[] fields, Object version, EntityPersister persister) { - if ( !persister.isVersioned() ) { - return; + if ( persister.isVersioned() ) { + fields[ persister.getVersionProperty() ] = version; } - fields[ persister.getVersionProperty() ] = version; } /** @@ -135,10 +200,7 @@ public final class Versioning { * @return The extracted optimistic locking value */ public static Object getVersion(Object[] fields, EntityPersister persister) { - if ( !persister.isVersioned() ) { - return null; - } - return fields[ persister.getVersionProperty() ]; + return persister.isVersioned() ? fields[persister.getVersionProperty()] : null; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractReassociateEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractReassociateEventListener.java index 5b9e0232dc..3271aec3f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractReassociateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractReassociateEventListener.java @@ -7,7 +7,6 @@ package org.hibernate.event.internal; import org.hibernate.LockMode; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -21,6 +20,8 @@ import org.hibernate.type.TypeHelper; import org.jboss.logging.Logger; +import static org.hibernate.engine.internal.Versioning.getVersion; + /** * A convenience base class for listeners that respond to requests to reassociate an entity * to a session ( such as through lock() or update() ). @@ -65,7 +66,7 @@ public abstract class AbstractReassociateEventListener { values, source ); - Object version = Versioning.getVersion( values, persister ); + Object version = getVersion( values, persister ); EntityEntry newEntry = persistenceContext.addEntity( object, 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 0b849fdcf6..6411f688b8 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 @@ -16,7 +16,6 @@ import org.hibernate.action.internal.EntityInsertAction; import org.hibernate.classic.Lifecycle; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntryExtraState; @@ -27,7 +26,6 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.event.spi.EventSource; import org.hibernate.id.IdentifierGenerationException; -import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.jpa.event.spi.CallbackRegistry; @@ -40,6 +38,10 @@ import org.hibernate.type.Type; import org.hibernate.type.TypeHelper; import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; +import static org.hibernate.engine.internal.Versioning.getVersion; +import static org.hibernate.engine.internal.Versioning.seedVersion; +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.id.IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR; /** * A convenience base class for listeners responding to save events. @@ -115,11 +117,11 @@ public abstract class AbstractSaveEventListener final EntityPersister persister = source.getEntityPersister( entityName, entity ); Generator generator = persister.getGenerator(); if ( !generator.generatedByDatabase() ) { - Object generatedId = ( (InMemoryGenerator) generator ).generate( source, entity, null ); + final Object generatedId = ( (InMemoryGenerator) generator ).generate( source, entity, null, INSERT ); if ( generatedId == null ) { throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); } - else if ( generatedId == IdentifierGeneratorHelper.SHORT_CIRCUIT_INDICATOR ) { + else if ( generatedId == SHORT_CIRCUIT_INDICATOR ) { return source.getIdentifier( entity ); } else { @@ -188,7 +190,7 @@ public abstract class AbstractSaveEventListener } private static EntityKey entityKey(Object entity, Object id, EntityPersister persister, boolean useIdentityColumn, EventSource source) { - if ( !useIdentityColumn) { + if ( !useIdentityColumn ) { final EntityKey key = source.generateEntityKey( id, persister ); final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); final Object old = persistenceContext.getEntity( key ); @@ -316,16 +318,16 @@ public abstract class AbstractSaveEventListener } private Object[] cloneAndSubstituteValues(Object entity, EntityPersister persister, C context, EventSource source, Object id) { - Object[] values = persister.getPropertyValuesToInsert(entity, getMergeMap(context), source); + Object[] values = persister.getPropertyValuesToInsert( entity, getMergeMap(context), source ); Type[] types = persister.getPropertyTypes(); - boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source); + boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source ); if ( persister.hasCollections() ) { - substitute = visitCollectionsBeforeSave(entity, id, values, types, source) || substitute; + substitute = visitCollectionsBeforeSave( entity, id, values, types, source ) || substitute; } if ( substitute ) { - persister.setValues(entity, values ); + persister.setValues( entity, values ); } TypeHelper.deepCopy( @@ -363,7 +365,7 @@ public abstract class AbstractSaveEventListener id, values, entity, - Versioning.getVersion( values, persister ), + getVersion( values, persister ), persister, isVersionIncrementDisabled(), source @@ -429,12 +431,7 @@ public abstract class AbstractSaveEventListener //keep the existing version number in the case of replicate! if ( persister.isVersioned() ) { - substitute = Versioning.seedVersion( - values, - persister.getVersionProperty(), - persister.getVersionMapping(), - source - ) || substitute; + substitute = seedVersion( entity, values, persister, source ) || substitute; } return substitute; } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java index b53a73663f..e644ea3e58 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultFlushEntityEventListener.java @@ -44,6 +44,9 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTra import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker; import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; +import static org.hibernate.engine.internal.Versioning.getVersion; +import static org.hibernate.engine.internal.Versioning.incrementVersion; +import static org.hibernate.engine.internal.Versioning.setVersion; /** * An event that occurs for each entity instance at flush time @@ -381,13 +384,13 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener if ( persister.isVersioned() ) { Object[] values = event.getPropertyValues(); if ( entry.isBeingReplicated() ) { - return Versioning.getVersion( values, persister ); + return getVersion( values, persister ); } else { final Object nextVersion = isVersionIncrementRequired( event, entry ) - ? Versioning.increment( entry.getVersion(), persister.getVersionMapping(), event.getSession() ) + ? incrementVersion( event.getEntity(), entry.getVersion(), persister, event.getSession() ) : entry.getVersion(); //use the current version - Versioning.setVersion( values, nextVersion, persister ); + setVersion( values, nextVersion, persister ); return nextVersion; } } @@ -397,7 +400,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener } - private boolean isVersionIncrementRequired(FlushEntityEvent event, EntityEntry entry) { + private static boolean isVersionIncrementRequired(FlushEntityEvent event, EntityEntry entry) { if ( entry.getStatus() == Status.DELETED ) { return false; } diff --git a/hibernate-core/src/main/java/org/hibernate/generator/EventType.java b/hibernate-core/src/main/java/org/hibernate/generator/EventType.java index 6de6660a22..0c9f1e0d10 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/EventType.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/EventType.java @@ -24,6 +24,18 @@ package org.hibernate.generator; * @see EventTypeSets */ public enum EventType { + /** + * An event that occurs when any {@code insert} statements needed + * to persist a new entity instance are executed. This indicates, + * for example, that a surrogate primary key should be generated, + * or initial that an initial version number should be seeded. + */ INSERT, + /** + * An event that occurs when any {@code update} statements needed + * to persist changes to a dirty entity instance are executed. + * This indicates, for example, that a version number should be + * incremented. + */ UPDATE; } 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 74ad48f0f7..7d6f64f775 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/Generator.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/Generator.java @@ -22,7 +22,7 @@ import static org.hibernate.generator.EventType.UPDATE; * generated in Java code, or by the database. *
    *
  • Value generation via arbitrary code written in Java is the responsibility of the method - * {@link InMemoryGenerator#generate(SharedSessionContractImplementor, Object, Object)}. + * {@link InMemoryGenerator#generate(SharedSessionContractImplementor, Object, Object, EventType)}. * In this case, the generated value is written to the database just like any other field * or property value. *
  • A value generated by the database might be generated implicitly, by a trigger, or using @@ -45,11 +45,20 @@ import static org.hibernate.generator.EventType.UPDATE; * A generator must implement {@link #getEventTypes()} to specify the events for which it should be * called to produce a new value. {@link EventTypeSets} provides a convenient list of possibilities. *

    + * There are two especially important applications of this machinery: + *

      + *
    • * An {@linkplain jakarta.persistence.Id identifier} generator is a generator capable of producing * surrogate primary key values. An identifier generator must respond to insert events only. That * is, {@link #getEventTypes()} must return {@link EventTypeSets#INSERT_ONLY}. It may be integrated * using the {@link org.hibernate.annotations.IdGeneratorType} meta-annotation or the older-style * {@link org.hibernate.annotations.GenericGenerator} annotation. + *
    • + * A {@linkplain jakarta.persistence.Version version} generator is a generator capable of seeding + * and incrementing version numbers. A version generator must respond to both insert and update + * events. That is, {@link #getEventTypes()} must return {@link EventTypeSets#INSERT_AND_UPDATE}. + * It may be integrated using {@link org.hibernate.annotations.ValueGenerationType} meta-annotation. + *
    * * @see org.hibernate.annotations.ValueGenerationType * @see org.hibernate.annotations.IdGeneratorType diff --git a/hibernate-core/src/main/java/org/hibernate/generator/InMemoryGenerator.java b/hibernate-core/src/main/java/org/hibernate/generator/InMemoryGenerator.java index 89cc50dd10..797bff0778 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/InMemoryGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/InMemoryGenerator.java @@ -37,13 +37,13 @@ public interface InMemoryGenerator extends Generator { /** * Generate a value. * - * @param session The session from which the request originates. - * @param owner The instance of the object owning the attribute for which we are generating a value. + * @param session The session from which the request originates. + * @param owner The instance of the object owning the attribute for which we are generating a value. * @param currentValue The current value assigned to the property, or {@code null} - * + * @param eventType The type of event that has triggered generation of a new value * @return The generated value */ - Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue); + Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType); default boolean generatedByDatabase() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/CurrentTimestampGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/CurrentTimestampGeneration.java index 330ce76d68..7a02e9bd30 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/internal/CurrentTimestampGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/CurrentTimestampGeneration.java @@ -91,7 +91,7 @@ public class CurrentTimestampGeneration implements InMemoryGenerator, InDatabase } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return generator.generateValue( (Session) session, owner, currentValue ); } diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/SourceGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/SourceGeneration.java index 00c245db82..ac89617f97 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/internal/SourceGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/SourceGeneration.java @@ -89,7 +89,7 @@ public class SourceGeneration implements InMemoryGenerator { } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return valueGenerator.generateValue( (Session) session, owner, currentValue ); } diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java index 5ac5fef2e1..0f8fb50c35 100644 --- a/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/TenantIdGeneration.java @@ -51,7 +51,7 @@ public class TenantIdGeneration implements InMemoryGenerator { } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { SessionFactoryImplementor sessionFactory = session.getSessionFactory(); JavaType descriptor = sessionFactory.getTypeConfiguration().getJavaTypeRegistry() .findDescriptor(propertyType); diff --git a/hibernate-core/src/main/java/org/hibernate/generator/internal/VersionGeneration.java b/hibernate-core/src/main/java/org/hibernate/generator/internal/VersionGeneration.java new file mode 100644 index 0000000000..9d582ce553 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/generator/internal/VersionGeneration.java @@ -0,0 +1,52 @@ +/* + * 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 . + */ +package org.hibernate.generator.internal; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.EventType; +import org.hibernate.generator.InMemoryGenerator; +import org.hibernate.metamodel.mapping.EntityVersionMapping; + +import java.util.EnumSet; + +import static org.hibernate.engine.internal.Versioning.increment; +import static org.hibernate.engine.internal.Versioning.seed; +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.generator.EventTypeSets.INSERT_AND_UPDATE; + +/** + * A default {@link org.hibernate.generator.Generator} for {@link jakarta.persistence.Version @Version} + * properties. This implementation simply delegates back to: + *
      + *
    • {@link org.hibernate.type.descriptor.java.VersionJavaType#seed} to seed an initial version, and + *
    • {@link org.hibernate.type.descriptor.java.VersionJavaType#next} to increment a version. + *
    + * Thus, this implementation reproduces the "classic" behavior of Hibernate. A custom generator specified + * using a {@linkplain org.hibernate.annotations.ValueGenerationType generator annotation} will override + * this implementation, allowing customized versioning. + * + * @author Gavin King + */ +public class VersionGeneration implements InMemoryGenerator { + private final EntityVersionMapping versionMapping; + + public VersionGeneration(EntityVersionMapping versionMapping) { + this.versionMapping = versionMapping; + } + + @Override + public EnumSet getEventTypes() { + return INSERT_AND_UPDATE; + } + + @Override + public Object generate(SharedSessionContractImplementor session, Object owner, Object current, EventType eventType) { + return eventType == INSERT + ? seed( versionMapping, session ) + : increment( current, versionMapping, session ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java index 829f7bf97b..08adc1521d 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java @@ -142,7 +142,7 @@ public interface IdentifierGenerator extends InMemoryGenerator, ExportableProduc * The {@code currentValue} is usually null for id generation. */ @Override - default Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + default Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return generate( session, owner ); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java index 8c462ab914..bd16f3a561 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java @@ -72,7 +72,7 @@ public class UuidGenerator implements InMemoryGenerator { } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return valueTransformer.transform( generator.generateUuid( session ) ); } } 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 775e01e72e..ff6bf0f620 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -20,7 +20,6 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.StatefulPersistenceContext; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.PersistenceContext; @@ -41,6 +40,10 @@ import jakarta.transaction.SystemException; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; +import static org.hibernate.engine.internal.Versioning.incrementVersion; +import static org.hibernate.engine.internal.Versioning.seedVersion; +import static org.hibernate.engine.internal.Versioning.setVersion; +import static org.hibernate.generator.EventType.INSERT; /** * Concrete implementation of the {@link StatelessSession} API. @@ -97,17 +100,11 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen final EntityPersister persister = getEntityPersister( entityName, entity ); final Object id; final Object[] state = persister.getValues( entity ); - Generator generator = persister.getGenerator(); + final Generator generator = persister.getGenerator(); if ( !generator.generatedByDatabase() ) { - id = ( (InMemoryGenerator) generator).generate( this, entity, null ); + id = ( (InMemoryGenerator) generator).generate( this, entity, null, INSERT ); if ( persister.isVersioned() ) { - boolean substitute = Versioning.seedVersion( - state, - persister.getVersionProperty(), - persister.getVersionMapping(), - this - ); - if ( substitute ) { + if ( seedVersion( entity, state, persister, this ) ) { persister.setValues( entity, state ); } } @@ -156,8 +153,8 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen Object oldVersion; if ( persister.isVersioned() ) { oldVersion = persister.getVersion( entity ); - Object newVersion = Versioning.increment( oldVersion, persister.getVersionMapping(), this ); - Versioning.setVersion( state, newVersion, persister ); + Object newVersion = incrementVersion( entity, oldVersion, persister, this ); + setVersion( state, newVersion, persister ); persister.setValues( entity, state ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/entity/CacheEntityLoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/entity/CacheEntityLoaderHelper.java index 985f3843f5..c84da15a13 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/entity/CacheEntityLoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/entity/CacheEntityLoaderHelper.java @@ -18,7 +18,6 @@ import org.hibernate.cache.spi.entry.StandardCacheEntryImpl; import org.hibernate.engine.internal.CacheHelper; import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.internal.TwoPhaseLoad; -import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -45,6 +44,7 @@ import org.hibernate.type.TypeHelper; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; +import static org.hibernate.engine.internal.Versioning.getVersion; import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock; /** @@ -424,7 +424,7 @@ public class CacheEntityLoaderHelper { source ); } - version = Versioning.getVersion( values, subclassPersister ); + version = getVersion( values, subclassPersister ); LOG.tracef( "Cached Version : %s", version ); final Object proxy = persistenceContext.getProxy( entityKey ); 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 07d9e5367d..aa72e40fe1 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java @@ -28,7 +28,6 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.IdentifierGenerator; -import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.collections.ArrayHelper; @@ -41,6 +40,9 @@ import org.hibernate.type.ComponentType; import org.hibernate.type.EmbeddedComponentType; import org.hibernate.type.Type; +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.id.IdentifierGeneratorHelper.POST_INSERT_INDICATOR; + /** * A mapping model object that represents an {@linkplain jakarta.persistence.Embeddable embeddable class}. *

    @@ -508,38 +510,36 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable } public static class ValueGenerationPlan implements CompositeNestedGeneratedValueGenerator.GenerationPlan { - private final Generator subGenerator; + private final Generator subgenerator; private final Setter injector; - public ValueGenerationPlan( - Generator subGenerator, - Setter injector) { - this.subGenerator = subGenerator; + public ValueGenerationPlan(Generator subgenerator, Setter injector) { + this.subgenerator = subgenerator; this.injector = injector; } @Override public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) { - if ( !subGenerator.generatedByDatabase() ) { - Object generatedId = ( (InMemoryGenerator) subGenerator ).generate( session, incomingObject, null ); + if ( !subgenerator.generatedByDatabase() ) { + Object generatedId = ( (InMemoryGenerator) subgenerator).generate( session, incomingObject, null, INSERT ); injector.set( injectionContext, generatedId ); } else { - injector.set( injectionContext, IdentifierGeneratorHelper.POST_INSERT_INDICATOR ); + injector.set( injectionContext, POST_INSERT_INDICATOR ); } } @Override public void registerExportables(Database database) { - if ( subGenerator instanceof ExportableProducer ) { - ( (ExportableProducer) subGenerator ).registerExportables( database ); + if ( subgenerator instanceof ExportableProducer ) { + ( (ExportableProducer) subgenerator).registerExportables( database ); } } @Override public void initialize(SqlStringGenerationContext context) { - if ( subGenerator instanceof IdentifierGenerator ) { - ( (IdentifierGenerator) subGenerator ).initialize( context ); + if ( subgenerator instanceof IdentifierGenerator ) { + ( (IdentifierGenerator) subgenerator).initialize( context ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InDatabaseGeneratedValueResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InDatabaseGeneratedValueResolver.java index c338414389..c6fc60298c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InDatabaseGeneratedValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InDatabaseGeneratedValueResolver.java @@ -8,7 +8,7 @@ package org.hibernate.metamodel.mapping; import org.hibernate.Internal; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.tuple.GenerationTiming; +import org.hibernate.generator.EventType; /** * GeneratedValueResolver impl for in-db generation. It extracts the generated value @@ -18,11 +18,11 @@ import org.hibernate.tuple.GenerationTiming; */ @Internal public class InDatabaseGeneratedValueResolver implements GeneratedValueResolver { -// private final GenerationTiming timing; + private final EventType eventType; private final int resultPosition; - public InDatabaseGeneratedValueResolver(GenerationTiming timing, int resultPosition) { -// this.timing = timing; + public InDatabaseGeneratedValueResolver(EventType eventType, int resultPosition) { + this.eventType = eventType; this.resultPosition = resultPosition; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InMemoryGeneratedValueResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InMemoryGeneratedValueResolver.java index 8b031879c3..637bb8d779 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InMemoryGeneratedValueResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/InMemoryGeneratedValueResolver.java @@ -8,7 +8,7 @@ package org.hibernate.metamodel.mapping; import org.hibernate.Internal; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.tuple.GenerationTiming; +import org.hibernate.generator.EventType; import org.hibernate.generator.InMemoryGenerator; /** @@ -18,12 +18,12 @@ import org.hibernate.generator.InMemoryGenerator; */ @Internal public class InMemoryGeneratedValueResolver implements GeneratedValueResolver { -// private final GenerationTiming generationTiming; - private final InMemoryGenerator valueGenerator; + private final EventType eventType; + private final InMemoryGenerator generator; - public InMemoryGeneratedValueResolver(InMemoryGenerator valueGenerator, GenerationTiming generationTiming) { - this.valueGenerator = valueGenerator; -// this.generationTiming = generationTiming; + public InMemoryGeneratedValueResolver(InMemoryGenerator generator, EventType eventType) { + this.generator = generator; + this.eventType = eventType; } // @Override @@ -33,6 +33,6 @@ public class InMemoryGeneratedValueResolver implements GeneratedValueResolver { @Override public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session, Object currentValue) { - return valueGenerator.generate( session, entity, currentValue ); + return generator.generate( session, entity, currentValue, eventType ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java index a77a899939..5ddff0c3ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java @@ -14,6 +14,7 @@ import org.hibernate.LockOptions; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.EventType; import org.hibernate.generator.InDatabaseGenerator; import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.metamodel.UnsupportedMappingException; @@ -31,7 +32,6 @@ import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; -import org.hibernate.tuple.GenerationTiming; import org.hibernate.generator.Generator; import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER; @@ -59,7 +59,7 @@ public class GeneratedValuesProcessor { public GeneratedValuesProcessor( EntityMappingType entityDescriptor, - GenerationTiming timing, + EventType timing, SessionFactoryImplementor sessionFactory) { this.entityDescriptor = entityDescriptor; this.sessionFactory = sessionFactory; @@ -88,7 +88,7 @@ public class GeneratedValuesProcessor { * populate the list of {@link GeneratedValueDescriptor}s by side effect, and * return a list of {@link AttributeMapping}s. */ - private List getGeneratedAttributes(EntityMappingType entityDescriptor, GenerationTiming timing) { + private List getGeneratedAttributes(EntityMappingType entityDescriptor, EventType timing) { // 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 Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators(); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 2fac9f7f8f..64c6017c3d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -93,6 +93,9 @@ import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.LoadEvent; +import org.hibernate.generator.EventType; +import org.hibernate.generator.InMemoryGenerator; +import org.hibernate.generator.internal.VersionGeneration; import org.hibernate.id.Assigned; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.IdentifierGenerator; @@ -259,7 +262,6 @@ import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.generator.Generator; import org.hibernate.generator.InDatabaseGenerator; -import org.hibernate.tuple.GenerationTiming; import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityMetamodel; @@ -278,6 +280,8 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttrib import static org.hibernate.engine.internal.ManagedTypeHelper.processIfPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; import static org.hibernate.engine.internal.Versioning.isVersionIncrementRequired; +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.generator.EventType.UPDATE; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; /** @@ -414,7 +418,6 @@ public abstract class AbstractEntityPersister private final Map subclassPropertyAliases = new HashMap<>(); private final Map subclassPropertyColumnNames = new HashMap<>(); - private final JavaType javaType; private final EntityRepresentationStrategy representationStrategy; @@ -431,6 +434,8 @@ public abstract class AbstractEntityPersister protected Map declaredAttributeMappings = new LinkedHashMap<>(); protected List staticFetchableList; + private InMemoryGenerator versionGenerator; + protected ReflectionOptimizer.AccessOptimizer accessOptimizer; /** @@ -490,10 +495,10 @@ public abstract class AbstractEntityPersister // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - this.representationStrategy = creationContext.getBootstrapContext().getRepresentationStrategySelector() + representationStrategy = creationContext.getBootstrapContext().getRepresentationStrategySelector() .resolveStrategy( bootDescriptor, this, creationContext ); - this.javaType = representationStrategy.getLoadJavaType(); + javaType = representationStrategy.getLoadJavaType(); assert javaType != null; final JdbcServices jdbcServices = factory.getServiceRegistry().getService( JdbcServices.class ); @@ -1933,7 +1938,7 @@ public abstract class AbstractEntityPersister return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString(); } - private GeneratedValuesProcessor createGeneratedValuesProcessor(GenerationTiming timing) { + private GeneratedValuesProcessor createGeneratedValuesProcessor(EventType timing) { return new GeneratedValuesProcessor( this, timing, getFactory() ); } @@ -3992,6 +3997,11 @@ public abstract class AbstractEntityPersister return entityMetamodel.getIdentifierProperty().getGenerator(); } + @Override + public InMemoryGenerator getVersionGenerator() { + return versionGenerator; + } + @Override public String getRootEntityName() { return entityMetamodel.getRootName(); @@ -4771,11 +4781,11 @@ public abstract class AbstractEntityPersister if ( superMappingType != null ) { ( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess ); if ( shouldProcessSuperMapping() ) { - this.discriminatorMapping = superMappingType.getDiscriminatorMapping(); - this.identifierMapping = superMappingType.getIdentifierMapping(); - this.naturalIdMapping = superMappingType.getNaturalIdMapping(); - this.versionMapping = superMappingType.getVersionMapping(); - this.rowIdMapping = superMappingType.getRowIdMapping(); + discriminatorMapping = superMappingType.getDiscriminatorMapping(); + identifierMapping = superMappingType.getIdentifierMapping(); + naturalIdMapping = superMappingType.getNaturalIdMapping(); + versionMapping = superMappingType.getVersionMapping(); + rowIdMapping = superMappingType.getRowIdMapping(); } else { prepareMappingModel( creationProcess, bootEntityDescriptor ); @@ -4787,10 +4797,17 @@ public abstract class AbstractEntityPersister // rootEntityDescriptor = this; } - final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel(); + final EntityMetamodel currentEntityMetamodel = getEntityMetamodel(); + + if ( currentEntityMetamodel.isVersioned() ) { + final InMemoryGenerator generator = currentEntityMetamodel.getVersionGenerator(); + // need to do this here because EntityMetamodel doesn't have the EntityVersionMapping :-( + versionGenerator = generator == null ? new VersionGeneration( versionMapping ) : generator; + } + int stateArrayPosition = getStateArrayInitialPosition( creationProcess ); - NonIdentifierAttribute[] properties = currentEntityMetamodel.getProperties(); + final NonIdentifierAttribute[] properties = currentEntityMetamodel.getProperties(); for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) { final NonIdentifierAttribute runtimeAttrDefinition = properties[i]; final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() ); @@ -4826,7 +4843,6 @@ public abstract class AbstractEntityPersister } - // register a callback for after all `#prepareMappingModel` calls have finished. here we want to delay the // generation of `staticFetchableList` because we need to wait until after all sub-classes have had their // `#prepareMappingModel` called (and their declared attribute mappings resolved) @@ -4834,10 +4850,10 @@ public abstract class AbstractEntityPersister "Entity(" + getEntityName() + ") `staticFetchableList` generator", () -> { if ( hasInsertGeneratedProperties() ) { - insertGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.INSERT ); + insertGeneratedValuesProcessor = createGeneratedValuesProcessor( INSERT ); } if ( hasUpdateGeneratedProperties() ) { - updateGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.UPDATE ); + updateGeneratedValuesProcessor = createGeneratedValuesProcessor( UPDATE ); } staticFetchableList = new ArrayList<>( attributeMappings.size() ); visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) ); @@ -4921,8 +4937,7 @@ public abstract class AbstractEntityPersister identifierMapping = creationProcess.processSubPart( EntityIdentifierMapping.ROLE_LOCAL_NAME, - (role, process) -> - generateIdentifierMapping( templateInstanceCreator, bootEntityDescriptor, process ) + (role, process) -> generateIdentifierMapping( templateInstanceCreator, bootEntityDescriptor, process ) ); versionMapping = generateVersionMapping( templateInstanceCreator, bootEntityDescriptor, creationProcess ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 38f003312e..3af2ade378 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -27,6 +27,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; +import org.hibernate.generator.InMemoryGenerator; +import org.hibernate.generator.internal.VersionGeneration; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.internal.FilterAliasGenerator; @@ -464,6 +466,10 @@ public interface EntityPersister return getIdentifierGenerator(); } + default InMemoryGenerator getVersionGenerator() { + return new VersionGeneration( getVersionMapping() ); + } + @Override default AttributeMapping getAttributeMapping(int position) { return getAttributeMappings().get( position ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java index ca2dedb471..c309bf1138 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/mutation/InsertCoordinator.java @@ -19,6 +19,7 @@ import org.hibernate.engine.jdbc.mutation.TableInclusionChecker; import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.EventType; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; @@ -103,11 +104,11 @@ public class InsertCoordinator extends AbstractMutationCoordinator { if ( entityMetamodel.hasPreInsertGeneratedValues() ) { final Generator[] generators = entityMetamodel.getGenerators(); for ( int i = 0; i < generators.length; i++ ) { - Generator generator = generators[i]; + final Generator generator = generators[i]; if ( generator != null && !generator.generatedByDatabase() && generator.generatesOnInsert() ) { - values[i] = ( (InMemoryGenerator) generator ).generate( session, entity, values[i] ); + values[i] = ( (InMemoryGenerator) generator ).generate( session, entity, values[i], EventType.INSERT ); entityPersister().setPropertyValue( entity, i, values[i] ); } } 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 33da84885e..065d2281f3 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 @@ -60,6 +60,7 @@ import static org.hibernate.engine.OptimisticLockStyle.DIRTY; import static org.hibernate.engine.OptimisticLockStyle.NONE; import static org.hibernate.engine.OptimisticLockStyle.VERSION; import static org.hibernate.engine.internal.Versioning.isVersionIncrementRequired; +import static org.hibernate.generator.EventType.UPDATE; import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_INT_ARRAY; /** @@ -452,7 +453,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple if ( generator != null && !generator.generatedByDatabase() && generator.generatesOnUpdate() ) { - newValues[i] = ( (InMemoryGenerator) generator ).generate( session, object, newValues[i] ); + newValues[i] = ( (InMemoryGenerator) generator ).generate( session, object, newValues[i], UPDATE ); entityPersister().setPropertyValue( object, i, newValues[i] ); fieldsPreUpdateNeeded[count++] = i; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index 6542a00448..e1d310acf8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -22,6 +22,7 @@ import org.hibernate.engine.FetchTiming; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.EventType; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.enhanced.Optimizer; @@ -508,7 +509,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio rootIdentity, new JdbcParameterBindingImpl( identifierMapping.getJdbcMapping(), - ( (InMemoryGenerator) generator ).generate( executionContext.getSession(), null, null ) + ( (InMemoryGenerator) generator ).generate( executionContext.getSession(), null, null, EventType.INSERT ) ) ); jdbcServices.getJdbcMutationExecutor().execute( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index d860577a70..2278b1a9d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -404,6 +404,7 @@ import jakarta.persistence.TemporalType; import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.Type; +import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD; import static org.hibernate.query.sqm.BinaryArithmeticOperator.MULTIPLY; @@ -1464,7 +1465,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base ExecutionContext executionContext) throws SQLException { getJdbcMapping().getJdbcValueBinder().bind( statement, - generator.generate( executionContext.getSession(), null, null ), + generator.generate( executionContext.getSession(), null, null, INSERT ), startPosition, executionContext.getSession() ); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java index 3a7d12f31c..92921c46c9 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/ValueGeneration.java @@ -55,7 +55,7 @@ public interface ValueGeneration extends InMemoryGenerator, InDatabaseGenerator ValueGenerator getValueGenerator(); @Override - default Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + default Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return getValueGenerator().generateValue( (Session) session, owner, currentValue ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java b/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java index 65aa40a086..ac2c1b3f6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/VmValueGeneration.java @@ -51,7 +51,7 @@ public class VmValueGeneration implements InMemoryGenerator { } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return generator.generateValue( (Session) session, owner, currentValue ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 70e374cbd4..769f30f2a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -139,6 +139,8 @@ public class EntityMetamodel implements Serializable { private final Set subclassEntityNames; private final Map,String> entityNameByInheritanceClassMap; + private final InMemoryGenerator versionGenerator; + private final BytecodeEnhancementMetadata bytecodeEnhancementMetadata; @Deprecated(since = "6.0") @@ -217,7 +219,7 @@ public class EntityMetamodel implements Serializable { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - this.generators = new Generator[propertySpan]; + generators = new Generator[propertySpan]; boolean foundPreInsertGeneratedValues = false; boolean foundPreUpdateGeneratedValues = false; @@ -233,6 +235,7 @@ public class EntityMetamodel implements Serializable { BitSet mutableIndexes = new BitSet(); boolean foundNonIdentifierPropertyNamedId = false; boolean foundUpdateableNaturalIdProperty = false; + InMemoryGenerator tempVersionGenerator = null; List props = persistentClass.getPropertyClosure(); for ( int i=0; i 1 ); + || ( getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() + && getBytecodeEnhancementMetadata().getLazyAttributesMetadata().getFetchGroupNames().size() > 1 ); dynamicInsert = persistentClass.useDynamicInsert(); polymorphic = persistentClass.isPolymorphic(); @@ -477,6 +492,10 @@ public class EntityMetamodel implements Serializable { return generators; } + public InMemoryGenerator getVersionGenerator() { + return versionGenerator; + } + public static class ValueGenerationStrategyException extends HibernateException { public ValueGenerationStrategyException(String message) { super( message ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java index 5e0010c36a..4ba81ef721 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/temporals/GeneratedUuidTests.java @@ -98,7 +98,7 @@ public class GeneratedUuidTests { } @Override - public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { return UUID.randomUUID(); } }