HHH-15782 use a Generator for seeding/incrementing versions

add javadoc for version generation
This commit is contained in:
Gavin 2022-12-04 02:09:46 +01:00 committed by Gavin King
parent 0228c3d185
commit 239dfa30fa
33 changed files with 335 additions and 156 deletions

View File

@ -11,13 +11,11 @@ import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.internal.ForeignKeys;
import org.hibernate.engine.internal.NonNullableTransientDependencies; import org.hibernate.engine.internal.NonNullableTransientDependencies;
import org.hibernate.engine.internal.Nullability; import org.hibernate.engine.internal.Nullability;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.CollectionKey;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.Status;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.metamodel.mapping.NaturalIdMapping; 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.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.engine.internal.Versioning.getVersion;
/** /**
* A base class for entity insert actions. * A base class for entity insert actions.
* *
@ -127,7 +127,7 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
*/ */
public final void makeEntityManaged() { public final void makeEntityManaged() {
nullifyTransientReferencesIfNotAlready(); nullifyTransientReferencesIfNotAlready();
final Object version = Versioning.getVersion( getState(), getPersister() ); final Object version = getVersion( getState(), getPersister() );
final PersistenceContext persistenceContextInternal = getSession().getPersistenceContextInternal(); final PersistenceContext persistenceContextInternal = getSession().getPersistenceContextInternal();
persistenceContextInternal.addEntity( persistenceContextInternal.addEntity(
getInstance(), getInstance(),

View File

@ -12,7 +12,6 @@ import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionEventListenerManager; 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.tuple.entity.EntityMetamodel;
import org.hibernate.type.TypeHelper; import org.hibernate.type.TypeHelper;
import static org.hibernate.engine.internal.Versioning.getVersion;
/** /**
* The action for performing entity updates. * 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 // have the entity entry doAfterTransactionCompletion post-update processing, passing it the
// update state and the new version (if one). // update state and the new version (if one).
if ( persister.isVersionPropertyGenerated() ) { if ( persister.isVersionPropertyGenerated() ) {
nextVersion = Versioning.getVersion( state, persister ); nextVersion = getVersion( state, persister );
} }
entry.postUpdate( instance, state, nextVersion ); entry.postUpdate( instance, state, nextVersion );
} }

View File

@ -47,14 +47,13 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
* property to be generated when any SQL statement to {@code insert} or * property to be generated when any SQL statement to {@code insert} or
* {@code update} the entity is executed. * {@code update} the entity is executed.
* <p> * <p>
* Every generator annotation type has an {@link Generator} * Every generator annotation type has an {@link Generator} implementation which
* implementation which is responsible for generating values. It must be either: * is responsible for generating values. It must be either:
* <ul> * <ul>
* <li>an {@link InMemoryGenerator}, for * <li>an {@link InMemoryGenerator}, for values that are generated in Java code,
* values that are generated in Java code, using a * using a {@link org.hibernate.tuple.ValueGenerator}, or
* {@link org.hibernate.tuple.ValueGenerator}, or * <li>an {@link InDatabaseGenerator}, for values which are generated by the
* <li>an {@link InDatabaseGenerator}, for * database.
* values which are generated by the database.
* </ul> * </ul>
* A generator annotation may have members, which are used to configure the * A generator annotation may have members, which are used to configure the
* generation strategy, when the strategy instance in initialized via * generation strategy, when the strategy instance in initialized via

View File

@ -23,6 +23,8 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.hibernate.generator.EventType.INSERT;
/** /**
* An {@code IdentifierBag} implements "bag" semantics more efficiently than * An {@code IdentifierBag} implements "bag" semantics more efficiently than
* a regular {@code Bag} by adding a synthetic identifier column to the * a regular {@code Bag} by adding a synthetic identifier column to the
@ -368,7 +370,7 @@ public class PersistentIdentifierBag<E> extends AbstractPersistentCollection<E>
final Integer loc = i++; final Integer loc = i++;
if ( !identifiers.containsKey( loc ) ) { if ( !identifiers.containsKey( loc ) ) {
//TODO: native ids //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 ); identifiers.put( loc, id );
} }
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.engine.internal; package org.hibernate.engine.internal;
import org.hibernate.Remove;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping;
@ -14,6 +15,9 @@ import org.hibernate.type.descriptor.java.VersionJavaType;
import org.jboss.logging.Logger; 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. * Utilities for dealing with optimistic locking values.
* *
@ -39,7 +43,7 @@ public final class Versioning {
* @param session The originating session * @param session The originating session
* @return The initial optimistic locking value * @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( final Object seed = versionMapping.getJavaType().seed(
versionMapping.getLength(), versionMapping.getLength(),
versionMapping.getPrecision(), versionMapping.getPrecision(),
@ -50,10 +54,40 @@ public final class Versioning {
return seed; return seed;
} }
/**
* Create an initial optimistic locking value using the for the version property
* <em>if required</em> 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} * Create an initial optimistic locking value according the {@link VersionJavaType}
* contract for the version property <b>if required</b> and inject it into * contract for the version property <em>if required</em> and inject it into the
* the snapshot state. * snapshot state.
* *
* @param fields The current snapshot state * @param fields The current snapshot state
* @param versionProperty The index of the version property * @param versionProperty The index of the version property
@ -61,28 +95,60 @@ public final class Versioning {
* @param session The originating session * @param session The originating session
* @return True if we injected a new version value into the fields array; false * @return True if we injected a new version value into the fields array; false
* otherwise. * otherwise.
*
* @deprecated Use {@link #seedVersion(Object, Object[], EntityPersister, SharedSessionContractImplementor)}
*/ */
@Deprecated(since = "6.2") @Remove
public static boolean seedVersion( public static boolean seedVersion(
Object[] fields, Object[] fields,
int versionProperty, int versionProperty,
EntityVersionMapping versionMapping, EntityVersionMapping versionMapping,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final Object initialVersion = fields[versionProperty]; final Object initialVersion = fields[versionProperty];
if ( if ( isNullInitialVersion( initialVersion ) ) {
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 )
) {
fields[versionProperty] = seed( versionMapping, session ); fields[versionProperty] = seed( versionMapping, session );
return true; return true;
} }
else {
LOG.tracev( "Using initial version: {0}", initialVersion ); LOG.tracev( "Using initial version: {0}", initialVersion );
return false; 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 * Generate the next increment in the optimistic locking value according
@ -121,11 +187,10 @@ public final class Versioning {
* @param persister The entity persister * @param persister The entity persister
*/ */
public static void setVersion(Object[] fields, Object version, EntityPersister persister) { public static void setVersion(Object[] fields, Object version, EntityPersister persister) {
if ( !persister.isVersioned() ) { if ( persister.isVersioned() ) {
return;
}
fields[ persister.getVersionProperty() ] = version; fields[ persister.getVersionProperty() ] = version;
} }
}
/** /**
* Extract the optimistic locking value out of the entity state snapshot. * Extract the optimistic locking value out of the entity state snapshot.
@ -135,10 +200,7 @@ public final class Versioning {
* @return The extracted optimistic locking value * @return The extracted optimistic locking value
*/ */
public static Object getVersion(Object[] fields, EntityPersister persister) { public static Object getVersion(Object[] fields, EntityPersister persister) {
if ( !persister.isVersioned() ) { return persister.isVersioned() ? fields[persister.getVersionProperty()] : null;
return null;
}
return fields[ persister.getVersionProperty() ];
} }
/** /**

View File

@ -7,7 +7,6 @@
package org.hibernate.event.internal; package org.hibernate.event.internal;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
@ -21,6 +20,8 @@ import org.hibernate.type.TypeHelper;
import org.jboss.logging.Logger; 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 * A convenience base class for listeners that respond to requests to reassociate an entity
* to a session ( such as through lock() or update() ). * to a session ( such as through lock() or update() ).
@ -65,7 +66,7 @@ public abstract class AbstractReassociateEventListener {
values, values,
source source
); );
Object version = Versioning.getVersion( values, persister ); Object version = getVersion( values, persister );
EntityEntry newEntry = persistenceContext.addEntity( EntityEntry newEntry = persistenceContext.addEntity(
object, object,

View File

@ -16,7 +16,6 @@ import org.hibernate.action.internal.EntityInsertAction;
import org.hibernate.classic.Lifecycle; import org.hibernate.classic.Lifecycle;
import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.Cascade;
import org.hibernate.engine.internal.CascadePoint; import org.hibernate.engine.internal.CascadePoint;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.CascadingAction; import org.hibernate.engine.spi.CascadingAction;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryExtraState; 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.engine.spi.Status;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.id.IdentifierGenerationException; import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistry;
@ -40,6 +38,10 @@ import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper; import org.hibernate.type.TypeHelper;
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; 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. * A convenience base class for listeners responding to save events.
@ -115,11 +117,11 @@ public abstract class AbstractSaveEventListener<C>
final EntityPersister persister = source.getEntityPersister( entityName, entity ); final EntityPersister persister = source.getEntityPersister( entityName, entity );
Generator generator = persister.getGenerator(); Generator generator = persister.getGenerator();
if ( !generator.generatedByDatabase() ) { if ( !generator.generatedByDatabase() ) {
Object generatedId = ( (InMemoryGenerator) generator ).generate( source, entity, null ); final Object generatedId = ( (InMemoryGenerator) generator ).generate( source, entity, null, INSERT );
if ( generatedId == null ) { if ( generatedId == null ) {
throw new IdentifierGenerationException( "null id generated for: " + entity.getClass() ); 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 ); return source.getIdentifier( entity );
} }
else { else {
@ -188,7 +190,7 @@ public abstract class AbstractSaveEventListener<C>
} }
private static EntityKey entityKey(Object entity, Object id, EntityPersister persister, boolean useIdentityColumn, EventSource source) { 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 EntityKey key = source.generateEntityKey( id, persister );
final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
final Object old = persistenceContext.getEntity( key ); final Object old = persistenceContext.getEntity( key );
@ -316,16 +318,16 @@ public abstract class AbstractSaveEventListener<C>
} }
private Object[] cloneAndSubstituteValues(Object entity, EntityPersister persister, C context, EventSource source, Object id) { 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(); Type[] types = persister.getPropertyTypes();
boolean substitute = substituteValuesIfNecessary(entity, id, values, persister, source); boolean substitute = substituteValuesIfNecessary( entity, id, values, persister, source );
if ( persister.hasCollections() ) { if ( persister.hasCollections() ) {
substitute = visitCollectionsBeforeSave(entity, id, values, types, source) || substitute; substitute = visitCollectionsBeforeSave( entity, id, values, types, source ) || substitute;
} }
if ( substitute ) { if ( substitute ) {
persister.setValues(entity, values ); persister.setValues( entity, values );
} }
TypeHelper.deepCopy( TypeHelper.deepCopy(
@ -363,7 +365,7 @@ public abstract class AbstractSaveEventListener<C>
id, id,
values, values,
entity, entity,
Versioning.getVersion( values, persister ), getVersion( values, persister ),
persister, persister,
isVersionIncrementDisabled(), isVersionIncrementDisabled(),
source source
@ -429,12 +431,7 @@ public abstract class AbstractSaveEventListener<C>
//keep the existing version number in the case of replicate! //keep the existing version number in the case of replicate!
if ( persister.isVersioned() ) { if ( persister.isVersioned() ) {
substitute = Versioning.seedVersion( substitute = seedVersion( entity, values, persister, source ) || substitute;
values,
persister.getVersionProperty(),
persister.getVersionMapping(),
source
) || substitute;
} }
return substitute; return substitute;
} }

View File

@ -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.isPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker; import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTracker;
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; 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 * An event that occurs for each entity instance at flush time
@ -381,13 +384,13 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener
if ( persister.isVersioned() ) { if ( persister.isVersioned() ) {
Object[] values = event.getPropertyValues(); Object[] values = event.getPropertyValues();
if ( entry.isBeingReplicated() ) { if ( entry.isBeingReplicated() ) {
return Versioning.getVersion( values, persister ); return getVersion( values, persister );
} }
else { else {
final Object nextVersion = isVersionIncrementRequired( event, entry ) 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 : entry.getVersion(); //use the current version
Versioning.setVersion( values, nextVersion, persister ); setVersion( values, nextVersion, persister );
return nextVersion; 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 ) { if ( entry.getStatus() == Status.DELETED ) {
return false; return false;
} }

View File

@ -24,6 +24,18 @@ package org.hibernate.generator;
* @see EventTypeSets * @see EventTypeSets
*/ */
public enum EventType { 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, 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; UPDATE;
} }

View File

@ -22,7 +22,7 @@ import static org.hibernate.generator.EventType.UPDATE;
* generated in Java code, or by the database. * generated in Java code, or by the database.
* <ul> * <ul>
* <li>Value generation via arbitrary code written in Java is the responsibility of the method * <li>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 * In this case, the generated value is written to the database just like any other field
* or property value. * or property value.
* <li>A value generated by the database might be generated implicitly, by a trigger, or using * <li>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 * 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. * called to produce a new value. {@link EventTypeSets} provides a convenient list of possibilities.
* <p> * <p>
* There are two especially important applications of this machinery:
* <ul>
* <li>
* An {@linkplain jakarta.persistence.Id identifier} generator is a generator capable of producing * 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 * 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 * 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 * using the {@link org.hibernate.annotations.IdGeneratorType} meta-annotation or the older-style
* {@link org.hibernate.annotations.GenericGenerator} annotation. * {@link org.hibernate.annotations.GenericGenerator} annotation.
* <li>
* 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.
* </ul>
* *
* @see org.hibernate.annotations.ValueGenerationType * @see org.hibernate.annotations.ValueGenerationType
* @see org.hibernate.annotations.IdGeneratorType * @see org.hibernate.annotations.IdGeneratorType

View File

@ -40,10 +40,10 @@ public interface InMemoryGenerator extends Generator {
* @param session The session from which the request originates. * @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 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 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 * @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() { default boolean generatedByDatabase() {
return false; return false;

View File

@ -91,7 +91,7 @@ public class CurrentTimestampGeneration implements InMemoryGenerator, InDatabase
} }
@Override @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 ); return generator.generateValue( (Session) session, owner, currentValue );
} }

View File

@ -89,7 +89,7 @@ public class SourceGeneration implements InMemoryGenerator {
} }
@Override @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 ); return valueGenerator.generateValue( (Session) session, owner, currentValue );
} }

