HHH-18959 remove unused ResolveNaturalIdEvent stuff

this has all been obsolete since Hibernate 6.0
This commit is contained in:
Gavin King 2024-12-21 11:26:18 +01:00
parent 18c56775da
commit 07ecda306a
10 changed files with 177 additions and 528 deletions

View File

@ -1,119 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.event.internal;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.NaturalIdResolutions;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.ResolveNaturalIdEvent;
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.stat.spi.StatisticsImplementor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.hibernate.pretty.MessageHelper.infoString;
/**
* Defines the default load event listeners used by hibernate for loading entities
* in response to generated load events.
*
* @author Eric Dalquist
* @author Steve Ebersole
*/
public class DefaultResolveNaturalIdEventListener implements ResolveNaturalIdEventListener {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( DefaultResolveNaturalIdEventListener.class );
@Override
public void onResolveNaturalId(ResolveNaturalIdEvent event) throws HibernateException {
event.setEntityId( resolveNaturalId( event ) );
}
/**
* Coordinates the efforts to load a given entity. First, an attempt is
* made to load the entity from the session-level cache. If not found there,
* an attempt is made to locate it in second-level cache. Lastly, an
* attempt is made to load it directly from the datasource.
*
* @param event The load event
*
* @return The loaded entity, or null.
*/
protected Object resolveNaturalId(final ResolveNaturalIdEvent event) {
final EntityPersister persister = event.getEntityPersister();
if ( LOG.isTraceEnabled() ) {
LOG.trace( "Attempting to resolve: " + infoString( persister ) + "#" + event.getNaturalIdValues() );
}
final Object entityId = resolveFromCache( event );
if ( entityId != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.trace( "Resolved object in cache: " + infoString( persister ) + "#" + event.getNaturalIdValues() );
}
return entityId;
}
else {
if ( LOG.isTraceEnabled() ) {
LOG.trace( "Object not resolved in any cache: "+ infoString( persister ) + "#" + event.getNaturalIdValues() );
}
return loadFromDatasource( event );
}
}
/**
* Attempts to resolve the entity id corresponding to the event's natural id values from the session
*
* @param event The load event
*
* @return The entity from the cache, or null.
*/
protected Object resolveFromCache(ResolveNaturalIdEvent event) {
return getNaturalIdResolutions( event)
.findCachedIdByNaturalId( event.getOrderedNaturalIdValues(), event.getEntityPersister() );
}
/**
* Performs the process of loading an entity from the configured
* underlying datasource.
*
* @param event The load event
*
* @return The object loaded from the datasource, or null if not found.
*/
protected Object loadFromDatasource(ResolveNaturalIdEvent event) {
final EventSource session = event.getSession();
final EntityPersister entityPersister = event.getEntityPersister();
final StatisticsImplementor statistics = event.getFactory().getStatistics();
final boolean statisticsEnabled = statistics.isStatisticsEnabled();
final long startTime = statisticsEnabled ? System.nanoTime() : 0;
final Object pk = entityPersister.loadEntityIdByNaturalId(
event.getOrderedNaturalIdValues(),
event.getLockOptions(),
session
);
if ( statisticsEnabled ) {
final long endTime = System.nanoTime();
final long milliseconds = MILLISECONDS.convert( endTime - startTime, NANOSECONDS );
statistics.naturalIdQueryExecuted( entityPersister.getRootEntityName(), milliseconds );
}
//PK can be null if the entity doesn't exist
if ( pk != null ) {
getNaturalIdResolutions( event )
.cacheResolutionFromLoad( pk, event.getOrderedNaturalIdValues(), entityPersister );
}
return pk;
}
private static NaturalIdResolutions getNaturalIdResolutions(ResolveNaturalIdEvent event) {
return event.getSession().getPersistenceContextInternal().getNaturalIdResolutions();
}
}

View File

