5
5
package org .hibernate .engine .internal ;
6
6
7
7
import java .io .IOException ;
8
+ import java .io .ObjectInputStream ;
8
9
import java .io .ObjectOutputStream ;
9
10
import java .io .Serializable ;
10
11
11
12
import org .hibernate .AssertionFailure ;
12
13
import org .hibernate .CustomEntityDirtinessStrategy ;
13
14
import org .hibernate .HibernateException ;
14
15
import org .hibernate .LockMode ;
16
+ import org .hibernate .UnsupportedLockAttemptException ;
15
17
import org .hibernate .bytecode .enhance .spi .interceptor .EnhancementAsProxyLazinessInterceptor ;
16
18
import org .hibernate .collection .spi .PersistentCollection ;
17
19
import org .hibernate .engine .spi .EntityEntry ;
33
35
import org .checkerframework .checker .nullness .qual .Nullable ;
34
36
35
37
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 ;
41
43
import static org .hibernate .engine .internal .ManagedTypeHelper .asManagedEntity ;
42
44
import static org .hibernate .engine .internal .ManagedTypeHelper .asPersistentAttributeInterceptable ;
43
45
import static org .hibernate .engine .internal .ManagedTypeHelper .asSelfDirtinessTracker ;
52
54
import static org .hibernate .engine .spi .Status .MANAGED ;
53
55
import static org .hibernate .engine .spi .Status .READ_ONLY ;
54
56
import static org .hibernate .engine .spi .Status .SAVING ;
57
+ import static org .hibernate .internal .util .StringHelper .nullIfEmpty ;
55
58
import static org .hibernate .pretty .MessageHelper .infoString ;
56
59
import static org .hibernate .proxy .HibernateProxy .extractLazyInitializer ;
57
60
63
66
* @author Gunnar Morling
64
67
* @author Sanne Grinovero
65
68
*/
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 ;
77
80
78
81
/**
79
82
* 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 {
102
105
*/
103
106
private transient int compressedState ;
104
107
105
- public AbstractEntityEntry (
108
+ public EntityEntryImpl (
106
109
final Status status ,
107
110
final Object [] loadedState ,
108
111
final Object rowId ,
@@ -127,14 +130,17 @@ public AbstractEntityEntry(
127
130
setCompressedValue ( LOCK_MODE , lockMode );
128
131
setCompressedValue ( IS_BEING_REPLICATED , disableVersionIncrement );
129
132
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 ;
131
138
}
132
139
133
140
/**
134
- * This for is used during custom deserialization handling
141
+ * Overloaded form used during custom deserialization
135
142
*/
136
- protected AbstractEntityEntry (
137
- final SessionFactoryImplementor factory ,
143
+ private EntityEntryImpl (
138
144
final String entityName ,
139
145
final Object id ,
140
146
final Status status ,
@@ -145,10 +151,14 @@ protected AbstractEntityEntry(
145
151
final LockMode lockMode ,
146
152
final boolean existsInDatabase ,
147
153
final boolean isBeingReplicated ,
154
+ final boolean mutable ,
148
155
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 );
152
162
this .id = id ;
153
163
setCompressedValue ( STATUS , status );
154
164
setCompressedValue ( PREVIOUS_STATUS , previousStatus );
@@ -159,7 +169,8 @@ protected AbstractEntityEntry(
159
169
setCompressedValue ( EXISTS_IN_DATABASE , existsInDatabase );
160
170
setCompressedValue ( IS_BEING_REPLICATED , isBeingReplicated );
161
171
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 ;
163
174
}
164
175
165
176
@ Override
@@ -169,10 +180,14 @@ public LockMode getLockMode() {
169
180
170
181
@ Override
171
182
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
+ }
172
188
setCompressedValue ( LOCK_MODE , lockMode );
173
189
}
174
190
175
-
176
191
@ Override
177
192
public Status getStatus () {
178
193
return getCompressedValue ( STATUS );
@@ -197,12 +212,12 @@ public void setStatus(Status status) {
197
212
}
198
213
199
214
@ Override
200
- public final Object getId () {
215
+ public Object getId () {
201
216
return id ;
202
217
}
203
218
204
219
@ Override
205
- public final Object [] getLoadedState () {
220
+ public Object [] getLoadedState () {
206
221
return loadedState ;
207
222
}
208
223
@@ -232,7 +247,7 @@ public boolean isExistsInDatabase() {
232
247
}
233
248
234
249
@ Override
235
- public final Object getVersion () {
250
+ public Object getVersion () {
236
251
return version ;
237
252
}
238
253
@@ -242,7 +257,7 @@ public void postInsert(Object version) {
242
257
}
243
258
244
259
@ Override
245
- public final EntityPersister getPersister () {
260
+ public EntityPersister getPersister () {
246
261
return persister ;
247
262
}
248
263
@@ -283,8 +298,8 @@ public void postUpdate(Object entity, Object[] updatedState, Object nextVersion)
283
298
persister .setValue ( entity , persister .getVersionProperty (), nextVersion );
284
299
}
285
300
286
- processIfSelfDirtinessTracker ( entity , AbstractEntityEntry ::clearDirtyAttributes );
287
- processIfManagedEntity ( entity , AbstractEntityEntry ::useTracker );
301
+ processIfSelfDirtinessTracker ( entity , EntityEntryImpl ::clearDirtyAttributes );
302
+ processIfManagedEntity ( entity , EntityEntryImpl ::useTracker );
288
303
289
304
final SharedSessionContractImplementor session = getPersistenceContext ().getSession ();
290
305
session .getFactory ().getCustomEntityDirtinessStrategy ()
@@ -437,11 +452,11 @@ public void setReadOnly(boolean readOnly, Object entity) {
437
452
setStatus ( READ_ONLY );
438
453
loadedState = null ;
439
454
}
455
+ else if ( !persister .isMutable () ) {
456
+ throw new IllegalStateException ( "Cannot make an entity of immutable type '"
457
+ + persister .getEntityName () + "' modifiable" );
458
+ }
440
459
else {
441
- if ( ! persister .isMutable () ) {
442
- throw new IllegalStateException ( "Cannot make an entity of immutable type '"
443
- + persister .getEntityName () + "' modifiable" );
444
- }
445
460
setStatus ( MANAGED );
446
461
loadedState = persister .getValues ( entity );
447
462
TypeHelper .deepCopy (
@@ -483,19 +498,51 @@ public String toString() {
483
498
@ Override
484
499
public void serialize (ObjectOutputStream oos ) throws IOException {
485
500
final Status previousStatus = getPreviousStatus ();
486
- oos .writeObject ( getEntityName () );
501
+ final String entityName = getEntityName ();
502
+ oos .writeUTF ( entityName == null ? "" : entityName );
487
503
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 ( ) );
490
506
// todo : potentially look at optimizing these two arrays
491
507
oos .writeObject ( loadedState );
492
508
oos .writeObject ( getDeletedState () );
493
509
oos .writeObject ( version );
494
- oos .writeObject ( getLockMode ().toString () );
510
+ oos .writeInt ( getLockMode ().ordinal () );
495
511
oos .writeBoolean ( isExistsInDatabase () );
496
512
oos .writeBoolean ( isBeingReplicated () );
513
+ oos .writeBoolean ( persister == null || persister .isMutable () );
497
514
}
498
515
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
+ }
499
546
500
547
@ Override
501
548
public void addExtraState (EntityEntryExtraState extraState ) {
@@ -520,7 +567,10 @@ public <T extends EntityEntryExtraState> T getExtraState(Class<T> extraStateType
520
567
}
521
568
}
522
569
523
- public PersistenceContext getPersistenceContext (){
570
+ public PersistenceContext getPersistenceContext () {
571
+ if ( persistenceContext == null ) {
572
+ throw new UnsupportedOperationException ( "PersistenceContext not available for immutable entity" );
573
+ }
524
574
return persistenceContext ;
525
575
}
526
576
@@ -533,7 +583,7 @@ public PersistenceContext getPersistenceContext(){
533
583
* the value to store; The caller must make sure that it matches
534
584
* the given identifier
535
585
*/
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 ) {
537
587
// reset the bits for the given property to 0
538
588
compressedState &= state .getUnsetMask ();
539
589
// 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
547
597
* identifies the value to store
548
598
* @return the current value of the specified property
549
599
*/
550
- protected <E extends Enum <E >> E getCompressedValue (EnumState <E > state ) {
600
+ <E extends Enum <E >> E getCompressedValue (EnumState <E > state ) {
551
601
// restore the numeric value from the bits at the right offset and return the corresponding enum constant
552
602
final int index = ( ( compressedState & state .getMask () ) >> state .getOffset () ) - 1 ;
553
603
return index == - 1 ? null : state .getEnumConstants ()[index ];
@@ -561,7 +611,7 @@ protected <E extends Enum<E>> E getCompressedValue(EnumState<E> state) {
561
611
* @param value
562
612
* the value to store
563
613
*/
564
- protected void setCompressedValue (BooleanState state , boolean value ) {
614
+ void setCompressedValue (BooleanState state , boolean value ) {
565
615
compressedState &= state .getUnsetMask ();
566
616
compressedState |= ( state .getValue ( value ) << state .getOffset () );
567
617
}
@@ -573,7 +623,7 @@ protected void setCompressedValue(BooleanState state, boolean value) {
573
623
* identifies the value to store
574
624
* @return the current value of the specified flag
575
625
*/
576
- protected boolean getCompressedValue (BooleanState state ) {
626
+ boolean getCompressedValue (BooleanState state ) {
577
627
return ( ( compressedState & state .getMask () ) >> state .getOffset () ) == 1 ;
578
628
}
579
629
@@ -582,7 +632,7 @@ protected boolean getCompressedValue(BooleanState state) {
582
632
*
583
633
* @author Gunnar Morling
584
634
*/
585
- protected static class EnumState <E extends Enum <E >> {
635
+ static class EnumState <E extends Enum <E >> {
586
636
587
637
protected static final EnumState <LockMode > LOCK_MODE = new EnumState <>( 0 , LockMode .class );
588
638
protected static final EnumState <Status > STATUS = new EnumState <>( 4 , Status .class );
@@ -593,11 +643,11 @@ protected static class EnumState<E extends Enum<E>> {
593
643
protected final int mask ;
594
644
protected final int unsetMask ;
595
645
596
- private EnumState (int offset , Class <E > enumType ) {
646
+ EnumState (int offset , Class <E > enumType ) {
597
647
final E [] enumConstants = enumType .getEnumConstants ();
598
648
599
649
// 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
601
651
if ( enumConstants .length > 15 ) {
602
652
throw new AssertionFailure ( "Cannot store enum type " + enumType .getName ()
603
653
+ " in compressed state as it has too many values." );
@@ -654,7 +704,7 @@ private E[] getEnumConstants() {
654
704
*
655
705
* @author Gunnar Morling
656
706
*/
657
- protected enum BooleanState {
707
+ enum BooleanState {
658
708
659
709
EXISTS_IN_DATABASE (13 ),
660
710
IS_BEING_REPLICATED (14 );
0 commit comments