View File

@ -51,7 +51,7 @@ public class TenantIdGeneration implements InMemoryGenerator {
} }
@Override @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(); SessionFactoryImplementor sessionFactory = session.getSessionFactory();
JavaType<Object> descriptor = sessionFactory.getTypeConfiguration().getJavaTypeRegistry() JavaType<Object> descriptor = sessionFactory.getTypeConfiguration().getJavaTypeRegistry()
.findDescriptor(propertyType); .findDescriptor(propertyType);

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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:
* <ul>
* <li>{@link org.hibernate.type.descriptor.java.VersionJavaType#seed} to seed an initial version, and
* <li>{@link org.hibernate.type.descriptor.java.VersionJavaType#next} to increment a version.
* </ul>
* 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<EventType> 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 );
}
}

View File

@ -142,7 +142,7 @@ public interface IdentifierGenerator extends InMemoryGenerator, ExportableProduc
* The {@code currentValue} is usually null for id generation. * The {@code currentValue} is usually null for id generation.
*/ */
@Override @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 ); return generate( session, owner );
} }

View File

@ -72,7 +72,7 @@ public class UuidGenerator implements InMemoryGenerator {
} }
@Override @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 ) ); return valueTransformer.transform( generator.generateUuid( session ) );
} }
} }

View File

