Skip to content

Commit 92a3ec2

Browse files
committed
Optimize initialization of EnhancementAsProxyLazinessInterceptor
1 parent 99e98cf commit 92a3ec2

File tree

2 files changed

+97
-73
lines changed

2 files changed

+97
-73
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java

Lines changed: 74 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,75 +18,35 @@
1818
import org.hibernate.type.CompositeType;
1919
import org.hibernate.type.Type;
2020

21+
import static java.util.Collections.unmodifiableSet;
2122
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
2223
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
2324
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTrackerType;
25+
import static org.hibernate.internal.util.collections.CollectionHelper.toSmallSet;
2426

2527
/**
2628
* @author Steve Ebersole
2729
*/
2830
public class EnhancementAsProxyLazinessInterceptor extends AbstractInterceptor implements BytecodeLazyAttributeInterceptor {
29-
private final Set<String> identifierAttributeNames;
30-
private final CompositeType nonAggregatedCidMapper;
31-
private final String entityName;
3231

3332
private final EntityKey entityKey;
34-
35-
private final boolean inLineDirtyChecking;
33+
private final EntityRelatedState meta;
3634
private Set<String> writtenFieldNames;
37-
private Set<String> collectionAttributeNames;
38-
3935
private Status status;
4036

41-
private final boolean initializeBeforeWrite;
42-
4337
public EnhancementAsProxyLazinessInterceptor(
44-
String entityName,
45-
Set<String> identifierAttributeNames,
46-
CompositeType nonAggregatedCidMapper,
38+
EntityRelatedState meta,
4739
EntityKey entityKey,
4840
SharedSessionContractImplementor session) {
49-
this.entityName = entityName;
50-
51-
this.identifierAttributeNames = identifierAttributeNames;
52-
assert identifierAttributeNames != null;
53-
54-
this.nonAggregatedCidMapper = nonAggregatedCidMapper;
55-
assert nonAggregatedCidMapper != null || identifierAttributeNames.size() == 1;
56-
5741
this.entityKey = entityKey;
58-
setSession( session );
59-
60-
final EntityPersister entityPersister =
61-
session.getFactory().getMappingMetamodel()
62-
.getEntityDescriptor( entityName );
63-
if ( entityPersister.hasCollections() ) {
64-
final Type[] propertyTypes = entityPersister.getPropertyTypes();
65-
final String[] propertyNames = entityPersister.getPropertyNames();
66-
collectionAttributeNames = new HashSet<>();
67-
for ( int i = 0; i < propertyTypes.length; i++ ) {
68-
Type propertyType = propertyTypes[i];
69-
if ( propertyType instanceof CollectionType ) {
70-
collectionAttributeNames.add( propertyNames[i] );
71-
}
72-
}
73-
}
74-
75-
this.inLineDirtyChecking = isSelfDirtinessTrackerType( entityPersister.getMappedClass() );
76-
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to
77-
// initialize the entity because the precomputed update statement contains even not
78-
// dirty properties. And so we need all the values we have to initialize. Or, if it's
79-
// versioned, we need to fetch the current version.
80-
initializeBeforeWrite =
81-
!inLineDirtyChecking
82-
|| !entityPersister.getEntityMetamodel().isDynamicUpdate()
83-
|| entityPersister.isVersioned();
42+
this.meta = meta;
8443
status = Status.UNINITIALIZED;
44+
setSession( session );
8545
}
8646

8747
@Override
8848
public String getEntityName() {
89-
return entityName;
49+
return meta.entityName;
9050
}
9151

9252
public EntityKey getEntityKey() {
@@ -102,7 +62,7 @@ protected Object handleRead(Object target, String attributeName, Object value) {
10262

10363
// the attribute being read is an entity-id attribute
10464
// - we already know the id, return that
105-
if ( identifierAttributeNames.contains( attributeName ) ) {
65+
if ( meta.identifierAttributeNames.contains( attributeName ) ) {
10666
return extractIdValue( target, attributeName );
10767
}
10868

@@ -120,14 +80,10 @@ private Object read(
12080
final Object[] writtenAttributeValues;
12181
final AttributeMapping[] writtenAttributeMappings;
12282

123-
final EntityPersister entityPersister =
124-
session.getFactory().getMappingMetamodel()
125-
.getEntityDescriptor( getEntityName() );
126-
12783
if ( writtenFieldNames != null && !writtenFieldNames.isEmpty() ) {
12884

12985
// enhancement has dirty-tracking available and at least one attribute was explicitly set
130-
86+
final EntityPersister entityPersister = meta.persister;
13187
if ( writtenFieldNames.contains( attributeName ) ) {
13288
// the requested attribute was one of the attributes explicitly set,
13389
// we can just return the explicitly-set value
@@ -162,7 +118,7 @@ private Object read(
162118
for ( int i = 0; i < writtenAttributeMappings.length; i++ ) {
163119
final AttributeMapping attribute = writtenAttributeMappings[i];
164120
attribute.setValue(target, writtenAttributeValues[i] );
165-
if ( inLineDirtyChecking ) {
121+
if ( meta.inLineDirtyChecking ) {
166122
asSelfDirtinessTracker(target).$$_hibernate_trackChange( attribute.getAttributeName() );
167123
}
168124
}
@@ -174,6 +130,7 @@ private Object read(
174130

175131
private Object extractIdValue(Object target, String attributeName) {
176132
// access to the id or part of it for non-aggregated cid
133+
final CompositeType nonAggregatedCidMapper = meta.nonAggregatedCidMapper;
177134
if ( nonAggregatedCidMapper == null ) {
178135
return getIdentifier();
179136
}
@@ -218,17 +175,13 @@ public Object forceInitialize(
218175
);
219176
}
220177

221-
final EntityPersister persister =
222-
session.getFactory().getMappingMetamodel()
223-
.getEntityDescriptor( getEntityName() );
224-
225178
if ( isTemporarySession ) {
226179
// Add an entry for this entity in the PC of the temp Session
227180
session.getPersistenceContext()
228181
.addEnhancedProxy( entityKey, asPersistentAttributeInterceptable( target ) );
229182
}
230183

231-
return persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
184+
return meta.persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
232185
}
233186

234187
@Override
@@ -237,19 +190,19 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
237190
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
238191
}
239192

240-
if ( identifierAttributeNames.contains( attributeName ) ) {
193+
if ( meta.identifierAttributeNames.contains( attributeName ) ) {
241194
// it is illegal for the identifier value to be changed. Normally Hibernate
242195
// validates this during flush. However, here it's dangerous to just allow the
243196
// new value to be set and continue on waiting for the flush for validation
244197
// because this interceptor manages the entity's entry in the PC itself. So
245198
// just do the check here up-front
246199
final boolean changed;
247-
if ( nonAggregatedCidMapper == null ) {
200+
if ( meta.nonAggregatedCidMapper == null ) {
248201
changed = ! entityKey.getPersister().getIdentifierType().isEqual( oldValue, newValue );
249202
}
250203
else {
251-
final int subAttrIndex = nonAggregatedCidMapper.getPropertyIndex( attributeName );
252-
final Type subAttrType = nonAggregatedCidMapper.getSubtypes()[subAttrIndex];
204+
final int subAttrIndex = meta.nonAggregatedCidMapper.getPropertyIndex( attributeName );
205+
final Type subAttrType = meta.nonAggregatedCidMapper.getSubtypes()[subAttrIndex];
253206
changed = ! subAttrType.isEqual( oldValue, newValue );
254207
}
255208

@@ -262,8 +215,8 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
262215
return newValue;
263216
}
264217

265-
if ( initializeBeforeWrite
266-
|| collectionAttributeNames != null && collectionAttributeNames.contains( attributeName ) ) {
218+
if ( meta.initializeBeforeWrite
219+
|| meta.collectionAttributeNames != null && meta.collectionAttributeNames.contains( attributeName ) ) {
267220
// we need to force-initialize the proxy - the fetch group to which the `attributeName` belongs
268221
try {
269222
forceInitialize( target, attributeName );
@@ -272,7 +225,7 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
272225
setInitialized();
273226
}
274227

275-
if ( inLineDirtyChecking ) {
228+
if ( meta.inLineDirtyChecking ) {
276229
asSelfDirtinessTracker( target ).$$_hibernate_trackChange( attributeName );
277230
}
278231
}
@@ -310,7 +263,7 @@ public boolean isAttributeLoaded(String fieldName) {
310263
throw new UnsupportedOperationException( "Call to EnhancementAsProxyLazinessInterceptor#isAttributeLoaded on an interceptor which is marked as initialized" );
311264
}
312265
// Only fields from the identifier are loaded (until it's initialized)
313-
return identifierAttributeNames.contains( fieldName );
266+
return meta.identifierAttributeNames.contains( fieldName );
314267
}
315268

316269
@Override
@@ -352,4 +305,58 @@ private enum Status {
352305
INITIALIZING,
353306
INITIALIZED
354307
}
308+
309+
/**
310+
* This is an helper object to group all state which relates to a particular entity type,
311+
* and which is needed for this interceptor.
312+
* Grouping such state allows for upfront construction as a per-entity singleton:
313+
* this reduces processing work on creation of an interceptor instance and is more
314+
* efficient from a point of view of memory usage and memory layout.
315+
*/
316+
public static class EntityRelatedState {
317+
318+
private final String entityName;
319+
private final CompositeType nonAggregatedCidMapper;
320+
private final Set<String> identifierAttributeNames;
321+
private final Set<String> collectionAttributeNames;
322+
private final boolean initializeBeforeWrite;
323+
private final boolean inLineDirtyChecking;
324+
private final EntityPersister persister;
325+
326+
public EntityRelatedState(EntityPersister persister,
327+
CompositeType nonAggregatedCidMapper,
328+
Set<String> identifierAttributeNames) {
329+
this.identifierAttributeNames = identifierAttributeNames;
330+
this.entityName = persister.getEntityName();
331+
this.nonAggregatedCidMapper = nonAggregatedCidMapper;
332+
this.persister = persister;
333+
assert nonAggregatedCidMapper != null || identifierAttributeNames.size() == 1;
334+
335+
if ( persister.hasCollections() ) {
336+
final Set<String> tmpCollectionAttributeNames = new HashSet<>();
337+
final Type[] propertyTypes = persister.getPropertyTypes();
338+
final String[] propertyNames = persister.getPropertyNames();
339+
for ( int i = 0; i < propertyTypes.length; i++ ) {
340+
Type propertyType = propertyTypes[i];
341+
if ( propertyType instanceof CollectionType ) {
342+
tmpCollectionAttributeNames.add( propertyNames[i] );
343+
}
344+
}
345+
this.collectionAttributeNames = toSmallSet( unmodifiableSet( tmpCollectionAttributeNames ) );
346+
}
347+
else {
348+
this.collectionAttributeNames = Collections.emptySet();
349+
}
350+
351+
this.inLineDirtyChecking = isSelfDirtinessTrackerType( persister.getMappedClass() );
352+
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to
353+
// initialize the entity because the precomputed update statement contains even not
354+
// dirty properties. And so we need all the values we have to initialize. Or, if it's
355+
// versioned, we need to fetch the current version.
356+
this.initializeBeforeWrite =
357+
!inLineDirtyChecking
358+
|| !persister.getEntityMetamodel().isDynamicUpdate()
359+
|| persister.isVersioned();
360+
}
361+
}
355362
}

hibernate-core/src/main/java/org/hibernate/bytecode/internal/BytecodeEnhancementMetadataPojoImpl.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ public static BytecodeEnhancementMetadata from(
7272
private final CompositeType nonAggregatedCidMapper;
7373
private final boolean enhancedForLazyLoading;
7474
private final LazyAttributesMetadata lazyAttributesMetadata;
75-
private final LazyAttributeLoadingInterceptor.EntityRelatedState loadingInterceptorState;
75+
private final LazyAttributeLoadingInterceptor.EntityRelatedState lazyAttributeLoadingInterceptorState;
76+
private volatile transient EnhancementAsProxyLazinessInterceptor.EntityRelatedState enhancementAsProxyInterceptorState;
7677

7778
BytecodeEnhancementMetadataPojoImpl(
7879
String entityName,
@@ -90,7 +91,7 @@ public static BytecodeEnhancementMetadata from(
9091
this.identifierAttributeNames = identifierAttributeNames;
9192
this.enhancedForLazyLoading = enhancedForLazyLoading;
9293
this.lazyAttributesMetadata = lazyAttributesMetadata;
93-
this.loadingInterceptorState = new LazyAttributeLoadingInterceptor.EntityRelatedState(
94+
this.lazyAttributeLoadingInterceptorState = new LazyAttributeLoadingInterceptor.EntityRelatedState(
9495
getEntityName(), lazyAttributesMetadata.getLazyAttributeNames() );
9596
}
9697

@@ -220,7 +221,7 @@ public LazyAttributeLoadingInterceptor injectInterceptor(
220221
);
221222
}
222223
final LazyAttributeLoadingInterceptor interceptor = new LazyAttributeLoadingInterceptor(
223-
this.loadingInterceptorState,
224+
this.lazyAttributeLoadingInterceptorState,
224225
identifier,
225226
session
226227
);
@@ -235,19 +236,35 @@ public void injectEnhancedEntityAsProxyInterceptor(
235236
Object entity,
236237
EntityKey entityKey,
237238
SharedSessionContractImplementor session) {
239+
EnhancementAsProxyLazinessInterceptor.EntityRelatedState meta = getEnhancementAsProxyLazinessInterceptorMetastate( session );
238240
injectInterceptor(
239241
entity,
240242
new EnhancementAsProxyLazinessInterceptor(
241-
entityName,
242-
identifierAttributeNames,
243-
nonAggregatedCidMapper,
243+
meta,
244244
entityKey,
245245
session
246246
),
247247
session
248248
);
249249
}
250250

251+
//This state object needs to be lazily initialized as it needs access to the Persister, but once
252+
//initialized it can be reused across multiple sessions.
253+
private EnhancementAsProxyLazinessInterceptor.EntityRelatedState getEnhancementAsProxyLazinessInterceptorMetastate(SharedSessionContractImplementor session) {
254+
EnhancementAsProxyLazinessInterceptor.EntityRelatedState state = this.enhancementAsProxyInterceptorState;
255+
if ( state == null ) {
256+
final EntityPersister entityPersister = session.getFactory().getMappingMetamodel()
257+
.getEntityDescriptor( entityName );
258+
state = new EnhancementAsProxyLazinessInterceptor.EntityRelatedState(
259+
entityPersister,
260+
nonAggregatedCidMapper,
261+
identifierAttributeNames
262+
);
263+
this.enhancementAsProxyInterceptorState = state;
264+
}
265+
return state;
266+
}
267+
251268
@Override
252269
public void injectInterceptor(
253270
Object entity,

0 commit comments

Comments
 (0)