HHH-14730 Avoid loading the same entity proxy twice for the same result set

This commit is contained in:
Andrea Boriero 2021-07-16 13:22:37 +02:00
parent 98e64579fa
commit 6c98441518
3 changed files with 44 additions and 10 deletions

View File

@ -667,15 +667,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
PersistenceContext persistenceContext) {
final EntityEntry entry = persistenceContext.getEntry( toInitialize );
if ( entry != null ) {
if ( entry.getStatus() != Status.LOADING ) {
final Object optionalEntityInstance = rowProcessingState.getJdbcValuesSourceProcessingState()
.getProcessingOptions()
.getEffectiveOptionalObject();
// If the instance to initialize is the main entity, we can't skip this
// This can happen if we initialize an enhanced proxy
if ( !isEntityReturn() || toInitialize != optionalEntityInstance ) {
return;
}
if ( skipInitialization( toInitialize, rowProcessingState, entry ) ) {
return;
}
}
@ -863,6 +856,25 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
}
protected boolean skipInitialization(
Object toInitialize,
RowProcessingState rowProcessingState,
EntityEntry entry) {
// If the instance to initialize is the main entity, we can't skip this
// This can happen if we initialize an enhanced proxy
if ( entry.getStatus() != Status.LOADING ) {
final Object optionalEntityInstance = rowProcessingState.getJdbcValuesSourceProcessingState()
.getProcessingOptions()
.getEffectiveOptionalObject();
// If the instance to initialize is the main entity, we can't skip this
// This can happen if we initialize an enhanced proxy
if ( !isEntityReturn() || toInitialize != optionalEntityInstance ) {
return true;
}
}
return false;
}
private boolean isReadOnly(
RowProcessingState rowProcessingState,
SharedSessionContractImplementor persistenceContext) {

View File

@ -7,7 +7,12 @@
package org.hibernate.sql.results.graph.entity.internal;
import org.hibernate.LockMode;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.Status;
import org.hibernate.internal.log.LoggingHelper;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -16,6 +21,7 @@ import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer;
import org.hibernate.sql.results.graph.entity.EntityResultGraphNode;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
/**
* @author Andrea Boriero
@ -80,4 +86,21 @@ public class EntityJoinedFetchInitializer extends AbstractEntityInitializer {
public String toString() {
return "EntityJoinedFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")";
}
@Override
protected boolean skipInitialization(
Object toInitialize, RowProcessingState rowProcessingState, EntityEntry entry) {
if ( toInitialize instanceof PersistentAttributeInterceptable ) {
final PersistentAttributeInterceptor interceptor = ( (PersistentAttributeInterceptable) toInitialize ).$$_hibernate_getInterceptor();
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
if ( entry.getStatus() != Status.LOADING ) {
// Avoid loading the same entity proxy twice for the same result set: it could lead to errors,
// because some code writes to its input (ID in hydrated state replaced by the loaded entity, in particular).
return false;
}
return true;
}
}
return super.skipInitialization( toInitialize, rowProcessingState, entry );
}
}

View File

@ -15,7 +15,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;