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:
parent
6d15c1d115
commit
5d86d88c03
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue