package org.hibernate.loader.plan.exec.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.loader.MultipleBagFetchException;
import org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter;
import org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl;
import org.hibernate.loader.plan.exec.process.spi.CollectionReferenceInitializer;
import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan.exec.process.spi.ReaderCollector;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessor;
import org.hibernate.loader.plan.exec.query.internal.SelectStatementBuilder;
import org.hibernate.loader.plan.exec.query.spi.QueryBuildingParameters;
import org.hibernate.loader.plan.exec.spi.AliasResolutionContext;
import org.hibernate.loader.plan.exec.spi.LoadQueryDetails;
import org.hibernate.loader.plan.spi.CollectionAttributeFetch;
import org.hibernate.loader.plan.spi.CollectionReturn;
import org.hibernate.loader.plan.spi.FetchSource;
import org.hibernate.loader.plan.spi.LoadPlan;
import org.hibernate.loader.plan.spi.QuerySpace;
import org.hibernate.loader.plan.spi.Return;
import org.hibernate.sql.ConditionFragment;
import org.hibernate.sql.DisjunctionFragment;
import org.hibernate.sql.InFragment;
public abstract class AbstractLoadQueryDetails implements LoadQueryDetails {
private final LoadPlan loadPlan;
private final String[] keyColumnNames;
private final Return rootReturn;
private final LoadQueryJoinAndFetchProcessor queryProcessor;
private String sqlStatement;
private ResultSetProcessor resultSetProcessor;
protected AbstractLoadQueryDetails(
LoadPlan loadPlan,
AliasResolutionContextImpl aliasResolutionContext,
QueryBuildingParameters buildingParameters,
String[] keyColumnNames,
Return rootReturn,
SessionFactoryImplementor factory) {
this.keyColumnNames = keyColumnNames;
this.rootReturn = rootReturn;
this.loadPlan = loadPlan;
this.queryProcessor = new LoadQueryJoinAndFetchProcessor( aliasResolutionContext, buildingParameters, factory );
}
protected QuerySpace getQuerySpace(String querySpaceUid) {
return loadPlan.getQuerySpaces().getQuerySpaceByUid( querySpaceUid );
}
@Override
public String getSqlStatement() {
return sqlStatement;
}
@Override
public ResultSetProcessor getResultSetProcessor() {
return resultSetProcessor;
}
protected final Return getRootReturn() {
return rootReturn;
}
protected final AliasResolutionContext getAliasResolutionContext() {
return queryProcessor.getAliasResolutionContext();
}
protected final QueryBuildingParameters getQueryBuildingParameters() {
return queryProcessor.getQueryBuildingParameters();
}
protected final SessionFactoryImplementor getSessionFactory() {
return queryProcessor.getSessionFactory();
}
protected LoadPlan getLoadPlan() {
return loadPlan;
}
protected String[] getKeyColumnNames() {
return keyColumnNames;
}
protected void generate() {
final SelectStatementBuilder select = new SelectStatementBuilder( queryProcessor.getSessionFactory().getDialect() );
applyRootReturnTableFragments( select );
if ( shouldApplyRootReturnFilterBeforeKeyRestriction() ) {
applyRootReturnFilterRestrictions( select );
applyKeyRestriction(
select,
getRootTableAlias(),
keyColumnNames,
getQueryBuildingParameters().getBatchSize()
);
}
else {
applyKeyRestriction(
select,
getRootTableAlias(),
keyColumnNames,
getQueryBuildingParameters().getBatchSize()
);
applyRootReturnFilterRestrictions( select );
}
applyRootReturnWhereJoinRestrictions( select );
applyRootReturnOrderByFragments( select );
applyRootReturnSelectFragments( select );
queryProcessor.processQuerySpaceJoins( getRootQuerySpace(), select );
FetchStats fetchStats = null;
if ( FetchSource.class.isInstance( rootReturn ) ) {
fetchStats = queryProcessor.processFetches(
(FetchSource) rootReturn,
select,
getReaderCollector()
);
}
else if ( CollectionReturn.class.isInstance( rootReturn ) ) {
final CollectionReturn collectionReturn = (CollectionReturn) rootReturn;
if ( collectionReturn.getElementGraph() != null ) {
fetchStats = queryProcessor.processFetches(
collectionReturn.getElementGraph(),
select,
getReaderCollector()
);
}
}
if ( fetchStats != null && fetchStats.getJoinedBagAttributeFetches().size() > 1 ) {
final List<String> bagRoles = new ArrayList<>();
for ( CollectionAttributeFetch bagFetch : fetchStats.getJoinedBagAttributeFetches() ) {
bagRoles.add( bagFetch.getCollectionPersister().getRole() );
}
throw new MultipleBagFetchException( bagRoles );
}
LoadPlanTreePrinter.INSTANCE.logTree( loadPlan, queryProcessor.getAliasResolutionContext() );
this.sqlStatement = select.toStatementString();
this.resultSetProcessor = new ResultSetProcessorImpl(
loadPlan,
queryProcessor.getAliasResolutionContext(),
getReaderCollector().buildRowReader(),
shouldUseOptionalEntityInstance(),
isSubselectLoadingEnabled( fetchStats )
);
}
protected abstract boolean isSubselectLoadingEnabled(FetchStats fetchStats);
protected abstract boolean shouldUseOptionalEntityInstance();
protected abstract ReaderCollector getReaderCollector();
protected abstract QuerySpace getRootQuerySpace();
protected abstract String getRootTableAlias();
protected abstract boolean shouldApplyRootReturnFilterBeforeKeyRestriction();
protected abstract void applyRootReturnSelectFragments(SelectStatementBuilder selectStatementBuilder );
protected abstract void applyRootReturnTableFragments(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnFilterRestrictions(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnWhereJoinRestrictions(SelectStatementBuilder selectStatementBuilder);
protected abstract void applyRootReturnOrderByFragments(SelectStatementBuilder selectStatementBuilder);
private static void applyKeyRestriction(SelectStatementBuilder select, String alias, String[] keyColumnNames, int batchSize) {
if ( keyColumnNames.length==1 ) {
final InFragment in = new InFragment().setColumn( alias, keyColumnNames[0] );
for ( int i = 0; i < batchSize; i++ ) {
in.addValue( "?" );
}
select.appendRestrictions( in.toFragmentString() );
}
else {
final ConditionFragment keyRestrictionBuilder = new ConditionFragment()
.setTableAlias( alias )
.setCondition( keyColumnNames, "?" );
final String keyRestrictionFragment = keyRestrictionBuilder.toFragmentString();
StringBuilder restrictions = new StringBuilder();
if ( batchSize==1 ) {
restrictions.append( keyRestrictionFragment );
}
else {
restrictions.append( '(' );
DisjunctionFragment df = new DisjunctionFragment();
for ( int i=0; i<batchSize; i++ ) {
df.addCondition( keyRestrictionFragment );
}
restrictions.append( df.toFragmentString() );
restrictions.append( ')' );
}
select.appendRestrictions( restrictions.toString() );
}
}
protected abstract static class ReaderCollectorImpl implements ReaderCollector {
private final List<EntityReferenceInitializer> entityReferenceInitializers = new ArrayList<EntityReferenceInitializer>();
private List<CollectionReferenceInitializer> arrayReferenceInitializers;
private List<CollectionReferenceInitializer> collectionReferenceInitializers;
@Override
public void add(CollectionReferenceInitializer collectionReferenceInitializer) {
if ( collectionReferenceInitializer.getCollectionReference().getCollectionPersister().isArray() ) {
if ( arrayReferenceInitializers == null ) {
arrayReferenceInitializers = new ArrayList<CollectionReferenceInitializer>();
}
arrayReferenceInitializers.add( collectionReferenceInitializer );
}
else {
if ( collectionReferenceInitializers == null ) {
collectionReferenceInitializers = new ArrayList<CollectionReferenceInitializer>();
}
collectionReferenceInitializers.add( collectionReferenceInitializer );
}
}
@Override
public void add(EntityReferenceInitializer entityReferenceInitializer) {
entityReferenceInitializers.add( entityReferenceInitializer );
}
@Override
public final List<EntityReferenceInitializer> getEntityReferenceInitializers() {
return entityReferenceInitializers;
}
@Override
public List<CollectionReferenceInitializer> getArrayReferenceInitializers() {
return arrayReferenceInitializers;
}
@Override
public List<CollectionReferenceInitializer> getNonArrayCollectionReferenceInitializers() {
return collectionReferenceInitializers;
}
}
}