diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/PersistenceContexts.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/PersistenceContexts.java new file mode 100644 index 000000000000..89be03c58897 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/PersistenceContexts.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.engine.internal; + +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.SessionImpl; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Operations to instantiate and (de)serialize {@link StatefulPersistenceContext} + * without exposing the class outside this package. + * + * @author Gavin King + */ +public class PersistenceContexts { + public static PersistenceContext createPersistenceContext(SharedSessionContractImplementor session) { + return new StatefulPersistenceContext( session ); + } + + public static PersistenceContext deserialize(ObjectInputStream ois, SessionImpl session) + throws IOException, ClassNotFoundException { + return StatefulPersistenceContext.deserialize( ois, session ); + } + + public static void serialize(PersistenceContext persistenceContext, ObjectOutputStream oos) + throws IOException { + ( (StatefulPersistenceContext) persistenceContext ).serialize( oos ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index bcde88fe6b5f..d9ae2fec01d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -89,7 +89,7 @@ * @author Steve Ebersole * @author Sanne Grinovero */ -public class StatefulPersistenceContext implements PersistenceContext { +class StatefulPersistenceContext implements PersistenceContext { private static final CoreMessageLogger LOG = Logger.getMessageLogger( MethodHandles.lookup(), CoreMessageLogger.class, @@ -173,7 +173,7 @@ the following fields are used in all circumstances, and are not worth (or not su * * @param session The session "owning" this context. */ - public StatefulPersistenceContext(SharedSessionContractImplementor session) { + StatefulPersistenceContext(SharedSessionContractImplementor session) { this.session = session; this.entityEntryContext = new EntityEntryContext( this ); } @@ -430,7 +430,7 @@ public EntityHolder claimEntityHolderIfPossible( } @Override - public @Nullable EntityHolderImpl getEntityHolder(EntityKey key) { + public @Nullable EntityHolder getEntityHolder(EntityKey key) { return entitiesByKey == null ? null : entitiesByKey.get( key ); } @@ -535,7 +535,7 @@ public boolean containsEntity(EntityKey key) { @Override public Object removeEntity(EntityKey key) { - final EntityHolderImpl holder = removeEntityHolder( key ); + final EntityHolderImpl holder = removeHolder( key ); if ( holder != null ) { final Object entity = holder.entity; if ( holder.proxy != null ) { @@ -549,7 +549,11 @@ public Object removeEntity(EntityKey key) { } @Override - public @Nullable EntityHolderImpl removeEntityHolder(EntityKey key) { + public @Nullable EntityHolder removeEntityHolder(EntityKey key) { + return removeHolder( key ); + } + + private EntityHolderImpl removeHolder(EntityKey key) { final EntityHolderImpl holder; if ( entitiesByKey != null ) { holder = entitiesByKey.remove( key ); @@ -1423,10 +1427,12 @@ public void setFlushing(boolean flushing) { } } - public boolean isRemovingOrphanBeforeUpates() { + @Override + public boolean isRemovingOrphanBeforeUpdates() { return removeOrphanBeforeUpdatesCounter > 0; } + @Override public void beginRemoveOrphanBeforeUpdates() { if ( getCascadeLevel() < 1 ) { throw new IllegalStateException( "Attempt to remove orphan when not cascading." ); @@ -1443,6 +1449,7 @@ public void beginRemoveOrphanBeforeUpdates() { removeOrphanBeforeUpdatesCounter++; } + @Override public void endRemoveOrphanBeforeUpdates() { if ( getCascadeLevel() < 1 ) { throw new IllegalStateException( "Finished removing orphan when not cascading." ); @@ -2317,7 +2324,7 @@ public NaturalIdResolutions getNaturalIdResolutions() { @Override public EntityHolder detachEntity(EntityKey key) { - final EntityHolderImpl entityHolder = removeEntityHolder( key ); + final EntityHolderImpl entityHolder = removeHolder( key ); if ( entityHolder != null ) { entityHolder.state = EntityHolderState.DETACHED; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index 47c3c8641f57..f3bc2aa6f48e 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -745,6 +745,12 @@ EntityHolder claimEntityHolderIfPossible( */ void setReadOnly(Object entityOrProxy, boolean readOnly); + boolean isRemovingOrphanBeforeUpdates(); + + void beginRemoveOrphanBeforeUpdates(); + + void endRemoveOrphanBeforeUpdates(); + void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Object generatedId); @Internal @@ -856,10 +862,9 @@ EntityHolder claimEntityHolderIfPossible( NaturalIdResolutions getNaturalIdResolutions(); /** - Remove the {@link EntityHolder} and set its state to DETACHED + Remove the {@link EntityHolder} and set its state to {@code DETACHED}. */ default @Nullable EntityHolder detachEntity(EntityKey key) { - EntityHolder entityHolder = removeEntityHolder( key ); - return entityHolder; + return removeEntityHolder( key ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java index 1cf47636bdae..3455f6b79d80 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java @@ -18,10 +18,10 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import static org.hibernate.pretty.MessageHelper.infoString; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** * Defines the default evict event listener used by hibernate for evicting entities @@ -48,7 +48,7 @@ public void onEvict(EvictEvent event) throws HibernateException { if ( object == null ) { throw new NullPointerException( "null passed to Session.evict()" ); } - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); + final LazyInitializer lazyInitializer = extractLazyInitializer( object ); if ( lazyInitializer != null ) { final Object id = lazyInitializer.getInternalIdentifier(); if ( id == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java index 8686dd5d1647..7f123d53d520 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionImpl.java @@ -55,7 +55,7 @@ import org.hibernate.UnresolvableObjectException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.internal.StatefulPersistenceContext; +import org.hibernate.engine.internal.PersistenceContexts; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.env.internal.NonContextualLobCreator; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; @@ -217,7 +217,7 @@ * session, and such actions are executed asynchronously when the session is {@linkplain #flush flushed}. The * motivation behind this architecture is two-fold: first, it enables customization by sophisticated extensions to * Hibernate ORM, and, second, it enables the transactional write-behind semantics of a stateful session. The stateful - * session holds its state in an instance of {@link StatefulPersistenceContext}, which we may view as the first-level + * session holds its state in an instance of {@code StatefulPersistenceContext}, which we may view as the first-level * cache associated with the session. * * @author Gavin King @@ -238,7 +238,7 @@ public class SessionImpl private transient ActionQueue actionQueue; private transient EventListenerGroups eventListenerGroups; - private transient StatefulPersistenceContext persistenceContext; + private transient PersistenceContext persistenceContext; private transient LoadQueryInfluencers loadQueryInfluencers; @@ -313,8 +313,8 @@ private FlushMode getInitialFlushMode() { : ConfigurationHelper.getFlushMode( getSessionProperty( HINT_FLUSH_MODE ), FlushMode.AUTO ); } - protected StatefulPersistenceContext createPersistenceContext() { - return new StatefulPersistenceContext( this ); + protected PersistenceContext createPersistenceContext() { + return PersistenceContexts.createPersistenceContext( this ); } protected ActionQueue createActionQueue() { @@ -864,7 +864,7 @@ private void fireMerge(final MergeContext mergeContext, final MergeEvent event) @Override public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, DeleteContext transientEntities) { checkOpenOrWaitingForAutoClose(); - final boolean removingOrphanBeforeUpdates = persistenceContext.isRemovingOrphanBeforeUpates(); + final boolean removingOrphanBeforeUpdates = persistenceContext.isRemovingOrphanBeforeUpdates(); final boolean traceEnabled = log.isTraceEnabled(); if ( traceEnabled && removingOrphanBeforeUpdates ) { logRemoveOrphanBeforeUpdates( "before continuing", entityName, object ); @@ -2992,7 +2992,7 @@ private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); - persistenceContext.serialize( oos ); + PersistenceContexts.serialize( persistenceContext, oos ); actionQueue.serialize( oos ); oos.writeObject( loadQueryInfluencers ); @@ -3014,7 +3014,7 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound ois.defaultReadObject(); - persistenceContext = StatefulPersistenceContext.deserialize( ois, this ); + persistenceContext = PersistenceContexts.deserialize( ois, this ); actionQueue = ActionQueue.deserialize( ois, this ); loadQueryInfluencers = (LoadQueryInfluencers) ois.readObject(); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java index 07ca49d3f6fb..1c26f81f0edf 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/StatelessSessionImpl.java @@ -24,7 +24,7 @@ import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.internal.StatefulPersistenceContext; +import org.hibernate.engine.internal.PersistenceContexts; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.EffectiveEntityGraph; import org.hibernate.engine.spi.EntityHolder; @@ -115,7 +115,7 @@ * cannot, unfortunately, reuse the various {@link org.hibernate.action.internal.EntityAction} subtypes. This is * a pity, since it results in some code duplication. On the other hand, a {@code StatelessSession} is easier to * debug and understand. A {@code StatelessSession} does hold state in a long-lived {@link PersistenceContext}, - * but it does temporarily keep state within an instance of {@link StatefulPersistenceContext} while processing + * but it does temporarily keep state within an instance of {@code StatefulPersistenceContext} while processing * the results of a given query. * * @author Gavin King @@ -134,7 +134,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen public StatelessSessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) { super( factory, options ); connectionProvided = options.getConnection() != null; - temporaryPersistenceContext = new StatefulPersistenceContext( this ); + temporaryPersistenceContext = PersistenceContexts.createPersistenceContext( this ); influencers = new LoadQueryInfluencers( getFactory() ); eventListenerGroups = factory.getEventListenerGroups(); setUpMultitenancy( factory, influencers ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 518fdffdbc07..1d4ddd3335d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -48,7 +48,6 @@ 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.spi.JdbcCoordinator; import org.hibernate.engine.profile.internal.FetchProfileAffectee; import org.hibernate.engine.spi.CachedNaturalIdValueSource; @@ -3853,15 +3852,18 @@ private void handleNaturalIdReattachment(Object entity, SharedSessionContractImp // for reattachment of mutable natural-ids, we absolutely positively have to grab the snapshot from the // database, because we have no other way to know if the state changed while detached. final Object[] entitySnapshot = persistenceContext.getDatabaseSnapshot( id, this ); - final Object naturalIdSnapshot = entitySnapshot == StatefulPersistenceContext.NO_ROW - ? null - : naturalIdMapping.extractNaturalIdFromEntityState( entitySnapshot ); + final Object naturalIdSnapshot = naturalIdFromSnapshot( entitySnapshot ); naturalIdResolutions.removeSharedResolution( id, naturalIdSnapshot, this, false ); final Object naturalId = naturalIdMapping.extractNaturalIdFromEntity( entity ); naturalIdResolutions.manageLocalResolution( id, naturalId, this, CachedNaturalIdValueSource.UPDATE ); } + private Object naturalIdFromSnapshot(Object[] entitySnapshot) { + return entitySnapshot == PersistenceContext.NO_ROW ? null + : naturalIdMapping.extractNaturalIdFromEntityState( entitySnapshot ); + } + @Override public Boolean isTransient(Object entity, SharedSessionContractImplementor session) throws HibernateException { final Object id = getIdentifier( entity, session );