Fix SingleIdEntityLoaderDynamicBatch#load() method
This commit is contained in:
parent
696eea9bbe
commit
29e22c68ac
|
@ -58,6 +58,16 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
|
|||
|
||||
@Override
|
||||
public T load(Object pkValue, LockOptions lockOptions, Boolean readOnly, SharedSessionContractImplementor session) {
|
||||
return load( pkValue, null, lockOptions, readOnly, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(
|
||||
Object pkValue,
|
||||
Object entityInstance,
|
||||
LockOptions lockOptions,
|
||||
Boolean readOnly,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Object[] batchIds = session.getPersistenceContextInternal()
|
||||
.getBatchFetchQueue()
|
||||
.getBatchLoadableEntityIds( getLoadable(), pkValue, maxBatchSize );
|
||||
|
@ -134,43 +144,7 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
|
|||
JdbcSelectExecutorStandardImpl.INSTANCE.list(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return new QueryOptionsAdapter() {
|
||||
@Override
|
||||
public Boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryIdentifier(String sql) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
|
||||
subSelectFetchableKeysHandler.addKey( entityKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return null;
|
||||
}
|
||||
|
||||
},
|
||||
getExecutionContext( entityInstance, readOnly, session, subSelectFetchableKeysHandler ),
|
||||
RowTransformerPassThruImpl.instance(),
|
||||
ListResultsConsumer.UniqueSemantic.FILTER
|
||||
);
|
||||
|
@ -185,17 +159,56 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
|
|||
final EntityKey entityKey = session.generateEntityKey( pkValue, getLoadable().getEntityPersister() );
|
||||
//noinspection unchecked
|
||||
return (T) session.getPersistenceContext().getEntity( entityKey );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public T load(
|
||||
Object pkValue,
|
||||
private ExecutionContext getExecutionContext(
|
||||
Object entityInstance,
|
||||
LockOptions lockOptions,
|
||||
Boolean readOnly,
|
||||
SharedSessionContractImplementor session) {
|
||||
initializeSingleIdLoaderIfNeeded( session );
|
||||
return singleIdLoader.load( pkValue, entityInstance, lockOptions, readOnly, session );
|
||||
SharedSessionContractImplementor session,
|
||||
SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler) {
|
||||
return new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntityInstance() {
|
||||
return entityInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return new QueryOptionsAdapter() {
|
||||
@Override
|
||||
public Boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryIdentifier(String sql) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerLoadingEntityEntry(EntityKey entityKey, LoadingEntityEntry entry) {
|
||||
subSelectFetchableKeysHandler.addKey( entityKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private void initializeSingleIdLoaderIfNeeded(SharedSessionContractImplementor session) {
|
||||
|
|
|
@ -17,11 +17,14 @@ import org.hibernate.HibernateException;
|
|||
import org.hibernate.LockMode;
|
||||
import org.hibernate.StaleObjectStateException;
|
||||
import org.hibernate.WrongClassException;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
|
||||
import org.hibernate.cache.spi.access.EntityDataAccess;
|
||||
import org.hibernate.cache.spi.entry.CacheEntry;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.spi.SessionEventListenerManager;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
@ -91,6 +94,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
private Object entityInstance;
|
||||
private Object entityInstanceForNotify;
|
||||
private boolean missing;
|
||||
private boolean isOwningInitializer;
|
||||
private Object[] resolvedEntityState;
|
||||
|
||||
// todo (6.0) : ^^ need a better way to track whether we are loading the entity state or if something else is/has
|
||||
|
@ -473,6 +477,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
final Object proxy = getProxy( persistenceContext );
|
||||
// Special case map proxy to avoid stack overflows
|
||||
// We know that a map proxy will always be of "the right type" so just use that object
|
||||
final LoadingEntityEntry existingLoadingEntry = persistenceContext
|
||||
.getLoadContexts()
|
||||
.findLoadingEntityEntry( entityKey );
|
||||
setIsOwningInitializer(entityKey.getIdentifier(), existingLoadingEntry );
|
||||
|
||||
if ( proxy != null && ( proxy instanceof MapProxy
|
||||
|| entityDescriptor.getJavaTypeDescriptor().getJavaTypeClass().isInstance( proxy ) ) ) {
|
||||
entityInstance = proxy;
|
||||
|
@ -488,11 +497,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
// initializer is already loading the entity
|
||||
entityInstance = resolveInstance(
|
||||
entityIdentifier,
|
||||
existingLoadingEntry,
|
||||
rowProcessingState,
|
||||
session,
|
||||
persistenceContext
|
||||
session
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( LockMode.NONE != lockMode ) {
|
||||
|
@ -536,16 +544,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
return persistenceContext.getProxy( entityKey );
|
||||
}
|
||||
|
||||
private Object resolveInstance(
|
||||
Object entityIdentifier,
|
||||
RowProcessingState rowProcessingState,
|
||||
SharedSessionContractImplementor session,
|
||||
PersistenceContext persistenceContext) {
|
||||
final LoadingEntityEntry existingLoadingEntry = persistenceContext
|
||||
.getLoadContexts()
|
||||
.findLoadingEntityEntry( entityKey );
|
||||
|
||||
Object instance = null;
|
||||
private void setIsOwningInitializer(Object entityIdentifier,LoadingEntityEntry existingLoadingEntry) {
|
||||
if ( existingLoadingEntry != null ) {
|
||||
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||
EntityLoadingLogger.LOGGER.debugf(
|
||||
|
@ -554,38 +553,47 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
toLoggableString( getNavigablePath(), entityIdentifier )
|
||||
);
|
||||
}
|
||||
|
||||
instance = existingLoadingEntry.getEntityInstance();
|
||||
|
||||
if ( existingLoadingEntry.getEntityInitializer() != this ) {
|
||||
// the entity is already being loaded elsewhere
|
||||
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||
EntityLoadingLogger.LOGGER.debugf(
|
||||
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
|
||||
getSimpleConcreteImplName(),
|
||||
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||
existingLoadingEntry.getEntityInitializer()
|
||||
);
|
||||
}
|
||||
|
||||
// EARLY EXIT!!!
|
||||
return instance;
|
||||
if ( existingLoadingEntry.getEntityInitializer() == this ) {
|
||||
isOwningInitializer = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
isOwningInitializer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( instance == null ) {
|
||||
// this isEntityReturn bit is just for entity loaders, not hql/criteria
|
||||
if ( isEntityReturn() ) {
|
||||
final Object requestedEntityId = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.getProcessingOptions()
|
||||
.getEffectiveOptionalId();
|
||||
final Object optionalEntityInstance = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.getProcessingOptions()
|
||||
.getEffectiveOptionalObject();
|
||||
if ( requestedEntityId != null && optionalEntityInstance != null && requestedEntityId.equals(
|
||||
entityKey.getIdentifier() ) ) {
|
||||
instance = optionalEntityInstance;
|
||||
}
|
||||
private Object resolveInstance(
|
||||
Object entityIdentifier,
|
||||
LoadingEntityEntry existingLoadingEntry,
|
||||
RowProcessingState rowProcessingState,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( !isOwningInitializer ) {
|
||||
// the entity is already being loaded elsewhere
|
||||
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
|
||||
EntityLoadingLogger.LOGGER.debugf(
|
||||
"(%s) Entity [%s] being loaded by another initializer [%s] - skipping processing",
|
||||
getSimpleConcreteImplName(),
|
||||
toLoggableString( getNavigablePath(), entityIdentifier ),
|
||||
existingLoadingEntry.getEntityInitializer()
|
||||
);
|
||||
}
|
||||
// EARLY EXIT!!!
|
||||
return existingLoadingEntry.getEntityInstance();
|
||||
}
|
||||
|
||||
Object instance = null;
|
||||
|
||||
// this isEntityReturn bit is just for entity loaders, not hql/criteria
|
||||
if ( isEntityReturn() ) {
|
||||
final Object requestedEntityId = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.getProcessingOptions()
|
||||
.getEffectiveOptionalId();
|
||||
final Object optionalEntityInstance = rowProcessingState.getJdbcValuesSourceProcessingState()
|
||||
.getProcessingOptions()
|
||||
.getEffectiveOptionalObject();
|
||||
if ( requestedEntityId != null && optionalEntityInstance != null && requestedEntityId.equals(
|
||||
entityKey.getIdentifier() ) ) {
|
||||
instance = optionalEntityInstance;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,6 +639,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
entityKey,
|
||||
loadingEntry
|
||||
);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -644,7 +653,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
|
||||
final SharedSessionContractImplementor session = rowProcessingState.getJdbcValuesSourceProcessingState().getSession();
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
|
||||
if ( entityInstance instanceof HibernateProxy ) {
|
||||
LazyInitializer hibernateLazyInitializer = ( (HibernateProxy) entityInstance ).getHibernateLazyInitializer();
|
||||
if ( !hibernateLazyInitializer.isUninitialized() ) {
|
||||
|
@ -655,9 +663,9 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
if ( instance == null ) {
|
||||
instance = resolveInstance(
|
||||
entityKey.getIdentifier(),
|
||||
persistenceContext.getLoadContexts().findLoadingEntityEntry( entityKey ),
|
||||
rowProcessingState,
|
||||
session,
|
||||
persistenceContext
|
||||
session
|
||||
);
|
||||
initializeEntity( instance, rowProcessingState, session, persistenceContext );
|
||||
}
|
||||
|
@ -865,10 +873,26 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean skipInitialization(
|
||||
private boolean skipInitialization(
|
||||
Object toInitialize,
|
||||
RowProcessingState rowProcessingState,
|
||||
EntityEntry entry) {
|
||||
if ( !isOwningInitializer ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
|
@ -891,7 +915,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
final Boolean queryOption = rowProcessingState.getJdbcValuesSourceProcessingState().getQueryOptions().isReadOnly();
|
||||
|
||||
return queryOption == null ? false : queryOption;
|
||||
|
@ -927,6 +950,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
|
|||
@Override
|
||||
public void finishUpRow(RowProcessingState rowProcessingState) {
|
||||
// reset row state
|
||||
isOwningInitializer = false;
|
||||
concreteDescriptor = null;
|
||||
entityKey = null;
|
||||
entityInstance = null;
|
||||
|
|
|
@ -7,12 +7,7 @@
|
|||
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;
|
||||
|
@ -22,7 +17,6 @@ import org.hibernate.sql.results.graph.DomainResult;
|
|||
import org.hibernate.sql.results.graph.Fetch;
|
||||
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
|
||||
|
@ -84,21 +78,4 @@ 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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.lazy.proxy;
|
||||
package org.hibernate.orm.test.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
Loading…
Reference in New Issue