Skip to content

Commit 676a633

Browse files
committed
HHH-19273 Don't trust state of read-only associations for initializtion
1 parent d9ea0a2 commit 676a633

19 files changed

+3189
-268
lines changed

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java

Lines changed: 58 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -131,100 +131,88 @@ protected void resolveInstanceFromIdentifier(Data data) {
131131

132132
@Override
133133
public void resolveInstance(Object instance, Data data) {
134-
if ( instance == null ) {
134+
final boolean identifierResolved = resolveIdentifier( instance, data );
135+
if ( data.entityIdentifier == null ) {
135136
data.setState( State.MISSING );
136137
data.entityKey = null;
137138
data.setInstance( null );
138-
return;
139139
}
140-
final var rowProcessingState = data.getRowProcessingState();
141-
final var session = rowProcessingState.getSession();
142-
final var persistenceContext = session.getPersistenceContextInternal();
143-
// Only need to extract the identifier if the identifier has a many to one
144-
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
145-
data.entityIdentifier = null;
146-
if ( lazyInitializer == null ) {
147-
// Entity is most probably initialized
148-
data.setInstance( instance );
149-
final PersistentAttributeInterceptor interceptor;
150-
if ( concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
151-
&& isPersistentAttributeInterceptable( instance )
152-
&& ( interceptor = getAttributeInterceptor( instance ) ) instanceof EnhancementAsProxyLazinessInterceptor ) {
153-
final EnhancementAsProxyLazinessInterceptor enhancementInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
154-
if ( enhancementInterceptor.isInitialized() ) {
155-
data.setState( State.INITIALIZED );
140+
else {
141+
final var rowProcessingState = data.getRowProcessingState();
142+
final var session = rowProcessingState.getSession();
143+
final var persistenceContext = session.getPersistenceContextInternal();
144+
final LazyInitializer lazyInitializer = extractLazyInitializer( instance );
145+
if ( lazyInitializer == null ) {
146+
// Entity is most probably initialized
147+
final PersistentAttributeInterceptor interceptor;
148+
if ( concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading()
149+
&& isPersistentAttributeInterceptable( instance )
150+
&& ( interceptor = getAttributeInterceptor( instance ) ) instanceof EnhancementAsProxyLazinessInterceptor ) {
151+
final EnhancementAsProxyLazinessInterceptor enhancementInterceptor = (EnhancementAsProxyLazinessInterceptor) interceptor;
152+
if ( enhancementInterceptor.isInitialized() ) {
153+
data.setState( State.INITIALIZED );
154+
}
155+
else {
156+
data.setState( State.RESOLVED );
157+
}
156158
}
157159
else {
160+
// If the entity initializer is null, we know the entity is fully initialized,
161+
// otherwise it will be initialized by some other initializer
158162
data.setState( State.RESOLVED );
159-
data.entityIdentifier = enhancementInterceptor.getIdentifier();
160163
}
161164
}
162-
else {
163-
// If the entity initializer is null, we know the entity is fully initialized,
164-
// otherwise it will be initialized by some other initializer
165+
else if ( lazyInitializer.isUninitialized() ) {
165166
data.setState( State.RESOLVED );
166-
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
167167
}
168-
if ( data.entityIdentifier == null ) {
169-
data.entityIdentifier = concreteDescriptor.getIdentifier( instance, session );
168+
else {
169+
// Entity is initialized
170+
data.setState( State.INITIALIZED );
170171
}
171-
}
172-
else if ( lazyInitializer.isUninitialized() ) {
173-
data.setState( State.RESOLVED );
174-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
175-
}
176-
else {
177-
// Entity is initialized
178-
data.setState( State.INITIALIZED );
179-
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
180-
data.setInstance( lazyInitializer.getImplementation() );
181-
}
182172

183-
data.entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
184-
final var entityHolder = persistenceContext.getEntityHolder(
185-
data.entityKey
186-
);
173+
data.entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor );
174+
final var entityHolder = persistenceContext.getEntityHolder( data.entityKey );
187175

188-
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
189-
// the existing entity instance is detached or transient
190-
if ( entityHolder != null ) {
191-
final var managed = entityHolder.getManagedObject();
192-
data.setInstance( managed );
193-
data.entityKey = entityHolder.getEntityKey();
194-
data.entityIdentifier = data.entityKey.getIdentifier();
195-
if ( entityHolder.isInitialized() ) {
196-
data.setState( State.INITIALIZED );
176+
if ( entityHolder == null || instance == null
177+
|| entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
178+
// the existing entity instance is detached or transient
179+
if ( entityHolder != null ) {
180+
final var managed = entityHolder.getManagedObject();
181+
data.setInstance( managed );
182+
data.entityKey = entityHolder.getEntityKey();
183+
data.entityIdentifier = data.entityKey.getIdentifier();
184+
data.setState( entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED );
197185
}
198186
else {
199187
data.setState( State.RESOLVED );
200188
}
201189
}
202190
else {
203-
data.setState( State.RESOLVED );
191+
data.setInstance( instance );
204192
}
205-
}
206193

207-
if ( data.getState() == State.RESOLVED ) {
208-
// similar to resolveInstanceFromIdentifier, but we already have the holder here
209-
if ( data.batchDisabled ) {
210-
initialize( data, entityHolder, session, persistenceContext );
211-
}
212-
else if ( entityHolder == null || !entityHolder.isEventuallyInitialized() ) {
213-
// need to add the key to the batch queue only when the entity has not been already loaded or
214-
// there isn't another initializer that is loading it
215-
registerResolutionListener( data );
216-
registerToBatchFetchQueue( data );
194+
if ( data.getState() == State.RESOLVED ) {
195+
// similar to resolveInstanceFromIdentifier, but we already have the holder here
196+
if ( data.batchDisabled ) {
197+
initialize( data, entityHolder, session, persistenceContext );
198+
}
199+
else if ( entityHolder == null || !entityHolder.isEventuallyInitialized() ) {
200+
// need to add the key to the batch queue only when the entity has not been already loaded or
201+
// there isn't another initializer that is loading it
202+
registerResolutionListener( data );
203+
registerToBatchFetchQueue( data );
204+
}
217205
}
218-
}
219206

220-
if ( keyIsEager ) {
221-
final Initializer<?> initializer = keyAssembler.getInitializer();
222-
assert initializer != null;
223-
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
224-
}
225-
else if ( rowProcessingState.needsResolveState() ) {
226-
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
227-
keyAssembler.resolveState( rowProcessingState );
207+
if ( keyIsEager && !identifierResolved ) {
208+
final Initializer<?> initializer = keyAssembler.getInitializer();
209+
assert initializer != null;
210+
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
211+
}
212+
else if ( rowProcessingState.needsResolveState() && !identifierResolved ) {
213+
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
214+
keyAssembler.resolveState( rowProcessingState );
215+
}
228216
}
229217
}
230218

hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1616
import org.hibernate.internal.log.LoggingHelper;
1717
import org.hibernate.metamodel.mapping.AttributeMapping;
18+
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
1819
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
20+
import org.hibernate.metamodel.mapping.DiscriminatorMapping;
1921
import org.hibernate.metamodel.mapping.ModelPart;
2022
import org.hibernate.persister.entity.EntityPersister;
2123
import org.hibernate.proxy.LazyInitializer;
@@ -53,6 +55,7 @@ public class DiscriminatedEntityInitializer
5355
private final boolean resultInitializer;
5456
private final boolean keyIsEager;
5557
private final boolean hasLazySubInitializer;
58+
protected final boolean isReadOnly;
5659

5760
public static class DiscriminatedEntityInitializerData extends InitializerData {
5861
protected EntityPersister concreteDescriptor;
@@ -90,6 +93,16 @@ public DiscriminatedEntityInitializer(
9093
this.keyIsEager = initializer.isEager();
9194
this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers();
9295
}
96+
this.isReadOnly = isReadOnly( fetchedPart );
97+
}
98+
99+
private static boolean isReadOnly(DiscriminatedAssociationModelPart fetchedPart) {
100+
final BasicValuedModelPart keyPart = fetchedPart.getKeyPart();
101+
final DiscriminatorMapping discriminatorMapping = fetchedPart.getDiscriminatorMapping();
102+
return !keyPart.isInsertable()
103+
&& !keyPart.isUpdateable()
104+
&& !discriminatorMapping.isInsertable()
105+
&& !discriminatorMapping.isUpdateable();
93106
}
94107

95108
@Override
@@ -201,13 +214,16 @@ else if ( instance == null ) {
201214
) );
202215
}
203216

204-
@Override
205-
public void resolveInstance(Object instance, DiscriminatedEntityInitializerData data) {
206-
if ( instance == null ) {
207-
data.setState( State.MISSING );
217+
protected boolean resolveIdentifier(Object instance, DiscriminatedEntityInitializerData data) {
218+
final boolean identifierResolved;
219+
if ( instance == null && !isReadOnly ) {
208220
data.entityIdentifier = null;
209-
data.concreteDescriptor = null;
210-
data.setInstance( null );
221+
identifierResolved = true;
222+
}
223+
else if ( isReadOnly ) {
224+
// When the mapping is read-only, we can't trust the state of the persistence context
225+
resolveKey( data );
226+
identifierResolved = true;
211227
}
212228
else {
213229
final var rowProcessingState = data.getRowProcessingState();
@@ -217,27 +233,43 @@ public void resolveInstance(Object instance, DiscriminatedEntityInitializerData
217233
data.setState( State.INITIALIZED );
218234
data.concreteDescriptor = session.getEntityPersister( null, instance );
219235
data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session );
236+
identifierResolved = false;
220237
}
221238
else if ( lazyInitializer.isUninitialized() ) {
222239
data.setState( eager ? State.RESOLVED : State.INITIALIZED );
223240
// Read the discriminator from the result set if necessary
224241
final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState );
225242
data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister();
226243
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
244+
identifierResolved = true;
227245
}
228246
else {
229247
data.setState( State.INITIALIZED );
230248
data.concreteDescriptor = session.getEntityPersister( null, lazyInitializer.getImplementation() );
231249
data.entityIdentifier = lazyInitializer.getInternalIdentifier();
250+
identifierResolved = false;
232251
}
252+
assert data.entityIdentifier != null;
253+
}
254+
return identifierResolved;
255+
}
233256

234-
257+
@Override
258+
public void resolveInstance(Object instance, DiscriminatedEntityInitializerData data) {
259+
final boolean identifierResolved = resolveIdentifier( instance, data );
260+
if ( data.entityIdentifier == null ) {
261+
data.setState( State.MISSING );
262+
data.concreteDescriptor = null;
263+
data.setInstance( null );
264+
}
265+
else {
266+
final var rowProcessingState = data.getRowProcessingState();
267+
final var session = rowProcessingState.getSession();
235268
final var entityKey = new EntityKey( data.entityIdentifier, data.concreteDescriptor );
236-
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder(
237-
entityKey
238-
);
269+
final var entityHolder = session.getPersistenceContextInternal().getEntityHolder( entityKey );
239270

240-
if ( entityHolder == null || entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
271+
if ( entityHolder == null || instance == null
272+
|| entityHolder.getEntity() != instance && entityHolder.getProxy() != instance ) {
241273
// the existing entity instance is detached or transient
242274
if ( entityHolder != null ) {
243275
final var managed = entityHolder.getManagedObject();
@@ -254,15 +286,18 @@ else if ( lazyInitializer.isUninitialized() ) {
254286
data.setInstance( instance );
255287
}
256288

257-
if ( keyIsEager ) {
289+
if ( keyIsEager && !identifierResolved ) {
258290
final Initializer<?> initializer = keyValueAssembler.getInitializer();
259291
assert initializer != null;
260292
initializer.resolveInstance( data.entityIdentifier, rowProcessingState );
293+
if ( rowProcessingState.needsResolveState() ) {
294+
discriminatorValueAssembler.resolveState( rowProcessingState );
295+
}
261296
}
262-
else if ( rowProcessingState.needsResolveState() ) {
297+
else if ( rowProcessingState.needsResolveState() && !identifierResolved ) {
263298
// Resolve the state of the identifier if result caching is enabled and this is not a query cache hit
264-
discriminatorValueAssembler.resolveState( rowProcessingState );
265299
keyValueAssembler.resolveState( rowProcessingState );
300+
discriminatorValueAssembler.resolveState( rowProcessingState );
266301
}
267302
}
268303
}

0 commit comments

Comments
 (0)