org.hibernate.loader.plan.exec.process.internal.ResultSetProcessingContextImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.loader.plan.exec.process.internal;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.SubselectFetch;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.query.spi.NamedParameterContext;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.loader.plan.spi.Fetch;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.type.EntityType;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class ResultSetProcessingContextImpl implements ResultSetProcessingContext {
private static final Logger LOG = CoreLogging.logger( ResultSetProcessingContextImpl.class );
private final ResultSet resultSet;
private final SharedSessionContractImplementor session;
private final LoadPlan loadPlan;
private final AliasResolutionContext aliasResolutionContext;
private final boolean readOnly;
private final boolean shouldUseOptionalEntityInformation;
private final boolean forceFetchLazyAttributes;
private final boolean shouldReturnProxies;
private final QueryParameters queryParameters;
private final NamedParameterContext namedParameterContext;
private final boolean hadSubselectFetches;
private List currentRowHydratedEntityRegistrationList;
private Map> subselectLoadableEntityKeyMap;
private List hydratedEntityRegistrationList;
private int nRowsRead = 0;
/**
* Builds a ResultSetProcessingContextImpl
*
* @param shouldUseOptionalEntityInformation There are times when the "optional entity information" on
* QueryParameters should be used and times when they should not. Collection initializers, batch loaders, etc
* are times when it should NOT be used.
*/
public ResultSetProcessingContextImpl(
final ResultSet resultSet,
final SharedSessionContractImplementor session,
final LoadPlan loadPlan,
final AliasResolutionContext aliasResolutionContext,
final boolean readOnly,
final boolean shouldUseOptionalEntityInformation,
final boolean forceFetchLazyAttributes,
final boolean shouldReturnProxies,
final QueryParameters queryParameters,
final NamedParameterContext namedParameterContext,
final boolean hadSubselectFetches) {
this.resultSet = resultSet;
this.session = session;
this.loadPlan = loadPlan;
this.aliasResolutionContext = aliasResolutionContext;
this.readOnly = readOnly;
this.shouldUseOptionalEntityInformation = shouldUseOptionalEntityInformation;
this.forceFetchLazyAttributes = forceFetchLazyAttributes;
this.shouldReturnProxies = shouldReturnProxies;
this.queryParameters = queryParameters;
this.namedParameterContext = namedParameterContext;
this.hadSubselectFetches = hadSubselectFetches;
if ( shouldUseOptionalEntityInformation ) {
if ( queryParameters.getOptionalId() != null ) {
// make sure we have only one return
if ( loadPlan.getReturns().size() > 1 ) {
throw new IllegalStateException( "Cannot specify 'optional entity' values with multi-return load plans" );
}
}
}
}
@Override
public SharedSessionContractImplementor getSession() {
return session;
}
@Override
public boolean shouldUseOptionalEntityInformation() {
return shouldUseOptionalEntityInformation;
}
@Override
public QueryParameters getQueryParameters() {
return queryParameters;
}
@Override
public boolean shouldReturnProxies() {
return shouldReturnProxies;
}
@Override
public LoadPlan getLoadPlan() {
return loadPlan;
}
public ResultSet getResultSet() {
return resultSet;
}
@Override
public LockMode resolveLockMode(EntityReference entityReference) {
if ( queryParameters.getLockOptions() != null && queryParameters.getLockOptions()
.getLockMode() != null ) {
return queryParameters.getLockOptions().getLockMode();
}
return LockMode.NONE;
}
private Map identifierResolutionContextMap;
@Override
public EntityReferenceProcessingState getProcessingState(final EntityReference entityReference) {
if ( identifierResolutionContextMap == null ) {
identifierResolutionContextMap = new IdentityHashMap<>();
}
EntityReferenceProcessingState context = identifierResolutionContextMap.get( entityReference );
if ( context == null ) {
context = new EntityReferenceProcessingState() {
private boolean wasMissingIdentifier;
private Object identifierHydratedForm;
private EntityKey entityKey;
private Object[] hydratedState;
private Object entityInstance;
@Override
public EntityReference getEntityReference() {
return entityReference;
}
@Override
public void registerMissingIdentifier() {
if ( !EntityFetch.class.isInstance( entityReference ) ) {
throw new IllegalStateException( "Missing return row identifier" );
}
ResultSetProcessingContextImpl.this.registerNonExists( (EntityFetch) entityReference );
wasMissingIdentifier = true;
}
@Override
public boolean isMissingIdentifier() {
return wasMissingIdentifier;
}
@Override
public void registerIdentifierHydratedForm(Object identifierHydratedForm) {
this.identifierHydratedForm = identifierHydratedForm;
}
@Override
public Object getIdentifierHydratedForm() {
return identifierHydratedForm;
}
@Override
public void registerEntityKey(EntityKey entityKey) {
this.entityKey = entityKey;
}
@Override
public EntityKey getEntityKey() {
return entityKey;
}
@Override
public void registerHydratedState(Object[] hydratedState) {
this.hydratedState = hydratedState;
}
@Override
public Object[] getHydratedState() {
return hydratedState;
}
@Override
public void registerEntityInstance(Object entityInstance) {
this.entityInstance = entityInstance;
}
@Override
public Object getEntityInstance() {
return entityInstance;
}
};
identifierResolutionContextMap.put( entityReference, context );
}
return context;
}
private void registerNonExists(EntityFetch fetch) {
final EntityType fetchedType = fetch.getFetchedType();
if ( ! fetchedType.isOneToOne() ) {
return;
}
final EntityReferenceProcessingState fetchOwnerState = getOwnerProcessingState( fetch );
if ( fetchOwnerState == null ) {
throw new IllegalStateException( "Could not locate fetch owner state" );
}
final EntityKey ownerEntityKey = fetchOwnerState.getEntityKey();
if ( ownerEntityKey == null ) {
throw new IllegalStateException( "Could not locate fetch owner EntityKey" );
}
session.getPersistenceContext().addNullProperty(
ownerEntityKey,
fetchedType.getPropertyName()
);
}
@Override
public EntityReferenceProcessingState getOwnerProcessingState(Fetch fetch) {
return getProcessingState( fetch.getSource().resolveEntityReference() );
}
@Override
public void registerHydratedEntity(EntityReference entityReference, EntityKey entityKey, Object entityInstance) {
if ( currentRowHydratedEntityRegistrationList == null ) {
currentRowHydratedEntityRegistrationList = new ArrayList<>();
}
currentRowHydratedEntityRegistrationList.add(
new HydratedEntityRegistration(
entityReference,
entityKey,
entityInstance
)
);
}
/**
* Package-protected
*/
void finishUpRow() {
nRowsRead++;
if ( currentRowHydratedEntityRegistrationList == null ) {
if ( identifierResolutionContextMap != null ) {
identifierResolutionContextMap.clear();
}
return;
}
// managing the running list of registrations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( hydratedEntityRegistrationList == null ) {
hydratedEntityRegistrationList = new ArrayList<>();
}
hydratedEntityRegistrationList.addAll( currentRowHydratedEntityRegistrationList );
// managing the map forms needed for subselect fetch generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( hadSubselectFetches ) {
if ( subselectLoadableEntityKeyMap == null ) {
subselectLoadableEntityKeyMap = new HashMap<>();
}
for ( HydratedEntityRegistration registration : currentRowHydratedEntityRegistrationList ) {
Set entityKeys = subselectLoadableEntityKeyMap.get(
registration.getEntityReference()
);
if ( entityKeys == null ) {
entityKeys = new HashSet<>();
subselectLoadableEntityKeyMap.put( registration.getEntityReference(), entityKeys );
}
entityKeys.add( registration.getKey() );
}
}
// release the currentRowHydratedEntityRegistrationList entries
currentRowHydratedEntityRegistrationList.clear();
identifierResolutionContextMap.clear();
}
public List getHydratedEntityRegistrationList() {
return hydratedEntityRegistrationList;
}
/**
* Package-protected
*/
void wrapUp() {
createSubselects();
if ( hydratedEntityRegistrationList != null ) {
hydratedEntityRegistrationList.clear();
hydratedEntityRegistrationList = null;
}
if ( subselectLoadableEntityKeyMap != null ) {
subselectLoadableEntityKeyMap.clear();
subselectLoadableEntityKeyMap = null;
}
}
private void createSubselects() {
if ( subselectLoadableEntityKeyMap == null || nRowsRead <= 1 ) {
LOG.tracef(
"Skipping create subselects because there are fewer than 2 results, so query by key is more efficient.",
getClass().getName()
);
return; // early return
}
final Map namedParameterLocMap =
ResultSetProcessorHelper.buildNamedParameterLocMap( queryParameters, namedParameterContext );
final String subselectQueryString = SubselectFetch.createSubselectFetchQueryFragment( queryParameters );
for ( Map.Entry> entry : subselectLoadableEntityKeyMap.entrySet() ) {
if ( ! entry.getKey().getEntityPersister().hasSubselectLoadableCollections() ) {
continue;
}
SubselectFetch subselectFetch = new SubselectFetch(
subselectQueryString,
aliasResolutionContext.resolveSqlTableAliasFromQuerySpaceUid( entry.getKey().getQuerySpaceUid() ),
(Loadable) entry.getKey().getEntityPersister(),
queryParameters,
entry.getValue(),
namedParameterLocMap
);
for ( EntityKey key : entry.getValue() ) {
session.getPersistenceContext().getBatchFetchQueue().addSubselect( key, subselectFetch );
}
}
}
public boolean isReadOnly() {
return readOnly;
}
}