@ -20,7 +20,6 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.PersistenceContext; 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.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; 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. * Concrete implementation of the {@link StatelessSession} API.
@ -97,17 +100,11 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
final EntityPersister persister = getEntityPersister( entityName, entity ); final EntityPersister persister = getEntityPersister( entityName, entity );
final Object id; final Object id;
final Object[] state = persister.getValues( entity ); final Object[] state = persister.getValues( entity );
Generator generator = persister.getGenerator(); final Generator generator = persister.getGenerator();
if ( !generator.generatedByDatabase() ) { if ( !generator.generatedByDatabase() ) {
id = ( (InMemoryGenerator) generator).generate( this, entity, null ); id = ( (InMemoryGenerator) generator).generate( this, entity, null, INSERT );
if ( persister.isVersioned() ) { if ( persister.isVersioned() ) {
boolean substitute = Versioning.seedVersion( if ( seedVersion( entity, state, persister, this ) ) {
state,
persister.getVersionProperty(),
persister.getVersionMapping(),
this
);
if ( substitute ) {
persister.setValues( entity, state ); persister.setValues( entity, state );
} }
} }
@ -156,8 +153,8 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
Object oldVersion; Object oldVersion;
if ( persister.isVersioned() ) { if ( persister.isVersioned() ) {
oldVersion = persister.getVersion( entity ); oldVersion = persister.getVersion( entity );
Object newVersion = Versioning.increment( oldVersion, persister.getVersionMapping(), this ); Object newVersion = incrementVersion( entity, oldVersion, persister, this );
Versioning.setVersion( state, newVersion, persister ); setVersion( state, newVersion, persister );
persister.setValues( entity, state ); persister.setValues( entity, state );
} }
else { else {

View File

@ -18,7 +18,6 @@ import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.engine.internal.CacheHelper; import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.StatefulPersistenceContext; import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.internal.TwoPhaseLoad; import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.internal.Versioning;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext; 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.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity;
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; 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; import static org.hibernate.loader.ast.internal.LoaderHelper.upgradeLock;
/** /**
@ -424,7 +424,7 @@ public class CacheEntityLoaderHelper {
source source
); );
} }
version = Versioning.getVersion( values, subclassPersister ); version = getVersion( values, subclassPersister );
LOG.tracef( "Cached Version : %s", version ); LOG.tracef( "Cached Version : %s", version );
final Object proxy = persistenceContext.getProxy( entityKey ); final Object proxy = persistenceContext.getProxy( entityKey );

View File

@ -28,7 +28,6 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator; import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
@ -41,6 +40,9 @@ import org.hibernate.type.ComponentType;
import org.hibernate.type.EmbeddedComponentType; import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.Type; 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}. * A mapping model object that represents an {@linkplain jakarta.persistence.Embeddable embeddable class}.
* <p> * <p>
@ -508,38 +510,36 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
} }
public static class ValueGenerationPlan implements CompositeNestedGeneratedValueGenerator.GenerationPlan { public static class ValueGenerationPlan implements CompositeNestedGeneratedValueGenerator.GenerationPlan {
private final Generator subGenerator; private final Generator subgenerator;
private final Setter injector; private final Setter injector;
public ValueGenerationPlan( public ValueGenerationPlan(Generator subgenerator, Setter injector) {
Generator subGenerator, this.subgenerator = subgenerator;
Setter injector) {
this.subGenerator = subGenerator;
this.injector = injector; this.injector = injector;
} }
@Override @Override
public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) { public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) {
if ( !subGenerator.generatedByDatabase() ) { if ( !subgenerator.generatedByDatabase() ) {
Object generatedId = ( (InMemoryGenerator) subGenerator ).generate( session, incomingObject, null ); Object generatedId = ( (InMemoryGenerator) subgenerator).generate( session, incomingObject, null, INSERT );
injector.set( injectionContext, generatedId ); injector.set( injectionContext, generatedId );
} }
else { else {
injector.set( injectionContext, IdentifierGeneratorHelper.POST_INSERT_INDICATOR ); injector.set( injectionContext, POST_INSERT_INDICATOR );
} }
} }
@Override @Override
public void registerExportables(Database database) { public void registerExportables(Database database) {
if ( subGenerator instanceof ExportableProducer ) { if ( subgenerator instanceof ExportableProducer ) {
( (ExportableProducer) subGenerator ).registerExportables( database ); ( (ExportableProducer) subgenerator).registerExportables( database );
} }
} }
@Override @Override
public void initialize(SqlStringGenerationContext context) { public void initialize(SqlStringGenerationContext context) {
if ( subGenerator instanceof IdentifierGenerator ) { if ( subgenerator instanceof IdentifierGenerator ) {
( (IdentifierGenerator) subGenerator ).initialize( context ); ( (IdentifierGenerator) subgenerator).initialize( context );
} }
} }
} }

View File

@ -8,7 +8,7 @@ package org.hibernate.metamodel.mapping;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor; 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 * GeneratedValueResolver impl for in-db generation. It extracts the generated value
@ -18,11 +18,11 @@ import org.hibernate.tuple.GenerationTiming;
*/ */
@Internal @Internal
public class InDatabaseGeneratedValueResolver implements GeneratedValueResolver { public class InDatabaseGeneratedValueResolver implements GeneratedValueResolver {
// private final GenerationTiming timing; private final EventType eventType;
private final int resultPosition; private final int resultPosition;
public InDatabaseGeneratedValueResolver(GenerationTiming timing, int resultPosition) { public InDatabaseGeneratedValueResolver(EventType eventType, int resultPosition) {
// this.timing = timing; this.eventType = eventType;
this.resultPosition = resultPosition; this.resultPosition = resultPosition;
} }

View File

@ -8,7 +8,7 @@ package org.hibernate.metamodel.mapping;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.tuple.GenerationTiming; import org.hibernate.generator.EventType;
import org.hibernate.generator.InMemoryGenerator; import org.hibernate.generator.InMemoryGenerator;
/** /**
@ -18,12 +18,12 @@ import org.hibernate.generator.InMemoryGenerator;
*/ */
@Internal @Internal
public class InMemoryGeneratedValueResolver implements GeneratedValueResolver { public class InMemoryGeneratedValueResolver implements GeneratedValueResolver {
// private final GenerationTiming generationTiming; private final EventType eventType;
private final InMemoryGenerator valueGenerator; private final InMemoryGenerator generator;
public InMemoryGeneratedValueResolver(InMemoryGenerator valueGenerator, GenerationTiming generationTiming) { public InMemoryGeneratedValueResolver(InMemoryGenerator generator, EventType eventType) {
this.valueGenerator = valueGenerator; this.generator = generator;
// this.generationTiming = generationTiming; this.eventType = eventType;
} }
// @Override // @Override
@ -33,6 +33,6 @@ public class InMemoryGeneratedValueResolver implements GeneratedValueResolver {
@Override @Override
public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session, Object currentValue) { public Object resolveGeneratedValue(Object[] row, Object entity, SharedSessionContractImplementor session, Object currentValue) {
return valueGenerator.generate( session, entity, currentValue ); return generator.generate( session, entity, currentValue, eventType );
} }
} }

View File

@ -14,6 +14,7 @@ import org.hibernate.LockOptions;
import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.generator.InDatabaseGenerator; import org.hibernate.generator.InDatabaseGenerator;
import org.hibernate.loader.ast.internal.LoaderSelectBuilder; import org.hibernate.loader.ast.internal.LoaderSelectBuilder;
import org.hibernate.metamodel.UnsupportedMappingException; 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.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.generator.Generator; import org.hibernate.generator.Generator;
import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER; import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER;
@ -59,7 +59,7 @@ public class GeneratedValuesProcessor {
public GeneratedValuesProcessor( public GeneratedValuesProcessor(
EntityMappingType entityDescriptor, EntityMappingType entityDescriptor,
GenerationTiming timing, EventType timing,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
this.entityDescriptor = entityDescriptor; this.entityDescriptor = entityDescriptor;
this.sessionFactory = sessionFactory; this.sessionFactory = sessionFactory;
@ -88,7 +88,7 @@ public class GeneratedValuesProcessor {
* populate the list of {@link GeneratedValueDescriptor}s by side effect, and * populate the list of {@link GeneratedValueDescriptor}s by side effect, and
* return a list of {@link AttributeMapping}s. * return a list of {@link AttributeMapping}s.
*/ */
private List<AttributeMapping> getGeneratedAttributes(EntityMappingType entityDescriptor, GenerationTiming timing) { private List<AttributeMapping> getGeneratedAttributes(EntityMappingType entityDescriptor, EventType timing) {
// todo (6.0): For now, we rely on the entity metamodel as composite attributes report // todo (6.0): For now, we rely on the entity metamodel as composite attributes report
// GenerationTiming.NEVER even if they have attributes that would need generation // GenerationTiming.NEVER even if they have attributes that would need generation
final Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators(); final Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators();

View File

@ -93,6 +93,9 @@ import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.LoadEvent; 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.Assigned;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator; 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.stat.spi.StatisticsImplementor;
import org.hibernate.generator.Generator; import org.hibernate.generator.Generator;
import org.hibernate.generator.InDatabaseGenerator; import org.hibernate.generator.InDatabaseGenerator;
import org.hibernate.tuple.GenerationTiming;
import org.hibernate.tuple.NonIdentifierAttribute; import org.hibernate.tuple.NonIdentifierAttribute;
import org.hibernate.tuple.entity.EntityBasedAssociationAttribute; import org.hibernate.tuple.entity.EntityBasedAssociationAttribute;
import org.hibernate.tuple.entity.EntityMetamodel; 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.processIfPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker; import static org.hibernate.engine.internal.ManagedTypeHelper.processIfSelfDirtinessTracker;
import static org.hibernate.engine.internal.Versioning.isVersionIncrementRequired; 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; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
/** /**
@ -414,7 +418,6 @@ public abstract class AbstractEntityPersister
private final Map<String,String[]> subclassPropertyAliases = new HashMap<>(); private final Map<String,String[]> subclassPropertyAliases = new HashMap<>();
private final Map<String,String[]> subclassPropertyColumnNames = new HashMap<>(); private final Map<String,String[]> subclassPropertyColumnNames = new HashMap<>();
private final JavaType<?> javaType; private final JavaType<?> javaType;
private final EntityRepresentationStrategy representationStrategy; private final EntityRepresentationStrategy representationStrategy;
@ -431,6 +434,8 @@ public abstract class AbstractEntityPersister
protected Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>(); protected Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>();
protected List<Fetchable> staticFetchableList; protected List<Fetchable> staticFetchableList;
private InMemoryGenerator versionGenerator;
protected ReflectionOptimizer.AccessOptimizer accessOptimizer; 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 ); .resolveStrategy( bootDescriptor, this, creationContext );
this.javaType = representationStrategy.getLoadJavaType(); javaType = representationStrategy.getLoadJavaType();
assert javaType != null; assert javaType != null;
final JdbcServices jdbcServices = factory.getServiceRegistry().getService( JdbcServices.class ); final JdbcServices jdbcServices = factory.getServiceRegistry().getService( JdbcServices.class );
@ -1933,7 +1938,7 @@ public abstract class AbstractEntityPersister
return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString(); return select.addCondition( rootTableKeyColumnNames, "=?" ).toStatementString();
} }
private GeneratedValuesProcessor createGeneratedValuesProcessor(GenerationTiming timing) { private GeneratedValuesProcessor createGeneratedValuesProcessor(EventType timing) {
return new GeneratedValuesProcessor( this, timing, getFactory() ); return new GeneratedValuesProcessor( this, timing, getFactory() );
} }
@ -3992,6 +3997,11 @@ public abstract class AbstractEntityPersister
return entityMetamodel.getIdentifierProperty().getGenerator(); return entityMetamodel.getIdentifierProperty().getGenerator();
} }
@Override
public InMemoryGenerator getVersionGenerator() {
return versionGenerator;
}
@Override @Override
public String getRootEntityName() { public String getRootEntityName() {
return entityMetamodel.getRootName(); return entityMetamodel.getRootName();
@ -4771,11 +4781,11 @@ public abstract class AbstractEntityPersister
if ( superMappingType != null ) { if ( superMappingType != null ) {
( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess ); ( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess );
if ( shouldProcessSuperMapping() ) { if ( shouldProcessSuperMapping() ) {
this.discriminatorMapping = superMappingType.getDiscriminatorMapping(); discriminatorMapping = superMappingType.getDiscriminatorMapping();
this.identifierMapping = superMappingType.getIdentifierMapping(); identifierMapping = superMappingType.getIdentifierMapping();
this.naturalIdMapping = superMappingType.getNaturalIdMapping(); naturalIdMapping = superMappingType.getNaturalIdMapping();
this.versionMapping = superMappingType.getVersionMapping(); versionMapping = superMappingType.getVersionMapping();
this.rowIdMapping = superMappingType.getRowIdMapping(); rowIdMapping = superMappingType.getRowIdMapping();
} }
else { else {
prepareMappingModel( creationProcess, bootEntityDescriptor ); prepareMappingModel( creationProcess, bootEntityDescriptor );
@ -4787,10 +4797,17 @@ public abstract class AbstractEntityPersister
// rootEntityDescriptor = this; // 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 ); int stateArrayPosition = getStateArrayInitialPosition( creationProcess );
NonIdentifierAttribute[] properties = currentEntityMetamodel.getProperties(); final NonIdentifierAttribute[] properties = currentEntityMetamodel.getProperties();
for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) { for ( int i = 0; i < currentEntityMetamodel.getPropertySpan(); i++ ) {
final NonIdentifierAttribute runtimeAttrDefinition = properties[i]; final NonIdentifierAttribute runtimeAttrDefinition = properties[i];
final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() ); 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 // 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 // generation of `staticFetchableList` because we need to wait until after all sub-classes have had their
// `#prepareMappingModel` called (and their declared attribute mappings resolved) // `#prepareMappingModel` called (and their declared attribute mappings resolved)
@ -4834,10 +4850,10 @@ public abstract class AbstractEntityPersister
"Entity(" + getEntityName() + ") `staticFetchableList` generator", "Entity(" + getEntityName() + ") `staticFetchableList` generator",
() -> { () -> {
if ( hasInsertGeneratedProperties() ) { if ( hasInsertGeneratedProperties() ) {
insertGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.INSERT ); insertGeneratedValuesProcessor = createGeneratedValuesProcessor( INSERT );
} }
if ( hasUpdateGeneratedProperties() ) { if ( hasUpdateGeneratedProperties() ) {
updateGeneratedValuesProcessor = createGeneratedValuesProcessor( GenerationTiming.UPDATE ); updateGeneratedValuesProcessor = createGeneratedValuesProcessor( UPDATE );
} }
staticFetchableList = new ArrayList<>( attributeMappings.size() ); staticFetchableList = new ArrayList<>( attributeMappings.size() );
visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) ); visitSubTypeAttributeMappings( attributeMapping -> staticFetchableList.add( attributeMapping ) );
@ -4921,8 +4937,7 @@ public abstract class AbstractEntityPersister
identifierMapping = creationProcess.processSubPart( identifierMapping = creationProcess.processSubPart(
EntityIdentifierMapping.ROLE_LOCAL_NAME, EntityIdentifierMapping.ROLE_LOCAL_NAME,
(role, process) -> (role, process) -> generateIdentifierMapping( templateInstanceCreator, bootEntityDescriptor, process )
generateIdentifierMapping( templateInstanceCreator, bootEntityDescriptor, process )
); );
versionMapping = generateVersionMapping( templateInstanceCreator, bootEntityDescriptor, creationProcess ); versionMapping = generateVersionMapping( templateInstanceCreator, bootEntityDescriptor, creationProcess );

View File

@ -27,6 +27,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource; 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.IdentifierGenerator;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.FilterAliasGenerator;
@ -464,6 +466,10 @@ public interface EntityPersister
return getIdentifierGenerator(); return getIdentifierGenerator();
} }
default InMemoryGenerator getVersionGenerator() {
return new VersionGeneration( getVersionMapping() );
}
@Override @Override
default AttributeMapping getAttributeMapping(int position) { default AttributeMapping getAttributeMapping(int position) {
return getAttributeMappings().get( position ); return getAttributeMappings().get( position );

View File

@ -19,6 +19,7 @@ import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService; import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
@ -103,11 +104,11 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
if ( entityMetamodel.hasPreInsertGeneratedValues() ) { if ( entityMetamodel.hasPreInsertGeneratedValues() ) {
final Generator[] generators = entityMetamodel.getGenerators(); final Generator[] generators = entityMetamodel.getGenerators();
for ( int i = 0; i < generators.length; i++ ) { for ( int i = 0; i < generators.length; i++ ) {
Generator generator = generators[i]; final Generator generator = generators[i];
if ( generator != null if ( generator != null
&& !generator.generatedByDatabase() && !generator.generatedByDatabase()
&& generator.generatesOnInsert() ) { && 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] ); entityPersister().setPropertyValue( entity, i, values[i] );
} }
} }

View File

@ -60,6 +60,7 @@ import static org.hibernate.engine.OptimisticLockStyle.DIRTY;
import static org.hibernate.engine.OptimisticLockStyle.NONE; import static org.hibernate.engine.OptimisticLockStyle.NONE;
import static org.hibernate.engine.OptimisticLockStyle.VERSION; import static org.hibernate.engine.OptimisticLockStyle.VERSION;
import static org.hibernate.engine.internal.Versioning.isVersionIncrementRequired; 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; import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_INT_ARRAY;
/** /**
@ -452,7 +453,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
if ( generator != null if ( generator != null
&& !generator.generatedByDatabase() && !generator.generatedByDatabase()
&& generator.generatesOnUpdate() ) { && 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] ); entityPersister().setPropertyValue( object, i, newValues[i] );
fieldsPreUpdateNeeded[count++] = i; fieldsPreUpdateNeeded[count++] = i;
} }

View File

@ -22,6 +22,7 @@ import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.generator.EventType;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.enhanced.Optimizer; import org.hibernate.id.enhanced.Optimizer;
@ -508,7 +509,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
rootIdentity, rootIdentity,
new JdbcParameterBindingImpl( new JdbcParameterBindingImpl(
identifierMapping.getJdbcMapping(), identifierMapping.getJdbcMapping(),
( (InMemoryGenerator) generator ).generate( executionContext.getSession(), null, null ) ( (InMemoryGenerator) generator ).generate( executionContext.getSession(), null, null, EventType.INSERT )
) )
); );
jdbcServices.getJdbcMutationExecutor().execute( jdbcServices.getJdbcMutationExecutor().execute(

View File

@ -404,6 +404,7 @@ import jakarta.persistence.TemporalType;
import jakarta.persistence.metamodel.SingularAttribute; import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type; import jakarta.persistence.metamodel.Type;
import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD; import static org.hibernate.query.sqm.BinaryArithmeticOperator.ADD;
import static org.hibernate.query.sqm.BinaryArithmeticOperator.MULTIPLY; import static org.hibernate.query.sqm.BinaryArithmeticOperator.MULTIPLY;
@ -1464,7 +1465,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
ExecutionContext executionContext) throws SQLException { ExecutionContext executionContext) throws SQLException {
getJdbcMapping().getJdbcValueBinder().bind( getJdbcMapping().getJdbcValueBinder().bind(
statement, statement,
generator.generate( executionContext.getSession(), null, null ), generator.generate( executionContext.getSession(), null, null, INSERT ),
startPosition, startPosition,
executionContext.getSession() executionContext.getSession()
); );

View File

@ -55,7 +55,7 @@ public interface ValueGeneration extends InMemoryGenerator, InDatabaseGenerator
ValueGenerator<?> getValueGenerator(); ValueGenerator<?> getValueGenerator();
@Override @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 ); return getValueGenerator().generateValue( (Session) session, owner, currentValue );
} }

View File

@ -51,7 +51,7 @@ public class VmValueGeneration implements InMemoryGenerator {
} }
@Override @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 ); return generator.generateValue( (Session) session, owner, currentValue );
} }
} }

View File

@ -139,6 +139,8 @@ public class EntityMetamodel implements Serializable {
private final Set<String> subclassEntityNames; private final Set<String> subclassEntityNames;
private final Map<Class<?>,String> entityNameByInheritanceClassMap; private final Map<Class<?>,String> entityNameByInheritanceClassMap;
private final InMemoryGenerator versionGenerator;
private final BytecodeEnhancementMetadata bytecodeEnhancementMetadata; private final BytecodeEnhancementMetadata bytecodeEnhancementMetadata;
@Deprecated(since = "6.0") @Deprecated(since = "6.0")
@ -217,7 +219,7 @@ public class EntityMetamodel implements Serializable {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.generators = new Generator[propertySpan]; generators = new Generator[propertySpan];
boolean foundPreInsertGeneratedValues = false; boolean foundPreInsertGeneratedValues = false;
boolean foundPreUpdateGeneratedValues = false; boolean foundPreUpdateGeneratedValues = false;
@ -233,6 +235,7 @@ public class EntityMetamodel implements Serializable {
BitSet mutableIndexes = new BitSet(); BitSet mutableIndexes = new BitSet();
boolean foundNonIdentifierPropertyNamedId = false; boolean foundNonIdentifierPropertyNamedId = false;
boolean foundUpdateableNaturalIdProperty = false; boolean foundUpdateableNaturalIdProperty = false;
InMemoryGenerator tempVersionGenerator = null;
List<Property> props = persistentClass.getPropertyClosure(); List<Property> props = persistentClass.getPropertyClosure();
for ( int i=0; i<props.size(); i++ ) { for ( int i=0; i<props.size(); i++ ) {
@ -298,16 +301,24 @@ public class EntityMetamodel implements Serializable {
propertyInsertability[i] = attribute.isInsertable(); propertyInsertability[i] = attribute.isInsertable();
propertyVersionability[i] = attribute.isVersionable(); propertyVersionability[i] = attribute.isVersionable();
nonlazyPropertyUpdateability[i] = attribute.isUpdateable() && !lazy; nonlazyPropertyUpdateability[i] = attribute.isUpdateable() && !lazy;
propertyCheckability[i] = propertyUpdateability[i] || propertyCheckability[i] = propertyUpdateability[i]
( propertyType.isAssociationType() && ( (AssociationType) propertyType ).isAlwaysDirtyChecked() ); || propertyType.isAssociationType() && ( (AssociationType) propertyType ).isAlwaysDirtyChecked();
cascadeStyles[i] = attribute.getCascadeStyle(); cascadeStyles[i] = attribute.getCascadeStyle();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final Generator generator = buildGenerator( property, creationContext ); final Generator generator = buildGenerator( property, creationContext );
generators[i] = generator;
if ( generator != null ) { if ( generator != null ) {
if ( i == tempVersionProperty && !generator.generatedByDatabase() ) {
// when we have an in-memory generator for the version, we
// want to plug it in to the older infrastructure specific
// to version generation, instead of treating it like a
// plain "value" generator for a regular attribute
tempVersionGenerator = (InMemoryGenerator) generator;
}
else {
generators[i] = generator;
if ( generatedWithNoParameter( generator ) ) { if ( generatedWithNoParameter( generator ) ) {
propertyInsertability[i] = false; propertyInsertability[i] = false;
propertyUpdateability[i] = false; propertyUpdateability[i] = false;
@ -329,6 +340,7 @@ public class EntityMetamodel implements Serializable {
} }
} }
} }
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -369,17 +381,19 @@ public class EntityMetamodel implements Serializable {
hasCacheableNaturalId = persistentClass.getNaturalIdCacheRegionName() != null; hasCacheableNaturalId = persistentClass.getNaturalIdCacheRegionName() != null;
} }
this.hasPreInsertGeneratedValues = foundPreInsertGeneratedValues; hasPreInsertGeneratedValues = foundPreInsertGeneratedValues;
this.hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues; hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues;
this.hasInsertGeneratedValues = foundPostInsertGeneratedValues; hasInsertGeneratedValues = foundPostInsertGeneratedValues;
this.hasUpdateGeneratedValues = foundPostUpdateGeneratedValues; hasUpdateGeneratedValues = foundPostUpdateGeneratedValues;
versionGenerator = tempVersionGenerator;
hasCascades = foundCascade; hasCascades = foundCascade;
hasCascadeDelete = foundCascadeDelete; hasCascadeDelete = foundCascadeDelete;
hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId; hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
versionPropertyIndex = tempVersionProperty; versionPropertyIndex = tempVersionProperty;
hasLazyProperties = hasLazy; hasLazyProperties = hasLazy;
if (hasLazyProperties) { if ( hasLazyProperties ) {
LOG.lazyPropertyFetchingAvailable(name); LOG.lazyPropertyFetchingAvailable(name);
} }
@ -405,7 +419,8 @@ public class EntityMetamodel implements Serializable {
selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate(); selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate();
dynamicUpdate = persistentClass.useDynamicUpdate() dynamicUpdate = persistentClass.useDynamicUpdate()
|| ( getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() && getBytecodeEnhancementMetadata().getLazyAttributesMetadata().getFetchGroupNames().size() > 1 ); || ( getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
&& getBytecodeEnhancementMetadata().getLazyAttributesMetadata().getFetchGroupNames().size() > 1 );
dynamicInsert = persistentClass.useDynamicInsert(); dynamicInsert = persistentClass.useDynamicInsert();
polymorphic = persistentClass.isPolymorphic(); polymorphic = persistentClass.isPolymorphic();
@ -477,6 +492,10 @@ public class EntityMetamodel implements Serializable {
return generators; return generators;
} }
public InMemoryGenerator getVersionGenerator() {
return versionGenerator;
}
public static class ValueGenerationStrategyException extends HibernateException { public static class ValueGenerationStrategyException extends HibernateException {
public ValueGenerationStrategyException(String message) { public ValueGenerationStrategyException(String message) {
super( message ); super( message );

View File

@ -98,7 +98,7 @@ public class GeneratedUuidTests {
} }
@Override @Override
public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue) { public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) {
return UUID.randomUUID(); return UUID.randomUUID();
} }
} }