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.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(),

View File

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

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
* {@code update} the entity is executed.
* <p>
* 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:
* <ul>
* <li>an {@link InMemoryGenerator}, for
* values that are generated in Java code, using a
* {@link org.hibernate.tuple.ValueGenerator}, or
* <li>an {@link InDatabaseGenerator}, for
* values which are generated by the database.
* <li>an {@link InMemoryGenerator}, for values that are generated in Java code,
* using a {@link org.hibernate.tuple.ValueGenerator}, or
* <li>an {@link InDatabaseGenerator}, for values which are generated by the
* database.
* </ul>
* A generator annotation may have members, which are used to configure the
* 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.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<E> extends AbstractPersistentCollection<E>
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 );
}
}

View File

@ -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
* <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}
* contract for the version property <b>if required</b> and inject it into
* the snapshot state.
* contract for the version property <em>if required</em> 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;
}
/**

View File

@ -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,

View File

@ -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<C>
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<C>
}
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<C>
}
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<C>
id,
values,
entity,
Versioning.getVersion( values, persister ),
getVersion( values, persister ),
persister,
isVersionIncrementDisabled(),
source
@ -429,12 +431,7 @@ public abstract class AbstractSaveEventListener<C>
//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;
}

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

View File

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

View File

@ -22,7 +22,7 @@ import static org.hibernate.generator.EventType.UPDATE;
* generated in Java code, or by the database.
* <ul>
* <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
* or property value.
* <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
* called to produce a new value. {@link EventTypeSets} provides a convenient list of possibilities.
* <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
* 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.
* <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.IdGeneratorType

View File

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

View File

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

View File

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

View File

@ -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<Object> descriptor = sessionFactory.getTypeConfiguration().getJavaTypeRegistry()
.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.
*/
@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 );
}

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -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<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
// GenerationTiming.NEVER even if they have attributes that would need generation
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.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<String,String[]> subclassPropertyAliases = new HashMap<>();
private final Map<String,String[]> subclassPropertyColumnNames = new HashMap<>();
private final JavaType<?> javaType;
private final EntityRepresentationStrategy representationStrategy;
@ -431,6 +434,8 @@ public abstract class AbstractEntityPersister
protected Map<String, AttributeMapping> declaredAttributeMappings = new LinkedHashMap<>();
protected List<Fetchable> 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 );

View File

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

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.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] );
}
}

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

View File

@ -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(

View File

@ -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<T extends Statement> 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()
);

View File

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

View File

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

View File

@ -139,6 +139,8 @@ public class EntityMetamodel implements Serializable {
private final Set<String> subclassEntityNames;
private final Map<Class<?>,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<Property> props = persistentClass.getPropertyClosure();
for ( int i=0; i<props.size(); i++ ) {
@ -298,34 +301,43 @@ public class EntityMetamodel implements Serializable {
propertyInsertability[i] = attribute.isInsertable();
propertyVersionability[i] = attribute.isVersionable();
nonlazyPropertyUpdateability[i] = attribute.isUpdateable() && !lazy;
propertyCheckability[i] = propertyUpdateability[i] ||
( propertyType.isAssociationType() && ( (AssociationType) propertyType ).isAlwaysDirtyChecked() );
propertyCheckability[i] = propertyUpdateability[i]
|| propertyType.isAssociationType() && ( (AssociationType) propertyType ).isAlwaysDirtyChecked();
cascadeStyles[i] = attribute.getCascadeStyle();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// generated value strategies ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final Generator generator = buildGenerator( property, creationContext );
generators[i] = generator;
if ( generator != null ) {
if ( generatedWithNoParameter( generator ) ) {
propertyInsertability[i] = false;
propertyUpdateability[i] = false;
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;
}
if ( generator.generatesOnInsert() ) {
if ( generator.generatedByDatabase() ) {
foundPostInsertGeneratedValues = true;
else {
generators[i] = generator;
if ( generatedWithNoParameter( generator ) ) {
propertyInsertability[i] = false;
propertyUpdateability[i] = false;
}
else {
foundPreInsertGeneratedValues = true;
if ( generator.generatesOnInsert() ) {
if ( generator.generatedByDatabase() ) {
foundPostInsertGeneratedValues = true;
}
else {
foundPreInsertGeneratedValues = true;
}
}
}
if ( generator.generatesOnUpdate() ) {
if ( generator.generatedByDatabase() ) {
foundPostUpdateGeneratedValues = true;
}
else {
foundPreUpdateGeneratedValues = true;
if ( generator.generatesOnUpdate() ) {
if ( generator.generatedByDatabase() ) {
foundPostUpdateGeneratedValues = true;
}
else {
foundPreUpdateGeneratedValues = true;
}
}
}
}
@ -369,17 +381,19 @@ public class EntityMetamodel implements Serializable {
hasCacheableNaturalId = persistentClass.getNaturalIdCacheRegionName() != null;
}
this.hasPreInsertGeneratedValues = foundPreInsertGeneratedValues;
this.hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues;
this.hasInsertGeneratedValues = foundPostInsertGeneratedValues;
this.hasUpdateGeneratedValues = foundPostUpdateGeneratedValues;
hasPreInsertGeneratedValues = foundPreInsertGeneratedValues;
hasPreUpdateGeneratedValues = foundPreUpdateGeneratedValues;
hasInsertGeneratedValues = foundPostInsertGeneratedValues;
hasUpdateGeneratedValues = foundPostUpdateGeneratedValues;
versionGenerator = tempVersionGenerator;
hasCascades = foundCascade;
hasCascadeDelete = foundCascadeDelete;
hasNonIdentifierPropertyNamedId = foundNonIdentifierPropertyNamedId;
versionPropertyIndex = tempVersionProperty;
hasLazyProperties = hasLazy;
if (hasLazyProperties) {
if ( hasLazyProperties ) {
LOG.lazyPropertyFetchingAvailable(name);
}
@ -405,7 +419,8 @@ public class EntityMetamodel implements Serializable {
selectBeforeUpdate = persistentClass.hasSelectBeforeUpdate();
dynamicUpdate = persistentClass.useDynamicUpdate()
|| ( getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() && getBytecodeEnhancementMetadata().getLazyAttributesMetadata().getFetchGroupNames().size() > 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 );

View File

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