@ -29,7 +29,6 @@
import org.hibernate.event.internal.DefaultPreLoadEventListener; import org.hibernate.event.internal.DefaultPreLoadEventListener;
import org.hibernate.event.internal.DefaultRefreshEventListener; import org.hibernate.event.internal.DefaultRefreshEventListener;
import org.hibernate.event.internal.DefaultReplicateEventListener; import org.hibernate.event.internal.DefaultReplicateEventListener;
import org.hibernate.event.internal.DefaultResolveNaturalIdEventListener;
import org.hibernate.event.internal.PostDeleteEventListenerStandardImpl; import org.hibernate.event.internal.PostDeleteEventListenerStandardImpl;
import org.hibernate.event.internal.PostInsertEventListenerStandardImpl; import org.hibernate.event.internal.PostInsertEventListenerStandardImpl;
import org.hibernate.event.internal.PostUpdateEventListenerStandardImpl; import org.hibernate.event.internal.PostUpdateEventListenerStandardImpl;
@ -75,7 +74,6 @@
import static org.hibernate.event.spi.EventType.PRE_UPSERT; import static org.hibernate.event.spi.EventType.PRE_UPSERT;
import static org.hibernate.event.spi.EventType.REFRESH; import static org.hibernate.event.spi.EventType.REFRESH;
import static org.hibernate.event.spi.EventType.REPLICATE; import static org.hibernate.event.spi.EventType.REPLICATE;
import static org.hibernate.event.spi.EventType.RESOLVE_NATURAL_ID;
/** /**
* Standard implementation of EventListenerRegistry * Standard implementation of EventListenerRegistry
@ -240,9 +238,6 @@ private void applyStandardListeners() {
// load listeners // load listeners
prepareListeners( LOAD, new DefaultLoadEventListener() ); prepareListeners( LOAD, new DefaultLoadEventListener() );
// resolve natural-id listeners
prepareListeners( RESOLVE_NATURAL_ID, new DefaultResolveNaturalIdEventListener() );
// load-collection listeners // load-collection listeners
prepareListeners( INIT_COLLECTION, new DefaultInitializeCollectionEventListener() ); prepareListeners( INIT_COLLECTION, new DefaultInitializeCollectionEventListener() );

View File

@ -25,7 +25,6 @@ public final class EventType<T> {
private static final AtomicInteger STANDARD_TYPE_COUNTER = new AtomicInteger(); private static final AtomicInteger STANDARD_TYPE_COUNTER = new AtomicInteger();
public static final EventType<LoadEventListener> LOAD = create( "load", LoadEventListener.class ); public static final EventType<LoadEventListener> LOAD = create( "load", LoadEventListener.class );
public static final EventType<ResolveNaturalIdEventListener> RESOLVE_NATURAL_ID = create( "resolve-natural-id", ResolveNaturalIdEventListener.class );
public static final EventType<InitializeCollectionEventListener> INIT_COLLECTION = create( "load-collection", InitializeCollectionEventListener.class ); public static final EventType<InitializeCollectionEventListener> INIT_COLLECTION = create( "load-collection", InitializeCollectionEventListener.class );

View File

@ -1,117 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.event.spi;
import java.util.Collections;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.persister.entity.EntityPersister;
/**
* Defines an event class for the resolving of an entity id from the entity's natural-id
*
* @author Eric Dalquist
* @author Steve Ebersole
*/
public class ResolveNaturalIdEvent extends AbstractEvent {
public static final LockMode DEFAULT_LOCK_MODE = LockMode.NONE;
private final EntityPersister entityPersister;
private final Map<String, Object> naturalIdValues;
private final Object[] orderedNaturalIdValues;
private final LockOptions lockOptions;
private Object entityId;
public ResolveNaturalIdEvent(Map<String, Object> naturalIdValues, EntityPersister entityPersister, EventSource source) {
this( naturalIdValues, entityPersister, LockOptions.NONE, source );
}
public ResolveNaturalIdEvent(
Map<String, Object> naturalIdValues,
EntityPersister entityPersister,
LockOptions lockOptions,
EventSource source) {
super( source );
if ( entityPersister == null ) {
throw new IllegalArgumentException( "EntityPersister is required for loading" );
}
if ( ! entityPersister.hasNaturalIdentifier() ) {
throw new HibernateException( "Entity did not define a natural-id" );
}
if ( naturalIdValues == null || naturalIdValues.isEmpty() ) {
throw new IllegalArgumentException( "natural-id to load is required" );
}
if ( entityPersister.getNaturalIdentifierProperties().length != naturalIdValues.size() ) {
throw new HibernateException(
String.format(
"Entity [%s] defines its natural-id with %d properties but only %d were specified",
entityPersister.getEntityName(),
entityPersister.getNaturalIdentifierProperties().length,
naturalIdValues.size()
)
);
}
if ( lockOptions.getLockMode() == LockMode.WRITE ) {
throw new IllegalArgumentException( "Invalid lock mode for loading" );
}
else if ( lockOptions.getLockMode() == null ) {
lockOptions.setLockMode( DEFAULT_LOCK_MODE );
}
this.entityPersister = entityPersister;
this.naturalIdValues = naturalIdValues;
this.lockOptions = lockOptions;
int[] naturalIdPropertyPositions = entityPersister.getNaturalIdentifierProperties();
orderedNaturalIdValues = new Object[naturalIdPropertyPositions.length];
int i = 0;
for ( int position : naturalIdPropertyPositions ) {
final String propertyName = entityPersister.getPropertyNames()[position];
if ( ! naturalIdValues.containsKey( propertyName ) ) {
throw new HibernateException(
String.format( "No value specified for natural-id property %s#%s", getEntityName(), propertyName )
);
}
orderedNaturalIdValues[i++] = naturalIdValues.get( entityPersister.getPropertyNames()[position] );
}
}
public Map<String, Object> getNaturalIdValues() {
return Collections.unmodifiableMap( naturalIdValues );
}
public Object[] getOrderedNaturalIdValues() {
return orderedNaturalIdValues;
}
public EntityPersister getEntityPersister() {
return entityPersister;
}
public String getEntityName() {
return getEntityPersister().getEntityName();
}
public LockOptions getLockOptions() {
return lockOptions;
}
public Object getEntityId() {
return entityId;
}
public void setEntityId(Object entityId) {
this.entityId = entityId;
}
}

View File

