diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java index 601abefc56d3..2bb149bf4035 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java @@ -85,13 +85,11 @@ public void addEntityEntry(Object entity, EntityEntry entityEntry) { // any addition (even the double one described above) should invalidate the cross-ref array dirty = true; - assert entityEntry instanceof AbstractEntityEntry; - // We only need to check a mutable EntityEntry is associated with the same PersistenceContext. // Immutable EntityEntry can be associated with multiple PersistenceContexts, so no need to check. // ImmutableEntityEntry#getPersistenceContext() throws an exception (HHH-10251). assert !entityEntry.getPersister().isMutable() - || ( (AbstractEntityEntry) entityEntry ).getPersistenceContext() == persistenceContext; + || ( (EntityEntryImpl) entityEntry ).getPersistenceContext() == persistenceContext; // Determine the appropriate ManagedEntity instance to use based on whether the entity is enhanced or not. // Throw an exception if entity is a mutable ManagedEntity that is associated with a different @@ -171,8 +169,8 @@ private ManagedEntity getAssociatedManagedEntity(Object entity) { // it is not associated return null; } - final AbstractEntityEntry entityEntry = - (AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry(); + final EntityEntryImpl entityEntry = + (EntityEntryImpl) managedEntity.$$_hibernate_getEntityEntry(); if ( entityEntry.getPersister().isMutable() ) { return entityEntry.getPersistenceContext() == persistenceContext @@ -212,7 +210,7 @@ private void putImmutableManagedEntity(ManagedEntity managed, int instanceId, Im private void checkNotAssociatedWithOtherPersistenceContextIfMutable(ManagedEntity managedEntity) { // we only have to check mutable managedEntity - final AbstractEntityEntry entityEntry = (AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry(); + final EntityEntryImpl entityEntry = (EntityEntryImpl) managedEntity.$$_hibernate_getEntityEntry(); if ( entityEntry == null || !entityEntry.getPersister().isMutable() || entityEntry.getPersistenceContext() == null || @@ -729,8 +727,8 @@ public ImmutableManagedEntityHolder(ManagedEntity immutableManagedEntity) { // Check instance type of EntityEntry and if type is ImmutableEntityEntry, // check to see if entity is referenced cached in the second level cache private static boolean canClearEntityEntryReference(EntityEntry entityEntry) { - return !(entityEntry instanceof ImmutableEntityEntry) - || !isReferenceCachingEnabled( entityEntry.getPersister() ); + final EntityPersister persister = entityEntry.getPersister(); + return persister.isMutable() || !isReferenceCachingEnabled( persister ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java similarity index 81% rename from hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java rename to hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java index cc9a0dfbd7ac..d742a1bfffd1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java @@ -5,6 +5,7 @@ package org.hibernate.engine.internal; import java.io.IOException; +import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -12,6 +13,7 @@ import org.hibernate.CustomEntityDirtinessStrategy; import org.hibernate.HibernateException; import org.hibernate.LockMode; +import org.hibernate.UnsupportedLockAttemptException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.EntityEntry; @@ -33,11 +35,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; 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.EntityEntryImpl.BooleanState.EXISTS_IN_DATABASE; +import static org.hibernate.engine.internal.EntityEntryImpl.BooleanState.IS_BEING_REPLICATED; +import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.LOCK_MODE; +import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS; +import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker; @@ -52,6 +54,7 @@ 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.internal.util.StringHelper.nullIfEmpty; import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; @@ -63,17 +66,17 @@ * @author Gunnar Morling * @author Sanne Grinovero */ -public abstract class AbstractEntityEntry implements Serializable, EntityEntry { - - protected final Object id; - protected Object[] loadedState; - protected Object version; - protected final EntityPersister persister; // permanent but we only need the entityName state in a non transient way - protected transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized) - protected final transient Object rowId; - protected final transient PersistenceContext persistenceContext; - protected transient @Nullable ImmutableBitSet maybeLazySet; - protected EntityEntryExtraState next; +public final class EntityEntryImpl implements Serializable, EntityEntry { + + private final Object id; + private Object[] loadedState; + private Object version; + private final EntityPersister persister; // permanent but we only need the entityName state in a non transient way + private transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized) + private final transient Object rowId; + private final transient PersistenceContext persistenceContext; + private transient @Nullable ImmutableBitSet maybeLazySet; + private EntityEntryExtraState next; /** * Holds several boolean and enum typed attributes in a very compact manner. Enum values are stored in 4 bits @@ -102,7 +105,7 @@ public abstract class AbstractEntityEntry implements Serializable, EntityEntry { */ private transient int compressedState; - public AbstractEntityEntry( + public EntityEntryImpl( final Status status, final Object[] loadedState, final Object rowId, @@ -127,14 +130,17 @@ public AbstractEntityEntry( setCompressedValue( LOCK_MODE, lockMode ); setCompressedValue( IS_BEING_REPLICATED, disableVersionIncrement ); this.persister = persister; - this.persistenceContext = persistenceContext; + // don't store PersistenceContext for immutable entity, see HHH-10251 + this.persistenceContext = + persister == null || persister.isMutable() + ? persistenceContext + : null; } /** - * This for is used during custom deserialization handling + * Overloaded form used during custom deserialization */ - protected AbstractEntityEntry( - final SessionFactoryImplementor factory, + private EntityEntryImpl( final String entityName, final Object id, final Status status, @@ -145,10 +151,14 @@ protected AbstractEntityEntry( final LockMode lockMode, final boolean existsInDatabase, final boolean isBeingReplicated, + final boolean mutable, final PersistenceContext persistenceContext) { - this.persister = factory == null - ? null - : factory.getMappingMetamodel().getEntityDescriptor( entityName ); + final SessionFactoryImplementor factory = + persistenceContext.getSession().getFactory(); + this.persister = + factory == null + ? null + : factory.getMappingMetamodel().getEntityDescriptor( entityName ); this.id = id; setCompressedValue( STATUS, status ); setCompressedValue( PREVIOUS_STATUS, previousStatus ); @@ -159,7 +169,8 @@ protected AbstractEntityEntry( setCompressedValue( EXISTS_IN_DATABASE, existsInDatabase ); setCompressedValue( IS_BEING_REPLICATED, isBeingReplicated ); this.rowId = null; // this is equivalent to the old behavior... - this.persistenceContext = persistenceContext; + // don't store PersistenceContext for immutable entity, see HHH-10251 + this.persistenceContext = mutable ? persistenceContext : null; } @Override @@ -169,10 +180,14 @@ public LockMode getLockMode() { @Override public void setLockMode(LockMode lockMode) { + if ( lockMode.greaterThan( LockMode.READ ) + && persister!=null && !persister.isMutable() ) { + throw new UnsupportedLockAttemptException( "Lock mode " + lockMode + + " not supported for read-only entity" ); + } setCompressedValue( LOCK_MODE, lockMode ); } - @Override public Status getStatus() { return getCompressedValue( STATUS ); @@ -197,12 +212,12 @@ public void setStatus(Status status) { } @Override - public final Object getId() { + public Object getId() { return id; } @Override - public final Object[] getLoadedState() { + public Object[] getLoadedState() { return loadedState; } @@ -232,7 +247,7 @@ public boolean isExistsInDatabase() { } @Override - public final Object getVersion() { + public Object getVersion() { return version; } @@ -242,7 +257,7 @@ public void postInsert(Object version) { } @Override - public final EntityPersister getPersister() { + public EntityPersister getPersister() { return persister; } @@ -283,8 +298,8 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion) persister.setValue( entity, persister.getVersionProperty(), nextVersion ); } - processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes ); - processIfManagedEntity( entity, AbstractEntityEntry::useTracker ); + processIfSelfDirtinessTracker( entity, EntityEntryImpl::clearDirtyAttributes ); + processIfManagedEntity( entity, EntityEntryImpl::useTracker ); final SharedSessionContractImplementor session = getPersistenceContext().getSession(); session.getFactory().getCustomEntityDirtinessStrategy() @@ -437,11 +452,11 @@ public void setReadOnly(boolean readOnly, Object entity) { setStatus( READ_ONLY ); loadedState = null; } + else if ( !persister.isMutable() ) { + throw new IllegalStateException( "Cannot make an entity of immutable type '" + + persister.getEntityName() + "' modifiable" ); + } else { - if ( ! persister.isMutable() ) { - throw new IllegalStateException( "Cannot make an entity of immutable type '" - + persister.getEntityName() + "' modifiable" ); - } setStatus( MANAGED ); loadedState = persister.getValues( entity ); TypeHelper.deepCopy( @@ -483,19 +498,51 @@ public String toString() { @Override public void serialize(ObjectOutputStream oos) throws IOException { final Status previousStatus = getPreviousStatus(); - oos.writeObject( getEntityName() ); + final String entityName = getEntityName(); + oos.writeUTF( entityName == null ? "" : entityName ); oos.writeObject( id ); - oos.writeObject( getStatus().name() ); - oos.writeObject( (previousStatus == null ? "" : previousStatus.name()) ); + oos.writeInt( getStatus().ordinal() ); + oos.writeInt( previousStatus == null ? -1 : previousStatus.ordinal() ); // todo : potentially look at optimizing these two arrays oos.writeObject( loadedState ); oos.writeObject( getDeletedState() ); oos.writeObject( version ); - oos.writeObject( getLockMode().toString() ); + oos.writeInt( getLockMode().ordinal() ); oos.writeBoolean( isExistsInDatabase() ); oos.writeBoolean( isBeingReplicated() ); + oos.writeBoolean( persister == null || persister.isMutable() ); } + /** + * Custom deserialization routine used during deserialization + * of a {@link PersistenceContext} for increased performance. + * + * @param ois The stream from which to read the entry + * @param persistenceContext The context being deserialized + * + * @return The deserialized {@code EntityEntry} + * + * @throws IOException If a stream error occurs + * @throws ClassNotFoundException If any of the classes declared + * in the stream cannot be found + */ + public static EntityEntry deserialize(ObjectInputStream ois, PersistenceContext persistenceContext) + throws IOException, ClassNotFoundException { + return new EntityEntryImpl( + nullIfEmpty( ois.readUTF() ), + ois.readObject(), + Status.fromOrdinal( ois.readInt() ), + Status.fromOrdinal( ois.readInt() ), + (Object[]) ois.readObject(), + (Object[]) ois.readObject(), + ois.readObject(), + LockMode.values()[ ois.readInt() ], + ois.readBoolean(), + ois.readBoolean(), + ois.readBoolean(), + persistenceContext + ); + } @Override public void addExtraState(EntityEntryExtraState extraState) { @@ -520,7 +567,10 @@ public T getExtraState(Class extraStateType } } - public PersistenceContext getPersistenceContext(){ + public PersistenceContext getPersistenceContext() { + if ( persistenceContext == null ) { + throw new UnsupportedOperationException( "PersistenceContext not available for immutable entity" ); + } return persistenceContext; } @@ -533,7 +583,7 @@ public PersistenceContext getPersistenceContext(){ * the value to store; The caller must make sure that it matches * the given identifier */ - protected > void setCompressedValue(EnumState state, E value) { + > void setCompressedValue(EnumState state, E value) { // reset the bits for the given property to 0 compressedState &= state.getUnsetMask(); // store the numeric representation of the enum value at the right offset @@ -547,7 +597,7 @@ protected > void setCompressedValue(EnumState state, E valu * identifies the value to store * @return the current value of the specified property */ - protected > E getCompressedValue(EnumState state) { + > E getCompressedValue(EnumState state) { // restore the numeric value from the bits at the right offset and return the corresponding enum constant final int index = ( ( compressedState & state.getMask() ) >> state.getOffset() ) - 1; return index == - 1 ? null : state.getEnumConstants()[index]; @@ -561,7 +611,7 @@ protected > E getCompressedValue(EnumState state) { * @param value * the value to store */ - protected void setCompressedValue(BooleanState state, boolean value) { + void setCompressedValue(BooleanState state, boolean value) { compressedState &= state.getUnsetMask(); compressedState |= ( state.getValue( value ) << state.getOffset() ); } @@ -573,7 +623,7 @@ protected void setCompressedValue(BooleanState state, boolean value) { * identifies the value to store * @return the current value of the specified flag */ - protected boolean getCompressedValue(BooleanState state) { + boolean getCompressedValue(BooleanState state) { return ( ( compressedState & state.getMask() ) >> state.getOffset() ) == 1; } @@ -582,7 +632,7 @@ protected boolean getCompressedValue(BooleanState state) { * * @author Gunnar Morling */ - protected static class EnumState> { + static class EnumState> { protected static final EnumState LOCK_MODE = new EnumState<>( 0, LockMode.class ); protected static final EnumState STATUS = new EnumState<>( 4, Status.class ); @@ -593,11 +643,11 @@ protected static class EnumState> { protected final int mask; protected final int unsetMask; - private EnumState(int offset, Class enumType) { + EnumState(int offset, Class 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 + // we'd have to restructure 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." ); @@ -654,7 +704,7 @@ private E[] getEnumConstants() { * * @author Gunnar Morling */ - protected enum BooleanState { + enum BooleanState { EXISTS_IN_DATABASE(13), IS_BEING_REPLICATED(14); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntry.java deleted file mode 100644 index bfba4920a81b..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntry.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.engine.internal; - -import java.io.IOException; -import java.io.ObjectInputStream; - -import org.hibernate.AssertionFailure; -import org.hibernate.LockMode; -import org.hibernate.UnsupportedLockAttemptException; -import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.PersistenceContext; -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 {@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 - * @author Gunnar Morling - * @author Sanne Grinovero - * - * @see org.hibernate.annotations.Immutable - */ -public final class ImmutableEntityEntry extends AbstractEntityEntry { - public ImmutableEntityEntry( - final Status status, - final Object[] loadedState, - final Object rowId, - final Object id, - final Object version, - final LockMode lockMode, - final boolean existsInDatabase, - final EntityPersister persister, - final boolean disableVersionIncrement) { - - super( - status, - loadedState, - rowId, - id, - version, - lockMode, - existsInDatabase, - persister, - disableVersionIncrement, - // purposefully do not pass along the session/persistence-context : HHH-10251 - null - ); - } - - /** - * This for is used during custom deserialization handling - */ - private ImmutableEntityEntry( - final SessionFactoryImplementor factory, - final String entityName, - final Object id, - final Status status, - final Status previousStatus, - final Object[] loadedState, - final Object[] deletedState, - final Object version, - final LockMode lockMode, - final boolean existsInDatabase, - final boolean isBeingReplicated, - final PersistenceContext persistenceContext) { - - super( factory, entityName, id, status, previousStatus, loadedState, deletedState, - version, lockMode, existsInDatabase, isBeingReplicated, persistenceContext - ); - } - - @Override - public void setLockMode(LockMode lockMode) { - if ( lockMode.greaterThan(LockMode.READ) ) { - throw new UnsupportedLockAttemptException( "Lock mode " - + lockMode + " not supported for read-only entity" ); - } - else { - setCompressedValue( LOCK_MODE, lockMode ); - } - } - - /** - * Custom deserialization routine used during deserialization of a - * Session/PersistenceContext for increased performance. - * - * @param ois The stream from which to read the entry. - * @param persistenceContext The context being deserialized. - * - * @return The deserialized EntityEntry - * - * @throws IOException If a stream error occurs - * @throws ClassNotFoundException If any of the classes declared in the stream - * cannot be found - */ - public static EntityEntry deserialize( - ObjectInputStream ois, - PersistenceContext persistenceContext) throws IOException, ClassNotFoundException { - String previousStatusString; - return new ImmutableEntityEntry( - persistenceContext.getSession().getFactory(), - (String) ois.readObject(), - ois.readObject(), - Status.valueOf( (String) ois.readObject() ), - ( previousStatusString = (String) ois.readObject() ) - .isEmpty() - ? null - : Status.valueOf( previousStatusString ), - (Object[]) ois.readObject(), - (Object[]) ois.readObject(), - ois.readObject(), - LockMode.valueOf( (String) ois.readObject() ), - ois.readBoolean(), - ois.readBoolean(), - null - ); - } - - @Override - public PersistenceContext getPersistenceContext() { - throw new AssertionFailure( "Session/PersistenceContext is not available from an ImmutableEntityEntry" ); - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntryFactory.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntryFactory.java index ca910513c509..d18027c8bb11 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntryFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/ImmutableEntityEntryFactory.java @@ -40,7 +40,7 @@ public EntityEntry createEntityEntry( EntityPersister persister, boolean disableVersionIncrement, PersistenceContext persistenceContext) { - return new ImmutableEntityEntry( + return new EntityEntryImpl( status, loadedState, rowId, @@ -49,7 +49,8 @@ public EntityEntry createEntityEntry( lockMode, existsInDatabase, persister, - disableVersionIncrement + disableVersionIncrement, + persistenceContext ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntry.java deleted file mode 100644 index 906dc2ed1e9f..000000000000 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntry.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.engine.internal; - -import java.io.IOException; -import java.io.ObjectInputStream; - -import org.hibernate.LockMode; -import org.hibernate.engine.spi.EntityEntry; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.Status; -import org.hibernate.persister.entity.EntityPersister; - -/** - * An {@link EntityEntry} implementation for mutable entities. - * - * @author Gavin King - * @author Emmanuel Bernard - * @author Gunnar Morling - * @author Sanne Grinovero - */ -public final class MutableEntityEntry extends AbstractEntityEntry { - - public MutableEntityEntry( - final Status status, - final Object[] loadedState, - final Object rowId, - final Object id, - final Object version, - final LockMode lockMode, - final boolean existsInDatabase, - final EntityPersister persister, - final boolean disableVersionIncrement, - final PersistenceContext persistenceContext) { - super( status, loadedState, rowId, id, version, lockMode, existsInDatabase, persister, - disableVersionIncrement, persistenceContext - ); - } - - /** - * This for is used during custom deserialization handling - */ - private MutableEntityEntry( - final SessionFactoryImplementor factory, - final String entityName, - final Object id, - final Status status, - final Status previousStatus, - final Object[] loadedState, - final Object[] deletedState, - final Object version, - final LockMode lockMode, - final boolean existsInDatabase, - final boolean isBeingReplicated, - final PersistenceContext persistenceContext) { - super( factory, entityName, id, status, previousStatus, loadedState, deletedState, - version, lockMode, existsInDatabase, isBeingReplicated, persistenceContext - ); - } - - /** - * Custom deserialization routine used during deserialization of a - * Session/PersistenceContext for increased performance. - * - * @param ois The stream from which to read the entry. - * @param persistenceContext The context being deserialized. - * - * @return The deserialized EntityEntry - * - * @throws IOException If a stream error occurs - * @throws ClassNotFoundException If any of the classes declared in the stream - * cannot be found - */ - public static EntityEntry deserialize( - ObjectInputStream ois, - PersistenceContext persistenceContext) throws IOException, ClassNotFoundException { - String previousStatusString; - return new MutableEntityEntry( - persistenceContext.getSession().getFactory(), - (String) ois.readObject(), - ois.readObject(), - Status.valueOf( (String) ois.readObject() ), - ( previousStatusString = (String) ois.readObject() ) - .isEmpty() - ? null - : Status.valueOf( previousStatusString ), - (Object[]) ois.readObject(), - (Object[]) ois.readObject(), - ois.readObject(), - LockMode.valueOf( (String) ois.readObject() ), - ois.readBoolean(), - ois.readBoolean(), - persistenceContext - ); - } -} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntryFactory.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntryFactory.java index 41aa3e788eba..cb8f345493ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntryFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/MutableEntityEntryFactory.java @@ -40,7 +40,7 @@ public EntityEntry createEntityEntry( EntityPersister persister, boolean disableVersionIncrement, PersistenceContext persistenceContext) { - return new MutableEntityEntry( + return new EntityEntryImpl( status, loadedState, rowId, 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 f60e6d992097..83ad4f7a5aa6 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 @@ -656,30 +656,18 @@ public EntityEntry addEntry( final boolean disableVersionIncrement) { assert lockMode != null; final EntityEntry entityEntry = - persister.isMutable() - ? new MutableEntityEntry( - status, - loadedState, - rowId, - id, - version, - lockMode, - existsInDatabase, - persister, - disableVersionIncrement, - this - ) - : new ImmutableEntityEntry( - status, - loadedState, - rowId, - id, - version, - lockMode, - existsInDatabase, - persister, - disableVersionIncrement - ); + new EntityEntryImpl( + status, + loadedState, + rowId, + id, + version, + lockMode, + existsInDatabase, + persister, + disableVersionIncrement, + this + ); entityEntryContext.addEntityEntry( entity, entityEntry ); setHasNonReadOnlyEnties( status ); return entityEntry; @@ -758,15 +746,14 @@ public void reassociateProxy(Object value, Object id) throws MappingException { * @param proxy The proxy to reassociate. */ private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) { - if ( li.getSession() != this.getSession() ) { + if ( li.getSession() != session ) { final EntityPersister persister = session.getFactory().getMappingMetamodel() .getEntityDescriptor( li.getEntityName() ); final EntityKey key = session.generateEntityKey( li.getInternalIdentifier(), persister ); - // any earlier proxy takes precedence - final Map entityHolderMap = getOrInitializeEntitiesByKey(); final EntityHolderImpl holder = getOrInitializeNewHolder().withProxy( key, persister, proxy ); - final EntityHolderImpl oldHolder = entityHolderMap.putIfAbsent( key, holder ); + // any earlier proxy takes precedence + final EntityHolderImpl oldHolder = getOrInitializeEntitiesByKey().putIfAbsent( key, holder ); if ( oldHolder != null ) { if ( oldHolder.proxy == null ) { oldHolder.proxy = proxy; @@ -977,7 +964,7 @@ public Object getCollectionOwner(Object key, CollectionPersister collectionPersi // probably changes to how the sql for collection initializers are generated // // We could also possibly see if the referenced property is a natural id since we already have caching - // in place of natural id snapshots. BUt really its better to just do it the right way ^^ if we start + // in place of natural id snapshots. But really it's better to just do it the right way ^^ if we start // going that route final Object ownerId = ownerPersister.getIdByUniqueKey( key, collectionType.getLHSPropertyName(), session ); return getEntity( session.generateEntityKey( ownerId, ownerPersister ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/Status.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/Status.java index 79fd9511d767..a91ee74da45c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/Status.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/Status.java @@ -4,6 +4,8 @@ */ package org.hibernate.engine.spi; +import org.checkerframework.checker.nullness.qual.Nullable; + /** * Represents the status of an entity with respect to * this session. These statuses are for internal @@ -22,4 +24,9 @@ public enum Status { public boolean isDeletedOrGone() { return this == DELETED || this == GONE; } + + public static @Nullable Status fromOrdinal(int ordinal) { + final Status[] values = values(); + return ordinal < 0 || ordinal >= values.length ? null : values[ordinal]; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java index f0bb9073fbd0..4f28a8374738 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java @@ -192,7 +192,7 @@ private void deleteDetachedEntity( final EntityEntry entityEntry = persistenceContext.addEntity( entity, persister.isMutable() ? Status.MANAGED : Status.READ_ONLY, - persister.getValues(entity), + persister.getValues( entity ), key, version, LockMode.NONE, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cache/ByteCodeEnhancedImmutableReferenceCacheTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cache/ByteCodeEnhancedImmutableReferenceCacheTest.java index c7486b492a48..1fd234e1c485 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cache/ByteCodeEnhancedImmutableReferenceCacheTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cache/ByteCodeEnhancedImmutableReferenceCacheTest.java @@ -49,7 +49,7 @@ protected Class[] getAnnotatedClasses() { } @Test - public void testUseOfDirectReferencesInCache() throws Exception { + public void testUseOfDirectReferencesInCache() { EntityPersister persister = sessionFactory().getMappingMetamodel().getEntityDescriptor( MyEnhancedReferenceData.class ); assertFalse( persister.isMutable() ); assertTrue( persister.buildCacheEntry( null, null, null, null ).isReferenceEntry() ); @@ -75,17 +75,20 @@ public void testUseOfDirectReferencesInCache() throws Exception { s.close(); // the 2 instances should be the same (==) - assertTrue( "The two instances were different references", myReferenceData == loaded ); + assertSame( "The two instances were different references", myReferenceData, loaded ); // now try query caching s = openSession(); s.beginTransaction(); - MyEnhancedReferenceData queried = (MyEnhancedReferenceData) s.createQuery( "from MyEnhancedReferenceData" ).setCacheable( true ).list().get( 0 ); + MyEnhancedReferenceData queried = (MyEnhancedReferenceData) + s.createQuery( "from MyEnhancedReferenceData" ) + .setCacheable( true ) + .list().get( 0 ); s.getTransaction().commit(); s.close(); // the 2 instances should be the same (==) - assertTrue( "The two instances were different references", myReferenceData == queried ); + assertSame( "The two instances were different references", myReferenceData, queried ); // cleanup s = openSession(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/delete/CascadeDeleteTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/delete/CascadeDeleteTest.java index a9baa857d9fa..3f2c16d10f83 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/delete/CascadeDeleteTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/cascade/circle/delete/CascadeDeleteTest.java @@ -4,7 +4,6 @@ */ package org.hibernate.orm.test.cascade.circle.delete; -import java.util.List; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.DomainModel; @@ -28,10 +27,8 @@ public void setUp(SessionFactoryScope scope) { scope.inTransaction( session -> { Person person = new Person(); - Address currentAddress = new Address( "Localita S. Egidio Gradoli (VT)" ); person.addCurrentAddress( currentAddress ); - session.persist( person ); } ); @@ -41,10 +38,8 @@ public void setUp(SessionFactoryScope scope) { public void testDelete(SessionFactoryScope scope) { scope.inTransaction( session -> { - List people = session.createSelectionQuery( "from Person", Person.class ).list(); - people.forEach( person -> { - session.remove( person ); - } ); + session.createSelectionQuery( "from Person", Person.class ) + .list().forEach( session::remove ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java index ba50caf4c051..21615b0c8057 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/engine/spi/EntityEntryTest.java @@ -11,7 +11,7 @@ import java.io.ObjectOutputStream; import org.hibernate.LockMode; -import org.hibernate.engine.internal.MutableEntityEntry; +import org.hibernate.engine.internal.EntityEntryImpl; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SessionImplementor; @@ -21,6 +21,8 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -34,61 +36,53 @@ public class EntityEntryTest { @Test public void packedAttributesAreSetByConstructor() { EntityEntry entityEntry = createEntityEntry(); - assertEquals( LockMode.OPTIMISTIC, entityEntry.getLockMode() ); Assert.assertEquals( Status.MANAGED, entityEntry.getStatus() ); - assertEquals( true, entityEntry.isExistsInDatabase() ); - assertEquals( true, entityEntry.isBeingReplicated() ); + assertTrue( entityEntry.isExistsInDatabase() ); + assertTrue( entityEntry.isBeingReplicated() ); } @Test public void testLockModeCanBeSetAndDoesNotAffectOtherPackedAttributes() { // Given EntityEntry entityEntry = createEntityEntry(); - assertEquals( LockMode.OPTIMISTIC, entityEntry.getLockMode() ); assertEquals( Status.MANAGED, entityEntry.getStatus() ); - assertEquals( true, entityEntry.isExistsInDatabase() ); - assertEquals( true, entityEntry.isBeingReplicated() ); - + assertTrue( entityEntry.isExistsInDatabase() ); + assertTrue( entityEntry.isBeingReplicated() ); // When entityEntry.setLockMode( LockMode.PESSIMISTIC_READ ); - // Then assertEquals( LockMode.PESSIMISTIC_READ, entityEntry.getLockMode() ); assertEquals( Status.MANAGED, entityEntry.getStatus() ); - assertEquals( true, entityEntry.isExistsInDatabase() ); - assertEquals( true, entityEntry.isBeingReplicated() ); + assertTrue( entityEntry.isExistsInDatabase() ); + assertTrue( entityEntry.isBeingReplicated() ); } @Test public void testStatusCanBeSetAndDoesNotAffectOtherPackedAttributes() { // Given EntityEntry entityEntry = createEntityEntry(); - // When entityEntry.setStatus( Status.DELETED ); - // Then assertEquals( LockMode.OPTIMISTIC, entityEntry.getLockMode() ); assertEquals( Status.DELETED, entityEntry.getStatus() ); - assertEquals( true, entityEntry.isExistsInDatabase() ); - assertEquals( true, entityEntry.isBeingReplicated() ); + assertTrue( entityEntry.isExistsInDatabase() ); + assertTrue( entityEntry.isBeingReplicated() ); } @Test public void testPostDeleteSetsStatusAndExistsInDatabaseWithoutAffectingOtherPackedAttributes() { // Given EntityEntry entityEntry = createEntityEntry(); - // When entityEntry.postDelete(); - // Then assertEquals( LockMode.OPTIMISTIC, entityEntry.getLockMode() ); assertEquals( Status.GONE, entityEntry.getStatus() ); - assertEquals( false, entityEntry.isExistsInDatabase() ); - assertEquals( true, entityEntry.isBeingReplicated() ); + assertFalse( entityEntry.isExistsInDatabase() ); + assertTrue( entityEntry.isBeingReplicated() ); } @Test @@ -101,17 +95,18 @@ public void testSerializationAndDeserializationKeepCorrectPackedAttributes() thr oos.flush(); InputStream is = new ByteArrayInputStream( baos.toByteArray() ); - EntityEntry deserializedEntry = MutableEntityEntry.deserialize(new ObjectInputStream( is ), getPersistenceContextMock() ); + EntityEntry deserializedEntry = + EntityEntryImpl.deserialize( new ObjectInputStream( is ), + getPersistenceContextMock() ); assertEquals( LockMode.OPTIMISTIC, deserializedEntry.getLockMode() ); assertEquals( Status.MANAGED, deserializedEntry.getStatus() ); - assertEquals( true, deserializedEntry.isExistsInDatabase() ); - assertEquals( true, deserializedEntry.isBeingReplicated() ); + assertTrue( deserializedEntry.isExistsInDatabase() ); + assertTrue( deserializedEntry.isBeingReplicated() ); } private EntityEntry createEntityEntry() { - - return new MutableEntityEntry( + return new EntityEntryImpl( // status Status.MANAGED, // loadedState @@ -134,11 +129,10 @@ private EntityEntry createEntityEntry() { ); } - private final PersistenceContext getPersistenceContextMock() { + private PersistenceContext getPersistenceContextMock() { SessionImplementor sessionMock = mock( SessionImplementor.class ); PersistenceContext persistenceContextMock = mock( PersistenceContext.class ); when( persistenceContextMock.getSession() ).thenReturn( sessionMock ); - return persistenceContextMock; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ConnectionProviderDelegate.java b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ConnectionProviderDelegate.java index 060a24d98ffb..48fdfa1f7041 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ConnectionProviderDelegate.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/ConnectionProviderDelegate.java @@ -127,10 +127,8 @@ public void closeConnection(Connection connection) throws SQLException { @Override public boolean supportsAggressiveRelease() { - if ( forceSupportsAggressiveRelease ) { - return true; - } - return connectionProvider.supportsAggressiveRelease(); + return forceSupportsAggressiveRelease + || connectionProvider.supportsAggressiveRelease(); } @Override diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java index 0c07837bbd0f..2503a8157dbc 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/jdbc/ConnectionProviderDelegate.java @@ -98,10 +98,8 @@ public void closeConnection(Connection connection) throws SQLException { @Override public boolean supportsAggressiveRelease() { - if ( forceSupportsAggressiveRelease ) { - return true; - } - return connectionProvider.supportsAggressiveRelease(); + return forceSupportsAggressiveRelease + || connectionProvider.supportsAggressiveRelease(); } @Override