Skip to content

Commit c7737de

Browse files
committed
Optimize initialization of EnhancementAsProxyLazinessInterceptor
1 parent 3776824 commit c7737de

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,74 +18,34 @@
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
public String getEntityName() {
88-
return entityName;
48+
return meta.entityName;
8949
}
9050

9151
public EntityKey getEntityKey() {
@@ -101,7 +61,7 @@ protected Object handleRead(Object target, String attributeName, Object value) {
10161

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

@@ -119,14 +79,10 @@ private Object read(
11979
final Object[] writtenAttributeValues;
12080
final AttributeMapping[] writtenAttributeMappings;
12181

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

12884
// enhancement has dirty-tracking available and at least one attribute was explicitly set
129-
85+
final EntityPersister entityPersister = meta.persister;
13086
if ( writtenFieldNames.contains( attributeName ) ) {
13187
// the requested attribute was one of the attributes explicitly set,
13288
// we can just return the explicitly-set value
@@ -161,7 +117,7 @@ private Object read(
161117
for ( int i = 0; i < writtenAttributeMappings.length; i++ ) {
162118
final AttributeMapping attribute = writtenAttributeMappings[i];
163119
attribute.setValue(target, writtenAttributeValues[i] );
164-
if ( inLineDirtyChecking ) {
120+
if ( meta.inLineDirtyChecking ) {
165121
asSelfDirtinessTracker(target).$$_hibernate_trackChange( attribute.getAttributeName() );
166122
}
167123
}
@@ -173,6 +129,7 @@ private Object read(
173129

174130
private Object extractIdValue(Object target, String attributeName) {
175131
// access to the id or part of it for non-aggregated cid
132+
final CompositeType nonAggregatedCidMapper = meta.nonAggregatedCidMapper;
176133
if ( nonAggregatedCidMapper == null ) {
177134
return getIdentifier();
178135
}
@@ -217,17 +174,13 @@ public Object forceInitialize(
217174
);
218175
}
219176

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

230-
return persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
183+
return meta.persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
231184
}
232185

233186
@Override
@@ -236,19 +189,19 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
236189
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
237190
}
238191

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

@@ -261,8 +214,8 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
261214
return newValue;
262215
}
263216

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

274-
if ( inLineDirtyChecking ) {
227+
if ( meta.inLineDirtyChecking ) {
275228
asSelfDirtinessTracker( target ).$$_hibernate_trackChange( attributeName );
276229
}
277230
}
@@ -309,7 +262,7 @@ public boolean isAttributeLoaded(String fieldName) {
309262
throw new UnsupportedOperationException( "Call to EnhancementAsProxyLazinessInterceptor#isAttributeLoaded on an interceptor which is marked as initialized" );
310263
}
311264
// Only fields from the identifier are loaded (until it's initialized)
312-
return identifierAttributeNames.contains( fieldName );
265+
return meta.identifierAttributeNames.contains( fieldName );
313266
}
314267

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

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)