@ -1,26 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.event.spi;
import org.hibernate.HibernateException;
/**
* Defines the contract for handling of resolve natural id events generated from a session.
*
* @author Eric Dalquist
* @author Steve Ebersole
*/
public interface ResolveNaturalIdEventListener {
/**
* Handle the given resolve natural id event.
*
* @param event The resolve natural id event to be handled.
*
* @throws HibernateException Indicates a problem resolving natural id to primary key
*/
void onResolveNaturalId(ResolveNaturalIdEvent event) throws HibernateException;
}

View File

@ -117,7 +117,6 @@ public final class FastSessionServices {
public final EventListenerGroup<PreUpsertEventListener> eventListenerGroup_PRE_UPSERT; public final EventListenerGroup<PreUpsertEventListener> eventListenerGroup_PRE_UPSERT;
public final EventListenerGroup<RefreshEventListener> eventListenerGroup_REFRESH; public final EventListenerGroup<RefreshEventListener> eventListenerGroup_REFRESH;
public final EventListenerGroup<ReplicateEventListener> eventListenerGroup_REPLICATE; public final EventListenerGroup<ReplicateEventListener> eventListenerGroup_REPLICATE;
public final EventListenerGroup<ResolveNaturalIdEventListener> eventListenerGroup_RESOLVE_NATURAL_ID;
// Fields used only from within this package // Fields used only from within this package
final boolean disallowOutOfTransactionUpdateOperations; final boolean disallowOutOfTransactionUpdateOperations;
@ -196,7 +195,6 @@ public final class FastSessionServices {
this.eventListenerGroup_PRE_UPSERT = listeners( eventListenerRegistry, EventType.PRE_UPSERT ); this.eventListenerGroup_PRE_UPSERT = listeners( eventListenerRegistry, EventType.PRE_UPSERT );
this.eventListenerGroup_REFRESH = listeners( eventListenerRegistry, EventType.REFRESH ); this.eventListenerGroup_REFRESH = listeners( eventListenerRegistry, EventType.REFRESH );
this.eventListenerGroup_REPLICATE = listeners( eventListenerRegistry, EventType.REPLICATE ); this.eventListenerGroup_REPLICATE = listeners( eventListenerRegistry, EventType.REPLICATE );
this.eventListenerGroup_RESOLVE_NATURAL_ID = listeners( eventListenerRegistry, EventType.RESOLVE_NATURAL_ID );
//Other highly useful constants: //Other highly useful constants:
this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); this.dialect = jdbcServices.getJdbcEnvironment().getDialect();

View File

@ -104,8 +104,6 @@
import org.hibernate.event.spi.RefreshEventListener; import org.hibernate.event.spi.RefreshEventListener;
import org.hibernate.event.spi.ReplicateEvent; import org.hibernate.event.spi.ReplicateEvent;
import org.hibernate.event.spi.ReplicateEventListener; import org.hibernate.event.spi.ReplicateEventListener;
import org.hibernate.event.spi.ResolveNaturalIdEvent;
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.loader.internal.CacheLoadHelper; import org.hibernate.loader.internal.CacheLoadHelper;
import org.hibernate.resource.transaction.spi.TransactionObserver; import org.hibernate.resource.transaction.spi.TransactionObserver;
import org.hibernate.event.monitor.spi.EventMonitor; import org.hibernate.event.monitor.spi.EventMonitor;
@ -1286,14 +1284,6 @@ private void fireLoadNoChecks(final LoadEvent event, final LoadType loadType) {
.fireEventOnEachListener( event, loadType, LoadEventListener::onLoad ); .fireEventOnEachListener( event, loadType, LoadEventListener::onLoad );
} }
private void fireResolveNaturalId(final ResolveNaturalIdEvent event) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_RESOLVE_NATURAL_ID
.fireEventOnEachListener( event, ResolveNaturalIdEventListener::onResolveNaturalId );
delayedAfterCompletion();
}
// refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -74,34 +74,29 @@ public T load(Object naturalIdValue) {
return doLoad( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue) ); return doLoad( entityPersister().getNaturalIdMapping().normalizeInput( naturalIdValue) );
} }
/**
* Verify that the given natural id is "simple".
* <p>
* We allow compound natural id "simple" loading if all the values are passed as an array,
* list, or map. We assume an array is properly ordered following the attribute ordering.
* For lists, just like arrays, we assume the user has ordered them properly; for maps,
* the key is expected to be the attribute name.
*/
private void verifySimplicity(Object naturalIdValue) { private void verifySimplicity(Object naturalIdValue) {
assert naturalIdValue != null; assert naturalIdValue != null;
if ( !hasSimpleNaturalId
if ( hasSimpleNaturalId ) { && !naturalIdValue.getClass().isArray()
// implicitly && !(naturalIdValue instanceof List)
return; && !(naturalIdValue instanceof Map) ) {
throw new HibernateException(
String.format(
Locale.ROOT,
"Cannot interpret natural-id value [%s] for compound natural-id: %s",
naturalIdValue,
entityPersister().getEntityName()
)
);
} }
if ( naturalIdValue.getClass().isArray() ) {
// we allow compound natural-id "simple" loading all the values are passed as an array
// (we assume the array is properly ordered following the mapping-model attribute ordering)
return;
}
if ( naturalIdValue instanceof List || naturalIdValue instanceof Map ) {
// also allowed. For Lists, just like arrays, we assume the user has ordered them properly;
// for Maps, the key is expected to be the attribute name
return;
}
throw new HibernateException(
String.format(
Locale.ROOT,
"Cannot interpret natural-id value [%s] for compound natural-id: %s",
naturalIdValue,
entityPersister().getEntityName()
)
);
} }
@Override @Override

