HHH-16010 fix two bugs in natural id handling found just by inspection of code

- fix place where id and entity had always been passed in reversed order
- fix place where whole state array was passes instead of natural id array
- change the API of NaturalIdResolutions because it never needs the session,
  and the fix involved calling it from a place we did not have one
- and also clean up a bunch of warnings
- improve some visually-ugly code in AbstractEntityEntry
This commit is contained in:
Gavin 2023-01-10 12:16:52 +01:00 committed by Gavin King
parent 6d15c1d115
commit 5d86d88c03
24 changed files with 248 additions and 299 deletions

View File

@ -198,7 +198,7 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
if ( naturalIdMapping != null ) {
getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution(
getId(),
naturalIdMapping.extractNaturalIdFromEntityState( state, getSession() ),
naturalIdMapping.extractNaturalIdFromEntityState( state ),
getPersister(),
CachedNaturalIdValueSource.INSERT
);
@ -213,7 +213,7 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
public void handleNaturalIdPostSaveNotifications(Object generatedId) {
final NaturalIdMapping naturalIdMapping = getPersister().getNaturalIdMapping();
if ( naturalIdMapping != null ) {
final Object naturalIdValues = naturalIdMapping.extractNaturalIdFromEntityState( state, getSession() );
final Object naturalIdValues = naturalIdMapping.extractNaturalIdFromEntityState( state );
if ( isEarlyInsert() ) {
// with early insert, we still need to add a local (transactional) natural id cross-reference
getSession().getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution(

View File

@ -13,7 +13,6 @@ import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.service.spi.EventListenerGroup;
import org.hibernate.event.spi.EventSource;
@ -67,7 +66,7 @@ public class EntityDeleteAction extends EntityAction {
naturalIdValues = session.getPersistenceContextInternal().getNaturalIdResolutions()
.removeLocalResolution(
getId(),
naturalIdMapping.extractNaturalIdFromEntityState( state, session ),
naturalIdMapping.extractNaturalIdFromEntityState( state ),
getPersister()
);
}

View File

@ -96,7 +96,7 @@ public class EntityUpdateAction extends EntityAction {
previousNaturalIdValues = determinePreviousNaturalIdValues( persister, naturalIdMapping, id, previousState, session );
session.getPersistenceContextInternal().getNaturalIdResolutions().manageLocalResolution(
id,
naturalIdMapping.extractNaturalIdFromEntityState( state, session ),
naturalIdMapping.extractNaturalIdFromEntityState( state ),
persister,
CachedNaturalIdValueSource.UPDATE
);
@ -111,7 +111,7 @@ public class EntityUpdateAction extends EntityAction {
SharedSessionContractImplementor session) {
return previousState == null
? session.getPersistenceContextInternal().getNaturalIdSnapshot( id, persister )
: naturalIdMapping.extractNaturalIdFromEntityState( previousState, session );
: naturalIdMapping.extractNaturalIdFromEntityState( previousState );
}
protected Object[] getState() {
@ -183,7 +183,7 @@ public class EntityUpdateAction extends EntityAction {
if ( naturalIdMapping != null ) {
session.getPersistenceContextInternal().getNaturalIdResolutions().manageSharedResolution(
id,
naturalIdMapping.extractNaturalIdFromEntityState( state, session),
naturalIdMapping.extractNaturalIdFromEntityState( state ),
previousNaturalIdValues,
persister,
CachedNaturalIdValueSource.UPDATE

View File

@ -44,7 +44,7 @@ public class NaturalIdCacheKey implements Serializable {
final NaturalIdMapping naturalIdMapping = persister.getNaturalIdMapping();
this.naturalIdValues = naturalIdMapping.disassemble( naturalIdValues, session );
this.hashCode = naturalIdMapping.calculateHashCode( naturalIdValues, session );
this.hashCode = naturalIdMapping.calculateHashCode( naturalIdValues );
initTransients();
}

View File

@ -14,31 +14,43 @@ import org.hibernate.AssertionFailure;
import org.hibernate.CustomEntityDirtinessStrategy;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityEntryExtraState;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.SelfDirtinessTracker;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.HibernateProxy;
import static org.hibernate.LockMode.PESSIMISTIC_FORCE_INCREMENT;
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.EXISTS_IN_DATABASE;
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.IS_BEING_REPLICATED;
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.LOCK_MODE;
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.PREVIOUS_STATUS;
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.STATUS;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
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.spi.CachedNaturalIdValueSource.LOAD;
import static org.hibernate.engine.spi.Status.DELETED;
import static org.hibernate.engine.spi.Status.GONE;
import static org.hibernate.engine.spi.Status.MANAGED;
import static org.hibernate.engine.spi.Status.READ_ONLY;
import static org.hibernate.engine.spi.Status.SAVING;
import static org.hibernate.pretty.MessageHelper.infoString;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
/**
* A base implementation of EntityEntry
* A base implementation of {@link EntityEntry}.
*
* @author Gavin King
* @author Emmanuel Bernard
@ -94,19 +106,19 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
final EntityPersister persister,
final boolean disableVersionIncrement,
final PersistenceContext persistenceContext) {
setCompressedValue( EnumState.STATUS, status );
setCompressedValue( STATUS, status );
// not useful strictly speaking but more explicit
setCompressedValue( EnumState.PREVIOUS_STATUS, null );
setCompressedValue( PREVIOUS_STATUS, null );
// only retain loaded state if the status is not Status.READ_ONLY
if ( status != Status.READ_ONLY ) {
if ( status != READ_ONLY ) {
this.loadedState = loadedState;
}
this.id = id;
this.rowId = rowId;
setCompressedValue( BooleanState.EXISTS_IN_DATABASE, existsInDatabase );
setCompressedValue( EXISTS_IN_DATABASE, existsInDatabase );
this.version = version;
setCompressedValue( EnumState.LOCK_MODE, lockMode );
setCompressedValue( BooleanState.IS_BEING_REPLICATED, disableVersionIncrement );
setCompressedValue( LOCK_MODE, lockMode );
setCompressedValue( IS_BEING_REPLICATED, disableVersionIncrement );
this.persister = persister;
this.persistenceContext = persistenceContext;
}
@ -127,99 +139,94 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
final boolean existsInDatabase,
final boolean isBeingReplicated,
final PersistenceContext persistenceContext) {
if ( factory == null ) {
this.persister = null;
}
else {
this.persister = factory.getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor( entityName );
}
this.persister = factory == null
? null
: factory.getRuntimeMetamodels().getMappingMetamodel()
.getEntityDescriptor( entityName );
this.id = id;
setCompressedValue( EnumState.STATUS, status );
setCompressedValue( EnumState.PREVIOUS_STATUS, previousStatus );
setCompressedValue( STATUS, status );
setCompressedValue( PREVIOUS_STATUS, previousStatus );
this.loadedState = loadedState;
setDeletedState( deletedState );
this.version = version;
setCompressedValue( EnumState.LOCK_MODE, lockMode );
setCompressedValue( BooleanState.EXISTS_IN_DATABASE, existsInDatabase );
setCompressedValue( BooleanState.IS_BEING_REPLICATED, isBeingReplicated );
setCompressedValue( LOCK_MODE, lockMode );
setCompressedValue( EXISTS_IN_DATABASE, existsInDatabase );
setCompressedValue( IS_BEING_REPLICATED, isBeingReplicated );
this.rowId = null; // this is equivalent to the old behavior...
this.persistenceContext = persistenceContext;
}
@Override
public LockMode getLockMode() {
return getCompressedValue( EnumState.LOCK_MODE );
return getCompressedValue( LOCK_MODE );
}
@Override
public void setLockMode(LockMode lockMode) {
setCompressedValue( EnumState.LOCK_MODE, lockMode );
setCompressedValue( LOCK_MODE, lockMode );
}
@Override
public Status getStatus() {
return getCompressedValue( EnumState.STATUS );
return getCompressedValue( STATUS );
}
private Status getPreviousStatus() {
return getCompressedValue( EnumState.PREVIOUS_STATUS );
return getCompressedValue( PREVIOUS_STATUS );
}
@Override
public void setStatus(Status status) {
if ( status == Status.READ_ONLY ) {
if ( status == READ_ONLY ) {
//memory optimization
loadedState = null;
}
final Status currentStatus = this.getStatus();
if ( currentStatus != status ) {
setCompressedValue( EnumState.PREVIOUS_STATUS, currentStatus );
setCompressedValue( EnumState.STATUS, status );
setCompressedValue( PREVIOUS_STATUS, currentStatus );
setCompressedValue( STATUS, status );
}
}
@Override
public Object getId() {
public final Object getId() {
return id;
}
@Override
public Object[] getLoadedState() {
public final Object[] getLoadedState() {
return loadedState;
}
private static final Object[] DEFAULT_DELETED_STATE = null;
@Override
public Object[] getDeletedState() {
final EntityEntryExtraStateHolder extra = getExtraState( EntityEntryExtraStateHolder.class );
return extra != null ? extra.getDeletedState() : DEFAULT_DELETED_STATE;
return extra == null ? null : extra.getDeletedState();
}
@Override
public void setDeletedState(Object[] deletedState) {
EntityEntryExtraStateHolder extra = getExtraState( EntityEntryExtraStateHolder.class );
if ( extra == null && deletedState == DEFAULT_DELETED_STATE ) {
//this is the default value and we do not store the extra state
return;
final EntityEntryExtraStateHolder existingExtra = getExtraState( EntityEntryExtraStateHolder.class );
if ( existingExtra != null ) {
existingExtra.setDeletedState( deletedState );
}
if ( extra == null ) {
extra = new EntityEntryExtraStateHolder();
addExtraState( extra );
else if ( deletedState != null ) {
final EntityEntryExtraStateHolder newExtra = new EntityEntryExtraStateHolder();
newExtra.setDeletedState( deletedState );
addExtraState( newExtra );
}
extra.setDeletedState( deletedState );
//else this is the default value, we do not store the extra state
}
@Override
public boolean isExistsInDatabase() {
return getCompressedValue( BooleanState.EXISTS_IN_DATABASE );
return getCompressedValue( EXISTS_IN_DATABASE );
}
@Override
public Object getVersion() {
public final Object getVersion() {
return version;
}
@ -229,7 +236,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
}
@Override
public EntityPersister getPersister() {
public final EntityPersister getPersister() {
return persister;
}
@ -252,7 +259,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
@Override
public boolean isBeingReplicated() {
return getCompressedValue( BooleanState.IS_BEING_REPLICATED );
return getCompressedValue( IS_BEING_REPLICATED );
}
@Override
@ -262,21 +269,18 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
@Override
public void postUpdate(Object entity, Object[] updatedState, Object nextVersion) {
this.loadedState = updatedState;
loadedState = updatedState;
setLockMode( LockMode.WRITE );
final EntityPersister persister = getPersister();
if ( persister.isVersioned() ) {
this.version = nextVersion;
version = nextVersion;
persister.setValue( entity, persister.getVersionProperty(), nextVersion );
}
ManagedTypeHelper.processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes );
processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes );
final SharedSessionContractImplementor session = getPersistenceContext().getSession();
session
.getFactory()
.getCustomEntityDirtinessStrategy()
session.getFactory().getCustomEntityDirtinessStrategy()
.resetDirty( entity, persister, session.asSessionImplementor() );
}
@ -286,19 +290,19 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
@Override
public void postDelete() {
setCompressedValue( EnumState.PREVIOUS_STATUS, getStatus() );
setCompressedValue( EnumState.STATUS, Status.GONE );
setCompressedValue( BooleanState.EXISTS_IN_DATABASE, false );
setCompressedValue( PREVIOUS_STATUS, getStatus() );
setCompressedValue( STATUS, GONE );
setCompressedValue( EXISTS_IN_DATABASE, false );
}
@Override
public void postInsert(Object[] insertedState) {
setCompressedValue( BooleanState.EXISTS_IN_DATABASE, true );
setCompressedValue( EXISTS_IN_DATABASE, true );
}
@Override
public boolean isNullifiable(boolean earlyInsert, SharedSessionContractImplementor session) {
if ( getStatus() == Status.SAVING ) {
if ( getStatus() == SAVING ) {
return true;
}
else if ( earlyInsert ) {
@ -311,132 +315,134 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
@Override
public Object getLoadedValue(String propertyName) {
if ( loadedState == null || propertyName == null ) {
return null;
}
else {
final AttributeMapping attributeMapping = persister.findAttributeMapping( propertyName );
final int propertyIndex = attributeMapping.getStateArrayPosition();
return loadedState[propertyIndex];
}
return loadedState == null || propertyName == null
? null
: loadedState[ propertyIndex( propertyName ) ];
}
private int propertyIndex(String propertyName) {
return persister.findAttributeMapping( propertyName ).getStateArrayPosition();
}
@Override
public void overwriteLoadedStateCollectionValue(String propertyName, PersistentCollection<?> collection) {
// nothing to do if status is READ_ONLY
if ( getStatus() != Status.READ_ONLY ) {
if ( getStatus() != READ_ONLY ) {
assert propertyName != null;
assert loadedState != null;
final int propertyIndex = persister.findAttributeMapping( propertyName ).getStateArrayPosition();
loadedState[propertyIndex] = collection;
loadedState[ propertyIndex( propertyName ) ] = collection;
}
}
@Override
public boolean requiresDirtyCheck(Object entity) {
return isModifiableEntity()
&& ( !isUnequivocallyNonDirty( entity ) );
&& !isUnequivocallyNonDirty( entity );
}
@SuppressWarnings( {"SimplifiableIfStatement"})
private boolean isUnequivocallyNonDirty(Object entity) {
if ( ManagedTypeHelper.isSelfDirtinessTracker( entity ) ) {
boolean uninitializedProxy = false;
if ( isSelfDirtinessTracker( entity ) ) {
final boolean uninitializedProxy;
if ( isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
final PersistentAttributeInterceptor interceptor =
asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
return !enhancementAsProxyLazinessInterceptor.hasWrittenFieldNames();
EnhancementAsProxyLazinessInterceptor enhancementAsProxyLazinessInterceptor =
(EnhancementAsProxyLazinessInterceptor) interceptor;
return !enhancementAsProxyLazinessInterceptor.hasWrittenFieldNames(); //EARLY EXIT!
}
else {
uninitializedProxy = false;
}
}
else if ( isHibernateProxy( entity ) ) {
uninitializedProxy = HibernateProxy.extractLazyInitializer( entity ).isUninitialized();
uninitializedProxy = extractLazyInitializer( entity ).isUninitialized();
}
else {
uninitializedProxy = false;
}
// we never have to check an uninitialized proxy
return uninitializedProxy || !persister.hasCollections()
return uninitializedProxy
|| !persister.hasCollections()
&& !persister.hasMutableProperties()
&& !ManagedTypeHelper.asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
&& !asSelfDirtinessTracker( entity ).$$_hibernate_hasDirtyAttributes();
}
if ( isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptable interceptable = asPersistentAttributeInterceptable( entity );
final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// we never have to check an uninitialized proxy
return true;
else {
if ( isPersistentAttributeInterceptable( entity ) ) {
final PersistentAttributeInterceptor interceptor =
asPersistentAttributeInterceptable( entity ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
// we never have to check an uninitialized proxy
return true; //EARLY EXIT!
}
}
}
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
getPersistenceContext().getSession().getFactory().getCustomEntityDirtinessStrategy();
final Session session = getPersistenceContext().getSession().asSessionImplementor();
if ( customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), session ) ) {
return ! customEntityDirtinessStrategy.isDirty( entity, getPersister(), session );
final SessionImplementor session = getPersistenceContext().getSession().asSessionImplementor();
final CustomEntityDirtinessStrategy customEntityDirtinessStrategy =
session.getFactory().getCustomEntityDirtinessStrategy();
return customEntityDirtinessStrategy.canDirtyCheck( entity, getPersister(), session )
&& !customEntityDirtinessStrategy.isDirty( entity, getPersister(), session );
}
if ( getPersister().hasMutableProperties() ) {
return false;
}
return false;
}
@Override
public boolean isModifiableEntity() {
final Status status = getStatus();
final Status previousStatus = getPreviousStatus();
return getPersister().isMutable()
&& status != Status.READ_ONLY
&& ! ( status == Status.DELETED && previousStatus == Status.READ_ONLY );
return persister.isMutable()
&& status != READ_ONLY
&& ! ( status == DELETED && previousStatus == READ_ONLY );
}
@Override
public void forceLocked(Object entity, Object nextVersion) {
version = nextVersion;
loadedState[ persister.getVersionProperty() ] = version;
setLockMode( LockMode.PESSIMISTIC_FORCE_INCREMENT );
setLockMode( PESSIMISTIC_FORCE_INCREMENT );
persister.setValue( entity, getPersister().getVersionProperty(), nextVersion );
}
@Override
public boolean isReadOnly() {
final Status status = getStatus();
if (status != Status.MANAGED && status != Status.READ_ONLY) {
if ( status != MANAGED && status != READ_ONLY ) {
throw new HibernateException("instance was not in a valid state");
}
return status == Status.READ_ONLY;
return status == READ_ONLY;
}
@Override
public void setReadOnly(boolean readOnly, Object entity) {
if ( readOnly == isReadOnly() ) {
// simply return since the status is not being changed
return;
}
if ( readOnly ) {
setStatus( Status.READ_ONLY );
loadedState = null;
}
else {
if ( ! persister.isMutable() ) {
throw new IllegalStateException( "Cannot make an immutable entity modifiable." );
if ( readOnly != isReadOnly() ) {
if ( readOnly ) {
setStatus( READ_ONLY );
loadedState = null;
}
else {
if ( ! persister.isMutable() ) {
throw new IllegalStateException( "Cannot make an entity of immutable type '"
+ persister.getEntityName() + "' modifiable" );
}
setStatus( MANAGED );
loadedState = persister.getValues( entity );
if ( persister.hasNaturalIdentifier() ) {
getPersistenceContext().getNaturalIdResolutions().manageLocalResolution(
id,
persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState ),
persister,
LOAD
);
}
}
setStatus( Status.MANAGED );
loadedState = getPersister().getValues( entity );
getPersistenceContext().getNaturalIdResolutions().manageLocalResolution(
id, loadedState, persister,
CachedNaturalIdValueSource.LOAD
);
}
}
@Override
public String toString() {
return "EntityEntry" +
MessageHelper.infoString( getPersister().getEntityName(), id ) +
'(' + getStatus() + ')';
return "EntityEntry"
+ infoString( getPersister().getEntityName(), id )
+ '(' + getStatus() + ')';
}
@Override
@ -466,7 +472,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
}
}
@Override
@Override @SuppressWarnings("unchecked")
public <T extends EntityEntryExtraState> T getExtraState(Class<T> extraStateType) {
if ( next == null ) {
return null;
@ -555,11 +561,11 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
private EnumState(int offset, Class<E> enumType) {
final E[] enumConstants = enumType.getEnumConstants();
// In case any of the enums cannot be stored in 4 bits anymore, we'd have to re-structure the compressed
// state int
// In case any of the enums cannot be stored in 4 bits anymore,
// we'd have to re-structure the compressed state int
if ( enumConstants.length > 15 ) {
throw new AssertionFailure( "Cannot store enum type " + enumType.getName() + " in compressed state as"
+ " it has too many values." );
throw new AssertionFailure( "Cannot store enum type " + enumType.getName()
+ " in compressed state as it has too many values." );
}
this.offset = offset;

View File

@ -18,9 +18,13 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.LOCK_MODE;
/**
* An EntityEntry implementation for immutable entities. Note that this implementation is not completely
* immutable in terms of its internal state; the term immutable here refers to the entity it describes.
* An {@link EntityEntry} implementation for immutable entities.
*
* @implNote Note that this implementation is not completely immutable in terms of its internal state;
* the term immutable here refers to the entity it describes.
*
* @author Gavin King
* @author Emmanuel Bernard
@ -82,13 +86,11 @@ public final class ImmutableEntityEntry extends AbstractEntityEntry {
public void setLockMode(LockMode lockMode) {
switch ( lockMode ) {
case NONE:
case READ: {
setCompressedValue( EnumState.LOCK_MODE, lockMode );
case READ:
setCompressedValue( LOCK_MODE, lockMode );
break;
}
default: {
default:
throw new UnsupportedLockAttemptException( "Lock mode not supported" );
}
}
}

View File

@ -17,7 +17,7 @@ import org.hibernate.engine.spi.Status;
import org.hibernate.persister.entity.EntityPersister;
/**
* An EntityEntry implementation for mutable entities.
* An {@link EntityEntry} implementation for mutable entities.
*
* @author Gavin King
* @author Emmanuel Bernard

View File

@ -23,7 +23,6 @@ import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.Resolution;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.EventSource;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.NaturalIdLogging;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
@ -421,12 +420,8 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa
}
final EntityPersister persister = locatePersisterForKey( entityDescriptor.getEntityPersister() );
final Object naturalIdValuesFromCurrentObjectState = naturalIdMapping.extractNaturalIdFromEntity( entity, session() );
final boolean changed = ! sameAsCached(
persister,
pk,
naturalIdValuesFromCurrentObjectState
);
final Object naturalIdValuesFromCurrentObjectState = naturalIdMapping.extractNaturalIdFromEntity( entity );
final boolean changed = !sameAsCached( persister, pk, naturalIdValuesFromCurrentObjectState );
if ( changed ) {
final Object cachedNaturalIdValues = findCachedNaturalIdById( pk, persister );
@ -494,7 +489,7 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa
throw new IllegalArgumentException( "Entity did not define a natural-id" );
}
naturalIdMapping.validateInternalForm( naturalIdValues, persistenceContext.getSession() );
naturalIdMapping.validateInternalForm( naturalIdValues );
}
private boolean isValidValue(Object naturalIdValues, EntityMappingType entityDescriptor) {
@ -504,7 +499,7 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa
throw new IllegalArgumentException( "Entity did not define a natural-id" );
}
naturalIdMapping.validateInternalForm( naturalIdValues, persistenceContext.getSession() );
naturalIdMapping.validateInternalForm( naturalIdValues );
// validateInternalForm would have thrown an exception if not
return true;
@ -775,7 +770,7 @@ public class NaturalIdResolutionsImpl implements NaturalIdResolutions, Serializa
final int prime = 31;
int hashCodeCalculation = 1;
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.hashCode();
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue, persistenceContext.getSession() );
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue );
this.hashCode = hashCodeCalculation;
}

View File

@ -336,12 +336,9 @@ public class StatefulPersistenceContext implements PersistenceContext {
// check to see if the natural id is mutable/immutable
if ( persister.getEntityMetamodel().hasImmutableNaturalId() ) {
// an immutable natural-id is not retrieved during a normal database-snapshot operation...
final Object dbValue = persister.getNaturalIdentifierSnapshot( id, session );
naturalIdResolutions.cacheResolutionFromLoad(
id, dbValue, persister
);
return dbValue;
final Object naturalIdFromDb = persister.getNaturalIdentifierSnapshot( id, session );
naturalIdResolutions.cacheResolutionFromLoad( id, naturalIdFromDb, persister );
return naturalIdFromDb;
}
else {
// for a mutable natural id there is a likelihood that the information will already be
@ -356,9 +353,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
for ( int i = 0; i < props.length; i++ ) {
naturalIdSnapshotSubSet[i] = entitySnapshot[ props[i] ];
}
naturalIdResolutions.cacheResolutionFromLoad(
id, naturalIdSnapshotSubSet, persister
);
naturalIdResolutions.cacheResolutionFromLoad( id, naturalIdSnapshotSubSet, persister );
return naturalIdSnapshotSubSet;
}
}

View File

@ -14,10 +14,11 @@ import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.persister.entity.EntityPersister;
/**
* We need an entry to tell us all about the current state of an object with respect to its persistent state
* Information about the current state of a managed entity instance with respect
* to its persistent state.
*
* Implementation Warning: Hibernate needs to instantiate a high amount of instances of this class,
* therefore we need to take care of its impact on memory consumption.
* @implNote Hibernate instantiates very many of instances of this type,
* and so we need to take care of its impact on memory consumption.
*
* @author Gavin King
* @author Emmanuel Bernard
@ -54,9 +55,10 @@ public interface EntityEntry {
EntityPersister getPersister();
/**
* Get the EntityKey based on this EntityEntry.
* @return the EntityKey
* @throws IllegalStateException if getId() is null
* Get the {@link EntityKey} for this entry.
*
* @return the {@link EntityKey}
* @throws IllegalStateException if {@link #getId()} is null
*/
EntityKey getEntityKey();
@ -68,8 +70,8 @@ public interface EntityEntry {
/**
* Handle updating the internal state of the entry after actually performing
* the database update. Specifically we update the snapshot information and
* escalate the lock mode
* the database update. Specifically, we update the snapshot information and
* escalate the lock mode.
*
* @param entity The entity instance
* @param updatedState The state calculated after the update (becomes the
@ -80,43 +82,45 @@ public interface EntityEntry {
/**
* After actually deleting a row, record the fact that the instance no longer
* exists in the database
* exists in the database.
*/
void postDelete();
/**
* After actually inserting a row, record the fact that the instance exists on the
* database (needed for identity-column key generation)
* After actually inserting a row, record the fact that the instance exists
* in the database (needed for identity column key generation).
*/
void postInsert(Object[] insertedState);
boolean isNullifiable(boolean earlyInsert, SharedSessionContractImplementor session);
/**
* Not sure this is the best method name, but the general idea here is to return {@code true} if the entity can
* possibly be dirty. This can only be the case if it is in a modifiable state (not read-only/deleted) and it
* either has mutable properties or field-interception is not telling us it is dirty. Clear as mud? :/
*
* A name like canPossiblyBeDirty might be better
* Returns {@code true} if the entity can possibly be dirty. This can only
* be the case if it is in a modifiable state (not read-only nor deleted)
* and it either has mutable properties or field-interception is not telling
* us that it is dirty.
*
* @param entity The entity to test
*
* @return {@code true} indicates that the entity could possibly be dirty and that dirty check
* should happen; {@code false} indicates there is no way the entity can be dirty
* @return {@code true} indicates that the entity could possibly be dirty
* and that the dirty-check should happen;
* {@code false} indicates there is no way the entity can be dirty
*/
boolean requiresDirtyCheck(Object entity);
/**
* Can the entity be modified?
*
* The entity is modifiable if all of the following are true:
* <p>
* The entity is modifiable if all the following are true:
* <ul>
* <li>the entity class is mutable</li>
* <li>the entity is not read-only</li>
* <li>if the current status is Status.DELETED, then the entity was not read-only when it was deleted</li>
* <li>the entity class is mutable,
* <li>the entity is not read-only, and
* <li>if the current status is {@link Status#DELETED},
* then the entity was not read-only when it was deleted.
* </ul>
*
* @return true, if the entity is modifiable; false, otherwise,
* @return {@code true}, if the entity is modifiable;
* {@code false}, otherwise,
*/
boolean isModifiableEntity();
@ -131,7 +135,8 @@ public interface EntityEntry {
/**
* Custom serialization routine used during serialization of a
* Session/PersistenceContext for increased performance.
* {@code Session}/{@code PersistenceContext} for increased
* performance.
*
* @param oos The stream to which we should write the serial data.
*

View File

@ -29,7 +29,7 @@ public interface NaturalIdResolutions {
/**
* Removes a natural-id-to-identifier resolution.
*
* <p>
* Handles both the local (transactional) and shared (second-level) caches.
*
* @return The cached values, if any. May be different from incoming values.
@ -50,7 +50,7 @@ public interface NaturalIdResolutions {
/**
* Removes any local cross-reference, returning the previously cached value if one.
*
* <p>
* Again, this only effects the persistence-context cache, not the L2 cache
*/
Object removeLocalResolution(Object id, Object naturalId, EntityMappingType entityDescriptor);
@ -93,10 +93,10 @@ public interface NaturalIdResolutions {
/**
* Part of the "load synchronization process".
*
* <p>
* Responsible for maintaining cross-reference entries when natural-id values were found
* to have changed.
*
* <p>
* Also responsible for tracking the old values as no longer valid until the next flush
* because otherwise going to the database would just re-pull the old values as valid.
* In this responsibility, {@link #cleanupFromSynchronizations} is the inverse process

View File

@ -111,7 +111,7 @@ public class DefaultEvictEventListener implements EvictEventListener {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
if ( persister.hasNaturalIdentifier() ) {
persistenceContext.getNaturalIdResolutions().handleEviction( object, key.getIdentifier(), persister );
persistenceContext.getNaturalIdResolutions().handleEviction( key.getIdentifier(), object, persister );
}
// remove all collections for the entity from the session-level cache

View File

@ -545,7 +545,7 @@ public class DefaultLoadEventListener implements LoadEventListener {
session.getPersistenceContextInternal().getNaturalIdResolutions()
.cacheResolutionFromLoad(
event.getEntityId(),
persister.getNaturalIdMapping().extractNaturalIdFromEntity( entity, session ),
persister.getNaturalIdMapping().extractNaturalIdFromEntity( entity ),
persister
);
}

View File

@ -92,7 +92,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
@Override
public T load(Object naturalIdValue, NaturalIdLoadOptions options, SharedSessionContractImplementor session) {
return selectByNaturalId(
naturalIdMapping().normalizeInput( naturalIdValue, session ),
naturalIdMapping().normalizeInput( naturalIdValue ),
options,
(tableGroup, creationState) -> entityDescriptor.createDomainResult(
new NavigablePath( entityDescriptor().getRootPathName() ),
@ -262,7 +262,7 @@ public abstract class AbstractNaturalIdLoader<T> implements NaturalIdLoader<T> {
@Override
public Object resolveNaturalIdToId(Object naturalIdValue, SharedSessionContractImplementor session) {
return selectByNaturalId(
naturalIdMapping().normalizeInput( naturalIdValue, session ),
naturalIdMapping().normalizeInput( naturalIdValue ),
NaturalIdLoadOptions.NONE,
(tableGroup, creationState) -> entityDescriptor.getIdentifierMapping().createDomainResult(
tableGroup.getNavigablePath().append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),

View File

@ -74,7 +74,7 @@ public class MultiNaturalIdLoaderStandard<E> implements MultiNaturalIdLoader<E>
(naturalId, session1) -> {
// `naturalId` here is the one passed in by the API as part of the values array
// todo (6.0) : use this to help create the ordered results
return entityDescriptor.getNaturalIdMapping().normalizeInput( naturalId, session );
return entityDescriptor.getNaturalIdMapping().normalizeInput( naturalId );
},
session.getLoadQueryInfluencers(),
lockOptions,

View File

@ -51,18 +51,12 @@ public class NaturalIdLoadAccessImpl<T> extends BaseNaturalIdLoadAccessImpl<T> i
@Override
public final T getReference() {
final SessionImplementor session = getContext().getSession();
final Object normalizedValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters, session );
return doGetReference( normalizedValue );
return doGetReference( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters ) );
}
@Override
public final T load() {
final SessionImplementor session = getContext().getSession();
final Object normalizedValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters, session );
return doLoad( normalizedValue );
return doLoad( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdParameters ) );
}
@Override

View File

@ -66,21 +66,13 @@ public class SimpleNaturalIdLoadAccessImpl<T>
@Override
public T getReference(Object naturalIdValue) {
verifySimplicity( naturalIdValue );
final SessionImplementor session = getContext().getSession();
final Object normalizedNaturalIdValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue, session );
return doGetReference( normalizedNaturalIdValue );
return doGetReference( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue) );
}
@Override
public T load(Object naturalIdValue) {
verifySimplicity( naturalIdValue );
final SessionImplementor session = getContext().getSession();
final Object normalizedNaturalIdValue = entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue, session );
return doLoad( normalizedNaturalIdValue );
return doLoad( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue) );
}
private void verifySimplicity(Object naturalIdValue) {

View File

@ -8,6 +8,7 @@ package org.hibernate.metamodel.mapping;
import java.util.List;
import org.hibernate.Incubating;
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.loader.ast.spi.MultiNaturalIdLoader;
@ -39,6 +40,7 @@ import org.hibernate.loader.ast.spi.NaturalIdLoader;
*
* @author Steve Ebersole
*/
@Incubating
public interface NaturalIdMapping extends VirtualModelPart {
String PART_NAME = "{natural-id}";
@ -80,7 +82,7 @@ public interface NaturalIdMapping extends VirtualModelPart {
*
* @return The extracted natural id values. This is a normalized
*/
Object extractNaturalIdFromEntityState(Object[] state, SharedSessionContractImplementor session);
Object extractNaturalIdFromEntityState(Object[] state);
/**
* Given an entity instance, extract the normalized natural id representation
@ -89,31 +91,28 @@ public interface NaturalIdMapping extends VirtualModelPart {
*
* @return The extracted natural id values
*/
Object extractNaturalIdFromEntity(Object entity, SharedSessionContractImplementor session);
Object extractNaturalIdFromEntity(Object entity);
/**
* Normalize a user-provided natural-id value into the representation Hibernate uses internally
*
* @param incoming The user-supplied value
*
* @return The normalized, internal representation
*/
Object normalizeInput(Object incoming, SharedSessionContractImplementor session);
Object normalizeInput(Object incoming);
/**
* Validates a natural id value(s) for the described natural-id based on the expected internal representation
*/
void validateInternalForm(Object naturalIdValue, SharedSessionContractImplementor session);
void validateInternalForm(Object naturalIdValue);
/**
* Calculate the hash-code of a natural-id value
*
* @param value The natural-id value
*
* @return The hash-code
*/
int calculateHashCode(Object value, SharedSessionContractImplementor session);
int calculateHashCode(Object value);
/**
* Make a loader capable of loading a single entity by natural-id

View File

@ -59,7 +59,6 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
// todo (6.0) : create a composite MappingType for this descriptor's Object[]?
private final List<SingularAttributeMapping> attributes;
private final JavaType<?> jtd;
private List<JdbcMapping> jdbcMappings;
@ -67,13 +66,9 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
EntityMappingType declaringType,
List<SingularAttributeMapping> attributes,
MappingModelCreationProcess creationProcess) {
super( declaringType, isMutable( declaringType, attributes, creationProcess ) );
super( declaringType, isMutable( attributes ) );
this.attributes = attributes;
jtd = creationProcess.getCreationContext().getTypeConfiguration().getJavaTypeRegistry().getDescriptor(
Object[].class
);
creationProcess.registerInitializationCallback(
"Determine compound natural-id JDBC mappings ( " + declaringType.getEntityName() + ")",
() -> {
@ -90,10 +85,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
);
}
private static boolean isMutable(
EntityMappingType entityDescriptor,
List<SingularAttributeMapping> attributes,
MappingModelCreationProcess creationProcess) {
private static boolean isMutable(List<SingularAttributeMapping> attributes) {
for ( int i = 0; i < attributes.size(); i++ ) {
final SingularAttributeMapping attributeMapping = attributes.get( i );
final AttributeMetadata metadata = attributeMapping.getAttributeMetadata();
@ -106,7 +98,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
}
@Override
public Object[] extractNaturalIdFromEntityState(Object[] state, SharedSessionContractImplementor session) {
public Object[] extractNaturalIdFromEntityState(Object[] state) {
if ( state == null ) {
return null;
}
@ -126,7 +118,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
}
@Override
public Object[] extractNaturalIdFromEntity(Object entity, SharedSessionContractImplementor session) {
public Object[] extractNaturalIdFromEntity(Object entity) {
final Object[] values = new Object[ attributes.size() ];
for ( int i = 0; i < attributes.size(); i++ ) {
@ -138,7 +130,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
@Override
@SuppressWarnings( "rawtypes" )
public Object[] normalizeInput(Object incoming, SharedSessionContractImplementor session) {
public Object[] normalizeInput(Object incoming) {
if ( incoming instanceof Object[] ) {
return (Object[]) incoming;
}
@ -157,7 +149,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
}
@Override
public void validateInternalForm(Object naturalIdValue, SharedSessionContractImplementor session) {
public void validateInternalForm(Object naturalIdValue) {
if ( naturalIdValue == null ) {
return;
}
@ -179,7 +171,7 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
}
@Override
public int calculateHashCode(Object value, SharedSessionContractImplementor session) {
public int calculateHashCode(Object value) {
return 0;
}
@ -194,11 +186,11 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityPersister persister = getDeclaringType().getEntityPersister();
final Object[] naturalId = extractNaturalIdFromEntityState( currentState, session );
final Object[] naturalId = extractNaturalIdFromEntityState( currentState );
final Object snapshot = loadedState == null
? persistenceContext.getNaturalIdSnapshot( id, persister )
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState, session );
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState );
final Object[] previousNaturalId = (Object[]) snapshot;
assert naturalId.length == getNaturalIdAttributes().size();
@ -553,8 +545,6 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
}
private static class AssemblerImpl implements DomainResultAssembler<Object[]> {
private final NavigablePath navigablePath;
private final CompoundNaturalIdMapping naturalIdMapping;
private final JavaType<Object[]> jtd;
private final DomainResultAssembler<?>[] subAssemblers;
@ -565,8 +555,6 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement
CompoundNaturalIdMapping naturalIdMapping,
JavaType<Object[]> jtd,
AssemblerCreationState creationState) {
this.navigablePath = navigablePath;
this.naturalIdMapping = naturalIdMapping;
this.jtd = jtd;
// we don't even register the Initializer here... its really no-op.

View File

@ -71,10 +71,10 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityPersister persister = getDeclaringType().getEntityPersister();
final Object naturalId = extractNaturalIdFromEntityState( currentState, session );
final Object naturalId = extractNaturalIdFromEntityState( currentState );
final Object snapshot = loadedState == null
? persistenceContext.getNaturalIdSnapshot( id, persister )
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState, session );
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState );
if ( ! areEqual( naturalId, snapshot, session ) ) {
throw new HibernateException(
@ -89,7 +89,7 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
}
@Override
public Object extractNaturalIdFromEntityState(Object[] state, SharedSessionContractImplementor session) {
public Object extractNaturalIdFromEntityState(Object[] state) {
if ( state == null ) {
return null;
}
@ -102,12 +102,12 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
}
@Override
public Object extractNaturalIdFromEntity(Object entity, SharedSessionContractImplementor session) {
public Object extractNaturalIdFromEntity(Object entity) {
return attribute.getPropertyAccess().getGetter().get( entity );
}
@Override
public void validateInternalForm(Object naturalIdValue, SharedSessionContractImplementor session) {
public void validateInternalForm(Object naturalIdValue) {
if ( naturalIdValue == null ) {
return;
}
@ -135,13 +135,13 @@ public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements
}
@Override
public int calculateHashCode(Object value, SharedSessionContractImplementor session) {
public int calculateHashCode(Object value) {
//noinspection unchecked
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode( value );
}
@Override
public Object normalizeInput(Object incoming, SharedSessionContractImplementor session) {
public Object normalizeInput(Object incoming) {
return normalizeIncomingValue( incoming );
}

View File

@ -74,10 +74,8 @@ import org.hibernate.engine.internal.CacheHelper;
import org.hibernate.engine.internal.ImmutableEntityEntryFactory;
import org.hibernate.engine.internal.MutableEntityEntryFactory;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.CollectionKey;
@ -2708,8 +2706,6 @@ public abstract class AbstractEntityPersister
return true;
}
private static final boolean[] SINGLE_TRUE = new boolean[] { true };
public final boolean checkVersion(final boolean[] includeProperty) {
return includeProperty[getVersionProperty()] || isVersionGeneratedOnExecution();
}
@ -3859,10 +3855,10 @@ public abstract class AbstractEntityPersister
final Object[] entitySnapshot = persistenceContext.getDatabaseSnapshot( id, this );
final Object naturalIdSnapshot = entitySnapshot == StatefulPersistenceContext.NO_ROW
? null
: naturalIdMapping.extractNaturalIdFromEntityState( entitySnapshot, session );
: naturalIdMapping.extractNaturalIdFromEntityState( entitySnapshot );
naturalIdResolutions.removeSharedResolution( id, naturalIdSnapshot, this );
final Object naturalId = naturalIdMapping.extractNaturalIdFromEntity( entity, session );
final Object naturalId = naturalIdMapping.extractNaturalIdFromEntity( entity );
naturalIdResolutions.manageLocalResolution( id, naturalId, this, CachedNaturalIdValueSource.UPDATE );
}
@ -5256,8 +5252,6 @@ public abstract class AbstractEntityPersister
int fetchableIndex,
MappingModelCreationProcess creationProcess) {
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
final JdbcServices jdbcServices = creationContext.getJdbcServices();
final Dialect dialect = creationContext.getDialect();
final String attrName = tupleAttrDefinition.getName();
final Type attrType = tupleAttrDefinition.getType();
@ -5336,7 +5330,7 @@ public abstract class AbstractEntityPersister
assert attrColumnExpression.equals( selectable.getText( creationContext.getDialect() ) );
customReadExpr = selectable.getTemplate(
dialect,
creationContext.getDialect(),
creationContext.getTypeConfiguration(),
creationContext.getFunctionRegistry()
);

View File

@ -31,7 +31,6 @@ import org.hibernate.generator.Generator;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.internal.VersionGeneration;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.TableGroupFilterAliasGenerator;
import org.hibernate.loader.ast.spi.MultiIdLoadOptions;

View File

@ -448,14 +448,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private boolean useEntityInstanceFromExecutionContext(
Object entityInstanceFromExecutionContext,
SharedSessionContractImplementor session) {
if ( this instanceof EntityResultInitializer
&& entityInstanceFromExecutionContext != null
&& entityKey.getIdentifier()
.equals( entityDescriptor.getIdentifier( entityInstanceFromExecutionContext, session ) )
) {
return true;
}
return false;
return this instanceof EntityResultInitializer
&& entityInstanceFromExecutionContext != null
&& entityKey.getIdentifier()
.equals( entityDescriptor.getIdentifier( entityInstanceFromExecutionContext, session ) );
}
private void upgradeLockMode(RowProcessingState rowProcessingState) {
@ -482,18 +478,13 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private boolean isExistingEntityInitialized(Object existingEntity) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entityInstance );
if ( lazyInitializer != null ) {
if ( lazyInitializer.isUninitialized() ) {
return false;
}
return true;
return !lazyInitializer.isUninitialized();
}
else if ( isPersistentAttributeInterceptable( existingEntity ) ) {
final PersistentAttributeInterceptor persistentAttributeInterceptor = asPersistentAttributeInterceptable(
entityInstance ).$$_hibernate_getInterceptor();
if ( persistentAttributeInterceptor == null || persistentAttributeInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
return false;
}
return true;
final PersistentAttributeInterceptor persistentAttributeInterceptor =
asPersistentAttributeInterceptable( entityInstance ).$$_hibernate_getInterceptor();
return persistentAttributeInterceptor != null
&& !( persistentAttributeInterceptor instanceof EnhancementAsProxyLazinessInterceptor );
}
return true;
@ -742,7 +733,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
updateCaches( toInitialize, rowProcessingState, session, persistenceContext, entityIdentifier, version );
registerNaturalIdResolution( session, persistenceContext, entityIdentifier );
registerNaturalIdResolution( persistenceContext, entityIdentifier );
takeSnapshot( rowProcessingState, session, persistenceContext, entityEntry );
@ -778,13 +769,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
}
private void registerNaturalIdResolution(
SharedSessionContractImplementor session,
PersistenceContext persistenceContext,
Object entityIdentifier) {
private void registerNaturalIdResolution(PersistenceContext persistenceContext, Object entityIdentifier) {
if ( entityDescriptor.getNaturalIdMapping() != null ) {
final Object naturalId = entityDescriptor.getNaturalIdMapping()
.extractNaturalIdFromEntityState( resolvedEntityState, session);
final Object naturalId =
entityDescriptor.getNaturalIdMapping().extractNaturalIdFromEntityState( resolvedEntityState );
persistenceContext.getNaturalIdResolutions()
.cacheResolutionFromLoad( entityIdentifier, naturalId, entityDescriptor );
}

View File

@ -11,23 +11,16 @@ import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.hibernate.annotations.NaturalId;
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
import org.hibernate.cache.internal.NaturalIdCacheKey;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.metamodel.RuntimeMetamodels;
import org.hibernate.metamodel.mapping.NaturalIdMapping;
import org.hibernate.metamodel.spi.RuntimeMetamodelsImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.junit.Test;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
@ -53,7 +46,7 @@ public class NaturalIdCacheKeyTest {
when( entityPersister.getRootEntityName() ).thenReturn( "EntityName" );
when( entityPersister.getNaturalIdMapping() ).thenReturn( naturalIdMapping );
when( naturalIdMapping.disassemble( any(), eq( sessionImplementor ) ) ).thenAnswer( invocation -> invocation.getArguments()[0] );
when( naturalIdMapping.calculateHashCode( any(), eq( sessionImplementor ) ) ).thenAnswer( invocation -> invocation.getArguments()[0].hashCode() );
when( naturalIdMapping.calculateHashCode( any() ) ).thenAnswer( invocation -> invocation.getArguments()[0].hashCode() );
final NaturalIdCacheKey key = (NaturalIdCacheKey) DefaultCacheKeysFactory.staticCreateNaturalIdKey( new Object[] {"a", "b", "c"}, entityPersister, sessionImplementor );