Skip to content

Commit bc4d78b

Browse files
committed
HHH-19483 kill off completely unnecessary split into two kinds of EntityEntry
1 parent c70dfad commit bc4d78b

File tree

12 files changed

+162
-361
lines changed

12 files changed

+162
-361
lines changed

hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,11 @@ public void addEntityEntry(Object entity, EntityEntry entityEntry) {
8585
// any addition (even the double one described above) should invalidate the cross-ref array
8686
dirty = true;
8787

88-
assert entityEntry instanceof AbstractEntityEntry;
89-
9088
// We only need to check a mutable EntityEntry is associated with the same PersistenceContext.
9189
// Immutable EntityEntry can be associated with multiple PersistenceContexts, so no need to check.
9290
// ImmutableEntityEntry#getPersistenceContext() throws an exception (HHH-10251).
9391
assert !entityEntry.getPersister().isMutable()
94-
|| ( (AbstractEntityEntry) entityEntry ).getPersistenceContext() == persistenceContext;
92+
|| ( (EntityEntryImpl) entityEntry ).getPersistenceContext() == persistenceContext;
9593

9694
// Determine the appropriate ManagedEntity instance to use based on whether the entity is enhanced or not.
9795
// Throw an exception if entity is a mutable ManagedEntity that is associated with a different
@@ -171,8 +169,8 @@ private ManagedEntity getAssociatedManagedEntity(Object entity) {
171169
// it is not associated
172170
return null;
173171
}
174-
final AbstractEntityEntry entityEntry =
175-
(AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry();
172+
final EntityEntryImpl entityEntry =
173+
(EntityEntryImpl) managedEntity.$$_hibernate_getEntityEntry();
176174

177175
if ( entityEntry.getPersister().isMutable() ) {
178176
return entityEntry.getPersistenceContext() == persistenceContext
@@ -212,7 +210,7 @@ private void putImmutableManagedEntity(ManagedEntity managed, int instanceId, Im
212210

213211
private void checkNotAssociatedWithOtherPersistenceContextIfMutable(ManagedEntity managedEntity) {
214212
// we only have to check mutable managedEntity
215-
final AbstractEntityEntry entityEntry = (AbstractEntityEntry) managedEntity.$$_hibernate_getEntityEntry();
213+
final EntityEntryImpl entityEntry = (EntityEntryImpl) managedEntity.$$_hibernate_getEntityEntry();
216214
if ( entityEntry == null ||
217215
!entityEntry.getPersister().isMutable() ||
218216
entityEntry.getPersistenceContext() == null ||
@@ -729,8 +727,8 @@ public ImmutableManagedEntityHolder(ManagedEntity immutableManagedEntity) {
729727
// Check instance type of EntityEntry and if type is ImmutableEntityEntry,
730728
// check to see if entity is referenced cached in the second level cache
731729
private static boolean canClearEntityEntryReference(EntityEntry entityEntry) {
732-
return !(entityEntry instanceof ImmutableEntityEntry)
733-
|| !isReferenceCachingEnabled( entityEntry.getPersister() );
730+
final EntityPersister persister = entityEntry.getPersister();
731+
return persister.isMutable() || !isReferenceCachingEnabled( persister );
734732
}
735733

736734
@Override

hibernate-core/src/main/java/org/hibernate/engine/internal/AbstractEntityEntry.java renamed to hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryImpl.java

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
package org.hibernate.engine.internal;
66

77
import java.io.IOException;
8+
import java.io.ObjectInputStream;
89
import java.io.ObjectOutputStream;
910
import java.io.Serializable;
1011

1112
import org.hibernate.AssertionFailure;
1213
import org.hibernate.CustomEntityDirtinessStrategy;
1314
import org.hibernate.HibernateException;
1415
import org.hibernate.LockMode;
16+
import org.hibernate.UnsupportedLockAttemptException;
1517
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
1618
import org.hibernate.collection.spi.PersistentCollection;
1719
import org.hibernate.engine.spi.EntityEntry;
@@ -33,11 +35,11 @@
3335
import org.checkerframework.checker.nullness.qual.Nullable;
3436

3537
import static org.hibernate.LockMode.PESSIMISTIC_FORCE_INCREMENT;
36-
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.EXISTS_IN_DATABASE;
37-
import static org.hibernate.engine.internal.AbstractEntityEntry.BooleanState.IS_BEING_REPLICATED;
38-
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.LOCK_MODE;
39-
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.PREVIOUS_STATUS;
40-
import static org.hibernate.engine.internal.AbstractEntityEntry.EnumState.STATUS;
38+
import static org.hibernate.engine.internal.EntityEntryImpl.BooleanState.EXISTS_IN_DATABASE;
39+
import static org.hibernate.engine.internal.EntityEntryImpl.BooleanState.IS_BEING_REPLICATED;
40+
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.LOCK_MODE;
41+
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.PREVIOUS_STATUS;
42+
import static org.hibernate.engine.internal.EntityEntryImpl.EnumState.STATUS;
4143
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
4244
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
4345
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
@@ -52,6 +54,7 @@
5254
import static org.hibernate.engine.spi.Status.MANAGED;
5355
import static org.hibernate.engine.spi.Status.READ_ONLY;
5456
import static org.hibernate.engine.spi.Status.SAVING;
57+
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
5558
import static org.hibernate.pretty.MessageHelper.infoString;
5659
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
5760

@@ -63,17 +66,17 @@
6366
* @author Gunnar Morling
6467
* @author Sanne Grinovero
6568
*/
66-
public abstract class AbstractEntityEntry implements Serializable, EntityEntry {
67-
68-
protected final Object id;
69-
protected Object[] loadedState;
70-
protected Object version;
71-
protected final EntityPersister persister; // permanent but we only need the entityName state in a non transient way
72-
protected transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized)
73-
protected final transient Object rowId;
74-
protected final transient PersistenceContext persistenceContext;
75-
protected transient @Nullable ImmutableBitSet maybeLazySet;
76-
protected EntityEntryExtraState next;
69+
public final class EntityEntryImpl implements Serializable, EntityEntry {
70+
71+
private final Object id;
72+
private Object[] loadedState;
73+
private Object version;
74+
private final EntityPersister persister; // permanent but we only need the entityName state in a non transient way
75+
private transient EntityKey cachedEntityKey; // cached EntityKey (lazy-initialized)
76+
private final transient Object rowId;
77+
private final transient PersistenceContext persistenceContext;
78+
private transient @Nullable ImmutableBitSet maybeLazySet;
79+
private EntityEntryExtraState next;
7780

7881
/**
7982
* 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 {
102105
*/
103106
private transient int compressedState;
104107

105-
public AbstractEntityEntry(
108+
public EntityEntryImpl(
106109
final Status status,
107110
final Object[] loadedState,
108111
final Object rowId,
@@ -127,14 +130,17 @@ public AbstractEntityEntry(
127130
setCompressedValue( LOCK_MODE, lockMode );
128131
setCompressedValue( IS_BEING_REPLICATED, disableVersionIncrement );
129132
this.persister = persister;
130-
this.persistenceContext = persistenceContext;
133+
// don't store PersistenceContext for immutable entity, see HHH-10251
134+
this.persistenceContext =
135+
persister == null || persister.isMutable()
136+
? persistenceContext
137+
: null;
131138
}
132139

133140
/**
134-
* This for is used during custom deserialization handling
141+
* Overloaded form used during custom deserialization
135142
*/
136-
protected AbstractEntityEntry(
137-
final SessionFactoryImplementor factory,
143+
private EntityEntryImpl(
138144
final String entityName,
139145
final Object id,
140146
final Status status,
@@ -145,10 +151,14 @@ protected AbstractEntityEntry(
145151
final LockMode lockMode,
146152
final boolean existsInDatabase,
147153
final boolean isBeingReplicated,
154+
final boolean mutable,
148155
final PersistenceContext persistenceContext) {
149-
this.persister = factory == null
150-
? null
151-
: factory.getMappingMetamodel().getEntityDescriptor( entityName );
156+
final SessionFactoryImplementor factory =
157+
persistenceContext.getSession().getFactory();
158+
this.persister =
159+
factory == null
160+
? null
161+
: factory.getMappingMetamodel().getEntityDescriptor( entityName );
152162
this.id = id;
153163
setCompressedValue( STATUS, status );
154164
setCompressedValue( PREVIOUS_STATUS, previousStatus );
@@ -159,7 +169,8 @@ protected AbstractEntityEntry(
159169
setCompressedValue( EXISTS_IN_DATABASE, existsInDatabase );
160170
setCompressedValue( IS_BEING_REPLICATED, isBeingReplicated );
161171
this.rowId = null; // this is equivalent to the old behavior...
162-
this.persistenceContext = persistenceContext;
172+
// don't store PersistenceContext for immutable entity, see HHH-10251
173+
this.persistenceContext = mutable ? persistenceContext : null;
163174
}
164175

165176
@Override
@@ -169,10 +180,14 @@ public LockMode getLockMode() {
169180

170181
@Override
171182
public void setLockMode(LockMode lockMode) {
183+
if ( lockMode.greaterThan( LockMode.READ )
184+
&& persister!=null && !persister.isMutable() ) {
185+
throw new UnsupportedLockAttemptException( "Lock mode " + lockMode
186+
+ " not supported for read-only entity" );
187+
}
172188
setCompressedValue( LOCK_MODE, lockMode );
173189
}
174190

175-
176191
@Override
177192
public Status getStatus() {
178193
return getCompressedValue( STATUS );
@@ -197,12 +212,12 @@ public void setStatus(Status status) {
197212
}
198213

199214
@Override
200-
public final Object getId() {
215+
public Object getId() {
201216
return id;
202217
}
203218

204219
@Override
205-
public final Object[] getLoadedState() {
220+
public Object[] getLoadedState() {
206221
return loadedState;
207222
}
208223

@@ -232,7 +247,7 @@ public boolean isExistsInDatabase() {
232247
}
233248

234249
@Override
235-
public final Object getVersion() {
250+
public Object getVersion() {
236251
return version;
237252
}
238253

@@ -242,7 +257,7 @@ public void postInsert(Object version) {
242257
}
243258

244259
@Override
245-
public final EntityPersister getPersister() {
260+
public EntityPersister getPersister() {
246261
return persister;
247262
}
248263

@@ -283,8 +298,8 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
283298
persister.setValue( entity, persister.getVersionProperty(), nextVersion );
284299
}
285300

286-
processIfSelfDirtinessTracker( entity, AbstractEntityEntry::clearDirtyAttributes );
287-
processIfManagedEntity( entity, AbstractEntityEntry::useTracker );
301+
processIfSelfDirtinessTracker( entity, EntityEntryImpl::clearDirtyAttributes );
302+
processIfManagedEntity( entity, EntityEntryImpl::useTracker );
288303

289304
final SharedSessionContractImplementor session = getPersistenceContext().getSession();
290305
session.getFactory().getCustomEntityDirtinessStrategy()
@@ -437,11 +452,11 @@ public void setReadOnly(boolean readOnly, Object entity) {
437452
setStatus( READ_ONLY );
438453
loadedState = null;
439454
}
455+
else if ( !persister.isMutable() ) {
456+
throw new IllegalStateException( "Cannot make an entity of immutable type '"
457+
+ persister.getEntityName() + "' modifiable" );
458+
}
440459
else {
441-
if ( ! persister.isMutable() ) {
442-
throw new IllegalStateException( "Cannot make an entity of immutable type '"
443-
+ persister.getEntityName() + "' modifiable" );
444-
}
445460
setStatus( MANAGED );
446461
loadedState = persister.getValues( entity );
447462
TypeHelper.deepCopy(
@@ -483,19 +498,51 @@ public String toString() {
483498
@Override
484499
public void serialize(ObjectOutputStream oos) throws IOException {
485500
final Status previousStatus = getPreviousStatus();
486-
oos.writeObject( getEntityName() );
501+
final String entityName = getEntityName();
502+
oos.writeUTF( entityName == null ? "" : entityName );
487503
oos.writeObject( id );
488-
oos.writeObject( getStatus().name() );
489-
oos.writeObject( (previousStatus == null ? "" : previousStatus.name()) );
504+
oos.writeInt( getStatus().ordinal() );
505+
oos.writeInt( previousStatus == null ? -1 : previousStatus.ordinal() );
490506
// todo : potentially look at optimizing these two arrays
491507
oos.writeObject( loadedState );
492508
oos.writeObject( getDeletedState() );
493509
oos.writeObject( version );
494-
oos.writeObject( getLockMode().toString() );
510+
oos.writeInt( getLockMode().ordinal() );
495511
oos.writeBoolean( isExistsInDatabase() );
496512
oos.writeBoolean( isBeingReplicated() );
513+
oos.writeBoolean( persister == null || persister.isMutable() );
497514
}
498515

516+
/**
517+
* Custom deserialization routine used during deserialization
518+
* of a {@link PersistenceContext} for increased performance.
519+
*
520+
* @param ois The stream from which to read the entry
521+
* @param persistenceContext The context being deserialized
522+
*
523+
* @return The deserialized {@code EntityEntry}
524+
*
525+
* @throws IOException If a stream error occurs
526+
* @throws ClassNotFoundException If any of the classes declared
527+
* in the stream cannot be found
528+
*/
529+
public static EntityEntry deserialize(ObjectInputStream ois, PersistenceContext persistenceContext)
530+
throws IOException, ClassNotFoundException {
531+
return new EntityEntryImpl(
532+
nullIfEmpty( ois.readUTF() ),
533+
ois.readObject(),
534+
Status.fromOrdinal( ois.readInt() ),
535+
Status.fromOrdinal( ois.readInt() ),
536+
(Object[]) ois.readObject(),
537+
(Object[]) ois.readObject(),
538+
ois.readObject(),
539+
LockMode.values()[ ois.readInt() ],
540+
ois.readBoolean(),
541+
ois.readBoolean(),
542+
ois.readBoolean(),
543+
persistenceContext
544+
);
545+
}
499546

500547
@Override
501548
public void addExtraState(EntityEntryExtraState extraState) {
@@ -520,7 +567,10 @@ public <T extends EntityEntryExtraState> T getExtraState(Class<T> extraStateType
520567
}
521568
}
522569

523-
public PersistenceContext getPersistenceContext(){
570+
public PersistenceContext getPersistenceContext() {
571+
if ( persistenceContext == null ) {
572+
throw new UnsupportedOperationException( "PersistenceContext not available for immutable entity" );
573+
}
524574
return persistenceContext;
525575
}
526576

@@ -533,7 +583,7 @@ public PersistenceContext getPersistenceContext(){
533583
* the value to store; The caller must make sure that it matches
534584
* the given identifier
535585
*/
536-
protected <E extends Enum<E>> void setCompressedValue(EnumState<E> state, E value) {
586+
<E extends Enum<E>> void setCompressedValue(EnumState<E> state, E value) {
537587
// reset the bits for the given property to 0
538588
compressedState &= state.getUnsetMask();
539589
// store the numeric representation of the enum value at the right offset
@@ -547,7 +597,7 @@ protected <E extends Enum<E>> void setCompressedValue(EnumState<E> state, E valu
547597
* identifies the value to store
548598
* @return the current value of the specified property
549599
*/
550-
protected <E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
600+
<E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
551601
// restore the numeric value from the bits at the right offset and return the corresponding enum constant
552602
final int index = ( ( compressedState & state.getMask() ) >> state.getOffset() ) - 1;
553603
return index == - 1 ? null : state.getEnumConstants()[index];
@@ -561,7 +611,7 @@ protected <E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
561611
* @param value
562612
* the value to store
563613
*/
564-
protected void setCompressedValue(BooleanState state, boolean value) {
614+
void setCompressedValue(BooleanState state, boolean value) {
565615
compressedState &= state.getUnsetMask();
566616
compressedState |= ( state.getValue( value ) << state.getOffset() );
567617
}
@@ -573,7 +623,7 @@ protected void setCompressedValue(BooleanState state, boolean value) {
573623
* identifies the value to store
574624
* @return the current value of the specified flag
575625
*/
576-
protected boolean getCompressedValue(BooleanState state) {
626+
boolean getCompressedValue(BooleanState state) {
577627
return ( ( compressedState & state.getMask() ) >> state.getOffset() ) == 1;
578628
}
579629

@@ -582,7 +632,7 @@ protected boolean getCompressedValue(BooleanState state) {
582632
*
583633
* @author Gunnar Morling
584634
*/
585-
protected static class EnumState<E extends Enum<E>> {
635+
static class EnumState<E extends Enum<E>> {
586636

587637
protected static final EnumState<LockMode> LOCK_MODE = new EnumState<>( 0, LockMode.class );
588638
protected static final EnumState<Status> STATUS = new EnumState<>( 4, Status.class );
@@ -593,11 +643,11 @@ protected static class EnumState<E extends Enum<E>> {
593643
protected final int mask;
594644
protected final int unsetMask;
595645

596-
private EnumState(int offset, Class<E> enumType) {
646+
EnumState(int offset, Class<E> enumType) {
597647
final E[] enumConstants = enumType.getEnumConstants();
598648

599649
// In case any of the enums cannot be stored in 4 bits anymore,
600-
// we'd have to re-structure the compressed state int
650+
// we'd have to restructure the compressed state int
601651
if ( enumConstants.length > 15 ) {
602652
throw new AssertionFailure( "Cannot store enum type " + enumType.getName()
603653
+ " in compressed state as it has too many values." );
@@ -654,7 +704,7 @@ private E[] getEnumConstants() {
654704
*
655705
* @author Gunnar Morling
656706
*/
657-
protected enum BooleanState {
707+
enum BooleanState {
658708

659709
EXISTS_IN_DATABASE(13),
660710
IS_BEING_REPLICATED(14);

0 commit comments

Comments
 (0)