View File

@ -13,7 +13,6 @@
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.loader.ast.internal.CompoundNaturalIdLoader; import org.hibernate.loader.ast.internal.CompoundNaturalIdLoader;
@ -22,7 +21,6 @@
import org.hibernate.loader.ast.spi.NaturalIdLoader; import org.hibernate.loader.ast.spi.NaturalIdLoader;
import org.hibernate.metamodel.UnsupportedMappingException; import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
@ -89,7 +87,6 @@ public CompoundNaturalIdMapping(
) )
); );
this.jdbcMappings = jdbcMappings; this.jdbcMappings = jdbcMappings;
return true; return true;
} }
); );
@ -97,13 +94,10 @@ public CompoundNaturalIdMapping(
private static boolean isMutable(List<SingularAttributeMapping> attributes) { private static boolean isMutable(List<SingularAttributeMapping> attributes) {
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
final SingularAttributeMapping attributeMapping = attributes.get( i ); if ( attributes.get( i ).getAttributeMetadata().isUpdatable() ) {
final AttributeMetadata metadata = attributeMapping.getAttributeMetadata();
if ( metadata.isUpdatable() ) {
return true; return true;
} }
} }
return false; return false;
} }
@ -112,41 +106,34 @@ public Object[] extractNaturalIdFromEntityState(Object[] state) {
if ( state == null ) { if ( state == null ) {
return null; return null;
} }
else if ( state.length == attributes.size() ) {
if ( state.length == attributes.size() ) {
return state; return state;
} }
else {
final Object[] values = new Object[ attributes.size() ]; final Object[] values = new Object[attributes.size()];
for ( int i = 0; i <= attributes.size() - 1; i++ ) {
for ( int i = 0; i <= attributes.size() - 1; i++ ) { final SingularAttributeMapping attributeMapping = attributes.get( i );
final SingularAttributeMapping attributeMapping = attributes.get( i ); values[i] = state[attributeMapping.getStateArrayPosition()];
values[ i ] = state[ attributeMapping.getStateArrayPosition() ]; }
return values;
} }
return values;
} }
@Override @Override
public Object[] extractNaturalIdFromEntity(Object entity) { public Object[] extractNaturalIdFromEntity(Object entity) {
final Object[] values = new Object[ attributes.size() ]; final Object[] values = new Object[ attributes.size() ];
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
values[i] = attributes.get( i ).getPropertyAccess().getGetter().get( entity ); values[i] = attributes.get( i ).getPropertyAccess().getGetter().get( entity );
} }
return values; return values;
} }
@Override @Override
@SuppressWarnings( "rawtypes" )
public Object[] normalizeInput(Object incoming) { public Object[] normalizeInput(Object incoming) {
if ( incoming instanceof Object[] ) { if ( incoming instanceof Object[] array ) {
return (Object[]) incoming; return array;
} }
else if ( incoming instanceof Map<?,?> valueMap ) {
if ( incoming instanceof Map ) {
final Map valueMap = (Map) incoming;
final List<SingularAttributeMapping> attributes = getNaturalIdAttributes(); final List<SingularAttributeMapping> attributes = getNaturalIdAttributes();
final Object[] values = new Object[ attributes.size() ]; final Object[] values = new Object[ attributes.size() ];
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
@ -154,30 +141,28 @@ public Object[] normalizeInput(Object incoming) {
} }
return values; return values;
} }
else {
throw new UnsupportedMappingException( "Do not know how to normalize compound natural-id value : " + incoming ); throw new UnsupportedMappingException( "Could not normalize compound natural-id value: " + incoming );
}
} }
@Override @Override
public void validateInternalForm(Object naturalIdValue) { public void validateInternalForm(Object naturalIdValue) {
if ( naturalIdValue == null ) { if ( naturalIdValue != null ) {
return; // should be an array, with a size equal to the number of attributes making up this compound natural-id
} if ( naturalIdValue instanceof Object[] values ) {
if ( values.length != attributes.size() ) {
// should be an array, with a size equal to the number of attributes making up this compound natural-id throw new IllegalArgumentException(
if ( naturalIdValue instanceof Object[] ) { "Natural-id value [" + naturalIdValue + "] did not contain the expected number of elements ["
final Object[] values = (Object[]) naturalIdValue; + attributes.size() + "]"
if ( values.length != attributes.size() ) { );
throw new IllegalArgumentException( }
"Natural-id value [" + naturalIdValue + "] did not contain the expected number of elements [" }
+ attributes.size() + "]" else {
); throw new IllegalArgumentException(
"Natural-id value [" + naturalIdValue + "] was not an array as expected" );
} }
return;
} }
throw new IllegalArgumentException( "Natural-id value [" + naturalIdValue + "] was not an array as expected" );
} }
@Override @Override
@ -185,71 +170,65 @@ public int calculateHashCode(Object value) {
if ( value == null ) { if ( value == null ) {
return 0; return 0;
} }
Object[] values = (Object[]) value; else {
int hashcode = 0; final Object[] values = (Object[]) value;
for ( int i = 0; i < attributes.size(); i++ ) { int hashcode = 0;
final Object o = values[i]; for ( int i = 0; i < attributes.size(); i++ ) {
if ( o != null ) { final Object o = values[i];
hashcode = 27 * hashcode + ( (JavaType) attributes.get( i ).getExpressibleJavaType() ).extractHashCode( o ); if ( o != null ) {
hashcode = 27 * hashcode
+ ((JavaType) attributes.get( i ).getExpressibleJavaType()).extractHashCode( o );
}
} }
return hashcode;
} }
return hashcode;
} }
@Override @Override
public void verifyFlushState(Object id, Object[] currentState, Object[] loadedState, SharedSessionContractImplementor session) { public void verifyFlushState(Object id, Object[] currentState, Object[] loadedState, SharedSessionContractImplementor session) {
if ( isMutable() ) { if ( !isMutable() ) {
// EARLY EXIT!!! final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
// the natural id is mutable (!immutable), no need to do the checks final EntityPersister persister = getDeclaringType().getEntityPersister();
return;
}
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final Object[] naturalId = extractNaturalIdFromEntityState( currentState );
final EntityPersister persister = getDeclaringType().getEntityPersister(); final Object snapshot = loadedState == null
? persistenceContext.getNaturalIdSnapshot( id, persister )
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState );
final Object[] previousNaturalId = (Object[]) snapshot;
assert naturalId.length == getNaturalIdAttributes().size();
assert previousNaturalId.length == naturalId.length;
final Object[] naturalId = extractNaturalIdFromEntityState( currentState ); for ( int i = 0; i < getNaturalIdAttributes().size(); i++ ) {
final SingularAttributeMapping attributeMapping = getNaturalIdAttributes().get( i );
final Object snapshot = loadedState == null final boolean updatable = attributeMapping.getAttributeMetadata().isUpdatable();
? persistenceContext.getNaturalIdSnapshot( id, persister ) if ( !updatable ) {
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState ); final Object currentValue = naturalId[i];
final Object[] previousNaturalId = (Object[]) snapshot; final Object previousValue = previousNaturalId[i];
if ( !attributeMapping.areEqual( currentValue, previousValue, session ) ) {
assert naturalId.length == getNaturalIdAttributes().size(); throw new HibernateException(
assert previousNaturalId.length == naturalId.length; String.format(
"An immutable attribute [%s] within compound natural identifier of entity %s was altered from `%s` to `%s`",
for ( int i = 0; i < getNaturalIdAttributes().size(); i++ ) { attributeMapping.getAttributeName(),
final SingularAttributeMapping attributeMapping = getNaturalIdAttributes().get( i ); persister.getEntityName(),
previousValue,
final boolean updatable = attributeMapping.getAttributeMetadata().isUpdatable(); currentValue
if ( updatable ) { )
// property is updatable (mutable), there is nothing to check );
continue; }
} }
// else property is updatable (mutable), there is nothing to check
final Object currentValue = naturalId[ i ];
final Object previousValue = previousNaturalId[ i ];
if ( ! attributeMapping.areEqual( currentValue, previousValue, session ) ) {
throw new HibernateException(
String.format(
"An immutable attribute [%s] within compound natural identifier of entity %s was altered from `%s` to `%s`",
attributeMapping.getAttributeName(),
persister.getEntityName(),
previousValue,
currentValue
)
);
} }
} }
// otherwise the natural id is mutable (!immutable), no need to do the checks
} }
@Override @Override
public boolean areEqual(Object one, Object other, SharedSessionContractImplementor session) { public boolean areEqual(Object one, Object other, SharedSessionContractImplementor session) {
final Object[] one1 = (Object[]) one; final Object[] oneArray = (Object[]) one;
final Object[] other1 = (Object[]) other; final Object[] otherArray = (Object[]) other;
final List<SingularAttributeMapping> naturalIdAttributes = getNaturalIdAttributes(); final List<SingularAttributeMapping> naturalIdAttributes = getNaturalIdAttributes();
for ( int i = 0; i < naturalIdAttributes.size(); i++ ) { for ( int i = 0; i < naturalIdAttributes.size(); i++ ) {
if ( !naturalIdAttributes.get( i ).areEqual( one1[i], other1[i], session ) ) { if ( !naturalIdAttributes.get( i ).areEqual( oneArray[i], otherArray[i], session ) ) {
return false; return false;
} }
} }
@ -305,18 +284,14 @@ public boolean hasPartitionedSelectionMapping() {
public <T> DomainResult<T> createDomainResult(NavigablePath navigablePath, TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { public <T> DomainResult<T> createDomainResult(NavigablePath navigablePath, TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) {
assert navigablePath.getLocalName().equals( NaturalIdMapping.PART_NAME ); assert navigablePath.getLocalName().equals( NaturalIdMapping.PART_NAME );
final SessionFactoryImplementor sessionFactory = creationState.getSqlAstCreationState().getCreationContext().getSessionFactory(); final JavaType<Object[]> jtd =
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
final JavaType<Object[]> jtd = sessionFactory .getTypeConfiguration().getJavaTypeRegistry()
.getTypeConfiguration() .getDescriptor( Object[].class );
.getJavaTypeRegistry()
.getDescriptor( Object[].class );
// register the table group under `...{natural-id}` as well // register the table group under `...{natural-id}` as well
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( creationState.getSqlAstCreationState().getFromClauseAccess()
navigablePath, .resolveTableGroup( navigablePath, np -> tableGroup );
(np) -> tableGroup
);
return (DomainResult<T>) new DomainResultImpl( return (DomainResult<T>) new DomainResultImpl(
navigablePath, navigablePath,
@ -352,15 +327,14 @@ public <X, Y> int breakDownJdbcValues(
int span = 0; int span = 0;
if ( domainValue == null ) { if ( domainValue == null ) {
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
span += attributes.get( i ).breakDownJdbcValues( null, offset + span, x, y, valueConsumer, session ); span += attributes.get( i )
.breakDownJdbcValues( null, offset + span, x, y, valueConsumer, session );
} }
} }
else { else {
assert domainValue instanceof Object[]; assert domainValue instanceof Object[];
final Object[] values = (Object[]) domainValue; final Object[] values = (Object[]) domainValue;
assert values.length == attributes.size(); assert values.length == attributes.size();
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
span += attributes.get( i ).breakDownJdbcValues( span += attributes.get( i ).breakDownJdbcValues(
values[i], values[i],
@ -412,19 +386,16 @@ public Object disassemble(Object value, SharedSessionContractImplementor session
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
assert value instanceof Object[]; else {
assert value instanceof Object[];
final Object[] incoming = (Object[]) value; final Object[] incoming = (Object[]) value;
assert incoming.length == attributes.size(); assert incoming.length == attributes.size();
final Object[] outgoing = new Object[incoming.length];
final Object[] outgoing = new Object[ incoming.length ]; for ( int i = 0; i < attributes.size(); i++ ) {
outgoing[i] = attributes.get( i ).disassemble( incoming[i], session );
for ( int i = 0; i < attributes.size(); i++ ) { }
final SingularAttributeMapping attribute = attributes.get( i ); return outgoing;
outgoing[ i ] = attribute.disassemble( incoming[ i ], session );
} }
return outgoing;
} }
@Override @Override
@ -436,10 +407,8 @@ public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedS
} }
else { else {
assert value instanceof Object[]; assert value instanceof Object[];
final Object[] values = (Object[]) value; final Object[] values = (Object[]) value;
assert values.length == attributes.size(); assert values.length == attributes.size();
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
attributes.get( i ).addToCacheKey( cacheKey, values[i], session ); attributes.get( i ).addToCacheKey( cacheKey, values[i], session );
} }
@ -505,10 +474,8 @@ public <X, Y> int forEachJdbcValue(
} }
else { else {
assert value instanceof Object[]; assert value instanceof Object[];
final Object[] incoming = (Object[]) value; final Object[] incoming = (Object[]) value;
assert incoming.length == attributes.size(); assert incoming.length == attributes.size();
for ( int i = 0; i < attributes.size(); i++ ) { for ( int i = 0; i < attributes.size(); i++ ) {
final SingularAttributeMapping attribute = attributes.get( i ); final SingularAttributeMapping attribute = attributes.get( i );
span += attribute.forEachJdbcValue( incoming[i], span + offset, x, y, valuesConsumer, session ); span += attribute.forEachJdbcValue( incoming[i], span + offset, x, y, valuesConsumer, session );
@ -575,7 +542,6 @@ public DomainResultImpl(
this.naturalIdMapping = naturalIdMapping; this.naturalIdMapping = naturalIdMapping;
this.arrayJtd = arrayJtd; this.arrayJtd = arrayJtd;
this.resultVariable = resultVariable; this.resultVariable = resultVariable;
this.fetches = creationState.visitFetches( this ); this.fetches = creationState.visitFetches( this );
this.hasJoinFetches = this.fetches.hasJoinFetches(); this.hasJoinFetches = this.fetches.hasJoinFetches();
this.containsCollectionFetches = this.fetches.containsCollectionFetches(); this.containsCollectionFetches = this.fetches.containsCollectionFetches();
@ -593,13 +559,7 @@ public String getResultVariable() {
public DomainResultAssembler<Object[]> createResultAssembler( public DomainResultAssembler<Object[]> createResultAssembler(
InitializerParent<?> parent, InitializerParent<?> parent,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
return new AssemblerImpl( return new AssemblerImpl( fetches, arrayJtd, creationState );
fetches,
navigablePath,
naturalIdMapping,
arrayJtd,
creationState
);
} }
@Override @Override
@ -655,15 +615,9 @@ public boolean containsCollectionFetches() {
private static class AssemblerImpl implements DomainResultAssembler<Object[]> { private static class AssemblerImpl implements DomainResultAssembler<Object[]> {
private final JavaType<Object[]> jtd; private final JavaType<Object[]> jtd;
private final DomainResultAssembler<?>[] subAssemblers; private final DomainResultAssembler<?>[] subAssemblers;
private AssemblerImpl( private AssemblerImpl(ImmutableFetchList fetches, JavaType<Object[]> jtd, AssemblerCreationState creationState) {
ImmutableFetchList fetches,
NavigablePath navigablePath,
CompoundNaturalIdMapping naturalIdMapping,
JavaType<Object[]> jtd,
AssemblerCreationState creationState) {
this.jtd = jtd; this.jtd = jtd;
this.subAssemblers = new DomainResultAssembler[fetches.size()]; this.subAssemblers = new DomainResultAssembler[fetches.size()];
int i = 0; int i = 0;
@ -672,14 +626,8 @@ private AssemblerImpl(
} }
} }
private AssemblerImpl(JavaType<Object[]> jtd, DomainResultAssembler<?>[] subAssemblers) {
this.jtd = jtd;
this.subAssemblers = subAssemblers;
}
@Override @Override
public Object[] assemble( public Object[] assemble(RowProcessingState rowProcessingState) {
RowProcessingState rowProcessingState) {
final Object[] result = new Object[ subAssemblers.length ]; final Object[] result = new Object[ subAssemblers.length ];
for ( int i = 0; i < subAssemblers.length; i++ ) { for ( int i = 0; i < subAssemblers.length; i++ ) {
result[ i ] = subAssemblers[i].assemble( rowProcessingState ); result[ i ] = subAssemblers[i].assemble( rowProcessingState );
@ -710,7 +658,6 @@ public <X> void forEachResultAssembler(BiConsumer<Initializer<?>, X> consumer, X
public JavaType<Object[]> getAssembledJavaType() { public JavaType<Object[]> getAssembledJavaType() {
return jtd; return jtd;
} }
} }
} }

View File

@ -12,7 +12,7 @@
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.cache.MutableCacheKeyBuilder; import org.hibernate.cache.MutableCacheKeyBuilder;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
@ -42,8 +42,8 @@
/** /**
* Single-attribute NaturalIdMapping implementation * Single-attribute NaturalIdMapping implementation
*/ */
public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping implements JavaType.CoercionContext, public class SimpleNaturalIdMapping extends AbstractNaturalIdMapping
BasicValuedMapping { implements JavaType.CoercionContext, BasicValuedMapping {
private final SingularAttributeMapping attribute; private final SingularAttributeMapping attribute;
private final SessionFactoryImplementor sessionFactory; private final SessionFactoryImplementor sessionFactory;
private final TypeConfiguration typeConfiguration; private final TypeConfiguration typeConfiguration;
@ -52,15 +52,10 @@ public SimpleNaturalIdMapping(
SingularAttributeMapping attribute, SingularAttributeMapping attribute,
EntityMappingType declaringType, EntityMappingType declaringType,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
super( super( declaringType, attribute.getAttributeMetadata().isUpdatable() );
declaringType,
attribute.getAttributeMetadata().isUpdatable()
);
this.attribute = attribute; this.attribute = attribute;
this.sessionFactory = creationProcess.getCreationContext().getSessionFactory(); this.sessionFactory = creationProcess.getCreationContext().getSessionFactory();
this.typeConfiguration = creationProcess.getCreationContext().getTypeConfiguration(); this.typeConfiguration = creationProcess.getCreationContext().getTypeConfiguration();
} }
public SingularAttributeMapping getAttribute() { public SingularAttributeMapping getAttribute() {
@ -73,30 +68,24 @@ public void verifyFlushState(
Object[] currentState, Object[] currentState,
Object[] loadedState, Object[] loadedState,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
if ( isMutable() ) { if ( !isMutable() ) {
// EARLY EXIT!!! final EntityPersister persister = getDeclaringType().getEntityPersister();
// the natural id is mutable (!immutable), no need to do the checks final Object naturalId = extractNaturalIdFromEntityState( currentState );
return; final Object snapshot = loadedState == null
} ? session.getPersistenceContextInternal().getNaturalIdSnapshot( id, persister )
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState );
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); if ( !areEqual( naturalId, snapshot, session ) ) {
final EntityPersister persister = getDeclaringType().getEntityPersister(); throw new HibernateException(
String.format(
final Object naturalId = extractNaturalIdFromEntityState( currentState ); "An immutable natural identifier of entity %s was altered from `%s` to `%s`",
final Object snapshot = loadedState == null persister.getEntityName(),
? persistenceContext.getNaturalIdSnapshot( id, persister ) snapshot,
: persister.getNaturalIdMapping().extractNaturalIdFromEntityState( loadedState ); naturalId
)
if ( !areEqual( naturalId, snapshot, session ) ) { );
throw new HibernateException( }
String.format(
"An immutable natural identifier of entity %s was altered from `%s` to `%s`",
persister.getEntityName(),
snapshot,
naturalId
)
);
} }
// otherwise, the natural id is mutable (!immutable), no need to do the checks
} }
@Override @Override
@ -104,12 +93,12 @@ public Object extractNaturalIdFromEntityState(Object[] state) {
if ( state == null ) { if ( state == null ) {
return null; return null;
} }
else if ( state.length == 1 ) {
if ( state.length == 1 ) {
return state[0]; return state[0];
} }
else {
return state[attribute.getStateArrayPosition()]; return state[attribute.getStateArrayPosition()];
}
} }
@Override @Override
@ -119,65 +108,61 @@ public Object extractNaturalIdFromEntity(Object entity) {
@Override @Override
public void validateInternalForm(Object naturalIdValue) { public void validateInternalForm(Object naturalIdValue) {
if ( naturalIdValue == null ) { if ( naturalIdValue != null ) {
return; final Class<?> naturalIdValueClass = naturalIdValue.getClass();
} if ( naturalIdValueClass.isArray() && !naturalIdValueClass.getComponentType().isPrimitive() ) {
// be flexible
final Class<?> naturalIdValueClass = naturalIdValue.getClass(); final Object[] values = (Object[]) naturalIdValue;
if ( naturalIdValueClass.isArray() && !naturalIdValueClass.getComponentType().isPrimitive() ) { if ( values.length == 1 ) {
// be flexible naturalIdValue = values[0];
final Object[] values = (Object[]) naturalIdValue; }
if ( values.length == 1 ) {
naturalIdValue = values[0];
} }
}
if ( !getJavaType().getJavaTypeClass().isInstance( naturalIdValue ) ) { if ( !getJavaType().getJavaTypeClass().isInstance( naturalIdValue ) ) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,
"Incoming natural-id value [%s (`%s`)] is not of expected type [`%s`] and could not be coerced", "Incoming natural-id value [%s (`%s`)] is not of expected type [`%s`] and could not be coerced",
naturalIdValue, naturalIdValue,
naturalIdValueClass.getName(), naturalIdValueClass.getName(),
getJavaType().getTypeName() getJavaType().getTypeName()
) )
); );
}
} }
} }
@Override @Override
public int calculateHashCode(Object value) { public int calculateHashCode(Object value) {
//noinspection unchecked //noinspection rawtypes,unchecked
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode( value ); return value == null ? 0 : ( (JavaType) getJavaType() ).extractHashCode( value );
} }
@Override @Override
public Object normalizeInput(Object incoming) { public Object normalizeInput(Object incoming) {
return normalizeIncomingValue( incoming ); final Object normalizedValue = normalizedValue( incoming );
return isLoadByIdComplianceEnabled()
? normalizedValue
: getJavaType().coerce( normalizedValue, this );
} }
@SuppressWarnings("rawtypes") private Object normalizedValue(Object incoming) {
public Object normalizeIncomingValue(Object naturalIdToLoad) { if ( incoming instanceof Map<?,?> valueMap ) {
final Object normalizedValue;
if ( naturalIdToLoad instanceof Map ) {
final Map valueMap = (Map) naturalIdToLoad;
assert valueMap.size() == 1; assert valueMap.size() == 1;
assert valueMap.containsKey( getAttribute().getAttributeName() ); assert valueMap.containsKey( getAttribute().getAttributeName() );
normalizedValue = valueMap.get( getAttribute().getAttributeName() ); return valueMap.get( getAttribute().getAttributeName() );
} }
else if ( naturalIdToLoad instanceof Object[] ) { else if ( incoming instanceof Object[] values ) {
final Object[] values = (Object[]) naturalIdToLoad;
assert values.length == 1; assert values.length == 1;
normalizedValue = values[0]; return values[0];
} }
else { else {
normalizedValue = naturalIdToLoad; return incoming;
} }
}
if ( getTypeConfiguration().getJpaCompliance().isLoadByIdComplianceEnabled() ) { private boolean isLoadByIdComplianceEnabled() {
return normalizedValue; return getTypeConfiguration().getJpaCompliance().isLoadByIdComplianceEnabled();
}
return getJavaType().coerce( normalizedValue, this );
} }
@Override @Override
@ -301,11 +286,13 @@ public NaturalIdLoader<?> makeLoader(EntityMappingType entityDescriptor) {
@Override @Override
public MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor) { public MultiNaturalIdLoader<?> makeMultiLoader(EntityMappingType entityDescriptor) {
boolean supportsSqlArrayType = supportsSqlArrayType( sessionFactory.getFastSessionServices().jdbcServices.getDialect() ); return supportsSqlArrayType( getDialect() ) && attribute instanceof BasicAttributeMapping
if ( supportsSqlArrayType && attribute instanceof BasicAttributeMapping ) { ? new MultiNaturalIdLoaderArrayParam<>( entityDescriptor )
return new MultiNaturalIdLoaderArrayParam<>( entityDescriptor ); : new MultiNaturalIdLoaderInPredicate<>( entityDescriptor );
} }
return new MultiNaturalIdLoaderInPredicate<>( entityDescriptor );
private Dialect getDialect() {
return sessionFactory.getFastSessionServices().jdbcServices.getDialect();
} }
@Override @Override