pull up some duplicated code to AbstractSqmSelectionQuery

This commit is contained in:
Gavin King 2024-02-22 08:09:07 +01:00
parent f87ea083e6
commit 0502869545
6 changed files with 239 additions and 236 deletions

View File

@ -16,7 +16,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -77,7 +76,9 @@ import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.spi.PrimitiveJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import static java.util.Spliterators.spliteratorUnknownSize;
import static org.hibernate.CacheMode.fromJpaModes;
import static org.hibernate.FlushMode.fromJpaFlushMode;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_RETRIEVE_MODE;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_SHARED_CACHE_STORE_MODE;
import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_RETRIEVE_MODE;
@ -385,32 +386,26 @@ public abstract class AbstractSelectionQuery<R>
if ( jdbcType != null ) {
switch ( jdbcType.getDefaultSqlTypeCode() ) {
case Types.DATE:
if ( resultClass.isAssignableFrom( java.sql.Date.class ) ) {
return true;
}
break;
return resultClass.isAssignableFrom( java.sql.Date.class );
case Types.TIME:
if ( resultClass.isAssignableFrom( java.sql.Time.class ) ) {
return true;
}
break;
return resultClass.isAssignableFrom( java.sql.Time.class );
case Types.TIMESTAMP:
if ( resultClass.isAssignableFrom( java.sql.Timestamp.class ) ) {
return true;
}
break;
return resultClass.isAssignableFrom( java.sql.Timestamp.class );
default:
return false;
}
}
return false;
else {
return false;
}
}
private static <T> void throwQueryTypeMismatchException(Class<T> resultClass, SqmExpressible<?> sqmExpressible) {
final String errorMessage = String.format(
throw new QueryTypeMismatchException( String.format(
"Specified result type [%s] did not match Query selection type [%s] - multiple selections: use Tuple or array",
resultClass.getName(),
sqmExpressible.getTypeName()
);
throw new QueryTypeMismatchException( errorMessage );
) );
}
@ -442,9 +437,7 @@ public abstract class AbstractSelectionQuery<R>
protected HashSet<String> beforeQueryHandlingFetchProfiles() {
beforeQuery();
final MutableQueryOptions options = getQueryOptions();
return getSession().getLoadQueryInfluencers()
.adjustFetchProfiles( options.getDisabledFetchProfiles(), options.getEnabledFetchProfiles() );
}
@ -519,7 +512,7 @@ public abstract class AbstractSelectionQuery<R>
@Override
public ScrollableResultsImplementor<R> scroll() {
return scroll( getSession().getFactory().getJdbcServices().getJdbcEnvironment().getDialect().defaultScrollMode() );
return scroll( getSessionFactory().getJdbcServices().getDialect().defaultScrollMode() );
}
@Override
@ -545,7 +538,7 @@ public abstract class AbstractSelectionQuery<R>
public Stream stream() {
final ScrollableResultsImplementor scrollableResults = scroll( ScrollMode.FORWARD_ONLY );
final ScrollableResultsIterator iterator = new ScrollableResultsIterator<>( scrollableResults );
final Spliterator spliterator = Spliterators.spliteratorUnknownSize( iterator, Spliterator.NONNULL );
final Spliterator spliterator = spliteratorUnknownSize( iterator, Spliterator.NONNULL );
final Stream stream = StreamSupport.stream( spliterator, false );
return (Stream) stream.onClose( scrollableResults::close );
@ -577,14 +570,16 @@ public abstract class AbstractSelectionQuery<R>
if ( size == 0 ) {
return null;
}
final T first = list.get( 0 );
// todo (6.0) : add a setting here to control whether to perform this validation or not
for ( int i = 1; i < size; i++ ) {
if ( list.get( i ) != first ) {
throw new NonUniqueResultException( list.size() );
else {
final T first = list.get( 0 );
// todo (6.0) : add a setting here to control whether to perform this validation or not
for ( int i = 1; i < size; i++ ) {
if ( list.get( i ) != first ) {
throw new NonUniqueResultException( list.size() );
}
}
return first;
}
return first;
}
@Override
@ -602,17 +597,6 @@ public abstract class AbstractSelectionQuery<R>
}
}
protected static boolean hasLimit(SqmSelectStatement<?> sqm, MutableQueryOptions queryOptions) {
return queryOptions.hasLimit() || sqm.getFetch() != null || sqm.getOffset() != null;
}
protected static boolean hasAppliedGraph(MutableQueryOptions queryOptions) {
return queryOptions.getAppliedGraph() != null
&& queryOptions.getAppliedGraph().getSemantic() != null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DomainQueryExecutionContext
@ -641,7 +625,7 @@ public abstract class AbstractSelectionQuery<R>
@Override
public SelectionQuery<R> setFlushMode(FlushModeType flushMode) {
getQueryOptions().setFlushMode( FlushMode.fromJpaFlushMode( flushMode ) );
getQueryOptions().setFlushMode( fromJpaFlushMode( flushMode ) );
return this;
}

View File

@ -0,0 +1,121 @@
/*
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sqm.internal;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.spi.AppliedGraph;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.hql.internal.QuerySplitter;
import org.hibernate.query.spi.AbstractSelectionQuery;
import org.hibernate.query.spi.MutableQueryOptions;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.results.internal.TupleMetadata;
import java.util.List;
import static org.hibernate.cfg.QuerySettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
/**
* @author Gavin King
*/
abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
AbstractSqmSelectionQuery(SharedSessionContractImplementor session) {
super(session);
}
protected int max(boolean hasLimit, SqmSelectStatement<?> sqmStatement, List<R> list) {
return !hasLimit || getQueryOptions().getLimit().getMaxRows() == null
? getMaxRows( sqmStatement, list.size() )
: getQueryOptions().getLimit().getMaxRows();
}
protected int first(boolean hasLimit, SqmSelectStatement<?> sqmStatement) {
return !hasLimit || getQueryOptions().getLimit().getFirstRow() == null
? getIntegerLiteral( sqmStatement.getOffset(), 0 )
: getQueryOptions().getLimit().getFirstRow();
}
protected static boolean hasLimit(SqmSelectStatement<?> sqm, MutableQueryOptions queryOptions) {
return queryOptions.hasLimit() || sqm.getFetch() != null || sqm.getOffset() != null;
}
protected boolean needsDistinct(boolean containsCollectionFetches, boolean hasLimit, SqmSelectStatement<?> sqmStatement) {
return containsCollectionFetches
&& ( hasLimit || sqmStatement.usesDistinct() || hasAppliedGraph( getQueryOptions() ) );
}
protected static boolean hasAppliedGraph(MutableQueryOptions queryOptions) {
final AppliedGraph appliedGraph = queryOptions.getAppliedGraph();
return appliedGraph != null && appliedGraph.getSemantic() != null;
}
protected void errorOrLogForPaginationWithCollectionFetch() {
if ( getSessionFactory().getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled() ) {
throw new HibernateException(
"setFirstResult() or setMaxResults() specified with collection fetch join "
+ "(in-memory pagination was about to be applied, but '"
+ FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH
+ "' is enabled)"
);
}
else {
QueryLogging.QUERY_MESSAGE_LOGGER.firstOrMaxResultsSpecifiedWithCollectionFetch();
}
}
public abstract SqmStatement<R> getSqmStatement();
public abstract DomainParameterXref getDomainParameterXref();
public abstract TupleMetadata getTupleMetadata();
public abstract Class<R> getExpectedResultType();
protected SelectQueryPlan<R> buildSelectQueryPlan() {
final SqmSelectStatement<R> statement = (SqmSelectStatement<R>) getSqmStatement();
final SqmSelectStatement<R>[] concreteSqmStatements = QuerySplitter.split( statement );
return concreteSqmStatements.length > 1
? buildAggregatedQueryPlan( concreteSqmStatements )
: buildConcreteQueryPlan( concreteSqmStatements[0] );
}
private SelectQueryPlan<R> buildAggregatedQueryPlan(SqmSelectStatement<R>[] concreteSqmStatements) {
@SuppressWarnings("unchecked")
final SelectQueryPlan<R>[] aggregatedQueryPlans = new SelectQueryPlan[ concreteSqmStatements.length ];
// todo (6.0) : we want to make sure that certain thing (ResultListTransformer, etc) only get applied at the aggregator-level
for ( int i = 0, length = concreteSqmStatements.length; i < length; i++ ) {
aggregatedQueryPlans[i] = buildConcreteQueryPlan( concreteSqmStatements[i] );
}
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
}
protected SelectQueryPlan<R> buildConcreteQueryPlan(SqmSelectStatement<R> concreteSqmStatement) {
return buildConcreteQueryPlan(
concreteSqmStatement,
getExpectedResultType(),
getTupleMetadata(),
getQueryOptions()
);
}
protected <T> ConcreteSqmSelectQueryPlan<T> buildConcreteQueryPlan(
SqmSelectStatement<T> concreteSqmStatement,
Class<T> expectedResultType,
TupleMetadata tupleMetadata,
QueryOptions queryOptions) {
return new ConcreteSqmSelectQueryPlan<>(
concreteSqmStatement,
getQueryString(),
getDomainParameterXref(),
expectedResultType,
tupleMetadata,
queryOptions
);
}
}

View File

@ -10,7 +10,6 @@ import org.hibernate.graph.spi.AppliedGraph;
import org.hibernate.graph.spi.AttributeNodeImplementor;
import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.graph.spi.SubGraphImplementor;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.query.spi.QueryOptions;
/**
@ -23,17 +22,18 @@ public class AppliedGraphs {
public static boolean containsCollectionFetches(QueryOptions queryOptions) {
final AppliedGraph appliedGraph = queryOptions.getAppliedGraph();
return appliedGraph != null && appliedGraph.getGraph() != null && containsCollectionFetches(appliedGraph.getGraph());
return appliedGraph != null
&& appliedGraph.getGraph() != null
&& containsCollectionFetches( appliedGraph.getGraph() );
}
private static boolean containsCollectionFetches(GraphImplementor<?> graph) {
for (AttributeNodeImplementor<?> attributeNodeImplementor : graph.getAttributeNodeImplementors()) {
PersistentAttribute<?, ?> attributeDescriptor = attributeNodeImplementor.getAttributeDescriptor();
if (attributeDescriptor.isCollection()) {
for ( AttributeNodeImplementor<?> attributeNodeImplementor : graph.getAttributeNodeImplementors() ) {
if ( attributeNodeImplementor.getAttributeDescriptor().isCollection() ) {
return true;
}
for (SubGraphImplementor<?> subGraph : attributeNodeImplementor.getSubGraphMap().values()) {
if (containsCollectionFetches(subGraph)) {
for ( SubGraphImplementor<?> subGraph : attributeNodeImplementor.getSubGraphMap().values() ) {
if ( containsCollectionFetches(subGraph) ) {
return true;
}
}

View File

@ -26,7 +26,6 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.ScrollMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.query.spi.EntityGraphQueryHint;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -52,7 +51,6 @@ import org.hibernate.query.IllegalSelectQueryException;
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
import org.hibernate.query.Order;
import org.hibernate.query.Query;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.ResultListTransformer;
import org.hibernate.query.SemanticException;
@ -65,7 +63,6 @@ import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext;
import org.hibernate.query.internal.ParameterMetadataImpl;
import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.AbstractSelectionQuery;
import org.hibernate.query.spi.DelegatingQueryOptions;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.HqlInterpretation;
@ -125,6 +122,7 @@ import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_RETRIEVE_MODE;
import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_STORE_MODE;
import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions;
import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptionsWithUniqueSemanticFilter;
import static org.hibernate.query.sqm.internal.AppliedGraphs.containsCollectionFetches;
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.createInterpretationsKey;
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.generateNonSelectKey;
import static org.hibernate.query.sqm.internal.SqmUtil.isSelect;
@ -139,7 +137,7 @@ import static org.hibernate.query.sqm.tree.SqmCopyContext.noParamCopyContext;
* @author Steve Ebersole
*/
public class QuerySqmImpl<R>
extends AbstractSelectionQuery<R>
extends AbstractSqmSelectionQuery<R>
implements SqmQueryImplementor<R>, InterpretationsKeySource, DomainQueryExecutionContext {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( QuerySqmImpl.class );
@ -413,6 +411,7 @@ public class QuerySqmImpl<R>
}
}
@Override
public TupleMetadata getTupleMetadata() {
return tupleMetadata;
}
@ -427,6 +426,7 @@ public class QuerySqmImpl<R>
return sqm;
}
@Override
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
@ -446,10 +446,16 @@ public class QuerySqmImpl<R>
return getQueryParameterBindings();
}
@Override
public Class<R> getResultType() {
return resultType;
}
@Override
public Class<R> getExpectedResultType() {
return resultType;
}
@Override
public LoadQueryInfluencers getLoadQueryInfluencers() {
return getSession().getLoadQueryInfluencers();
@ -501,59 +507,48 @@ public class QuerySqmImpl<R>
}
};
final SqmSelectStatement<?> sqmStatement = (SqmSelectStatement<?>) getSqmStatement();
return buildConcreteSelectQueryPlan( sqmStatement.createCountQuery(), Long.class, null, getQueryOptions() )
return buildConcreteQueryPlan( sqmStatement.createCountQuery(), Long.class, null, getQueryOptions() )
.executeQuery( context, new SingleResultConsumer<>() );
}
protected List<R> doList() {
verifySelect();
final SqmSelectStatement<?> sqmStatement = (SqmSelectStatement<?>) getSqmStatement();
final SqmSelectStatement<?> statement = (SqmSelectStatement<?>) getSqmStatement();
final boolean containsCollectionFetches =
sqmStatement.containsCollectionFetches()
|| AppliedGraphs.containsCollectionFetches( getQueryOptions() );
final boolean hasLimit = hasLimit( sqmStatement, getQueryOptions() );
final boolean needsDistinct = containsCollectionFetches
&& ( hasLimit || sqmStatement.usesDistinct() || hasAppliedGraph( getQueryOptions() ) );
statement.containsCollectionFetches()
|| containsCollectionFetches( getQueryOptions() );
final boolean hasLimit = hasLimit( statement, getQueryOptions() );
final boolean needsDistinct = needsDistinct( containsCollectionFetches, hasLimit, statement );
final List<R> list = resolveSelectQueryPlan()
.performList( executionContextForDoList( containsCollectionFetches, hasLimit, needsDistinct ) );
return needsDistinct ? handleDistinct( hasLimit, statement, list ) : list;
}
if ( needsDistinct ) {
final int first = !hasLimit || getQueryOptions().getLimit().getFirstRow() == null
? getIntegerLiteral( sqmStatement.getOffset(), 0 )
: getQueryOptions().getLimit().getFirstRow();
final int max = !hasLimit || getQueryOptions().getLimit().getMaxRows() == null
? getMaxRows( sqmStatement, list.size() )
: getQueryOptions().getLimit().getMaxRows();
if ( first > 0 || max != -1 ) {
final int resultSize = list.size();
private List<R> handleDistinct(boolean hasLimit, SqmSelectStatement<?> statement, List<R> list) {
final int first = first( hasLimit, statement );
final int max = max( hasLimit, statement, list );
if ( first > 0 || max != -1 ) {
final int resultSize = list.size();
if ( first > resultSize ) {
return new ArrayList<>(0);
}
else {
final int toIndex = max != -1 ? first + max : resultSize;
if ( first > resultSize ) {
return new ArrayList<>(0);
}
return list.subList( first, Math.min( toIndex, resultSize ) );
}
}
return list;
else {
return list;
}
}
protected DomainQueryExecutionContext executionContextForDoList(boolean containsCollectionFetches, boolean hasLimit, boolean needsDistinct) {
// TODO: very similar to SqmSelectionQueryImpl.executionContext()
protected DomainQueryExecutionContext executionContextForDoList(
boolean containsCollectionFetches, boolean hasLimit, boolean needsDistinct) {
final MutableQueryOptions originalQueryOptions;
final QueryOptions normalizedQueryOptions;
if ( hasLimit && containsCollectionFetches ) {
if ( getSessionFactory().getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled() ) {
throw new HibernateException(
"setFirstResult() or setMaxResults() specified with collection fetch join "
+ "(in-memory pagination was about to be applied, but '"
+ AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH
+ "' is enabled)"
);
}
else {
QueryLogging.QUERY_MESSAGE_LOGGER.firstOrMaxResultsSpecifiedWithCollectionFetch();
}
errorOrLogForPaginationWithCollectionFetch();
originalQueryOptions = getQueryOptions();
normalizedQueryOptions = needsDistinct
@ -627,52 +622,6 @@ public class QuerySqmImpl<R>
}
}
private SelectQueryPlan<R> buildSelectQueryPlan() {
final SqmSelectStatement<R>[] concreteSqmStatements =
QuerySplitter.split( (SqmSelectStatement<R>) getSqmStatement() );
if ( concreteSqmStatements.length > 1 ) {
return buildAggregatedSelectQueryPlan( concreteSqmStatements );
}
else {
return buildConcreteSelectQueryPlan( concreteSqmStatements[0] );
}
}
private SelectQueryPlan<R> buildAggregatedSelectQueryPlan(SqmSelectStatement<R>[] concreteSqmStatements) {
@SuppressWarnings("unchecked")
final SelectQueryPlan<R>[] aggregatedQueryPlans = new SelectQueryPlan[ concreteSqmStatements.length ];
// todo (6.0) : we want to make sure that certain thing (ResultListTransformer, etc) only get applied at the aggregator-level
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
aggregatedQueryPlans[i] = buildConcreteSelectQueryPlan( concreteSqmStatements[i] );
}
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
}
private SelectQueryPlan<R> buildConcreteSelectQueryPlan(SqmSelectStatement<R> concreteSqmStatement) {
return buildConcreteSelectQueryPlan(
concreteSqmStatement,
getResultType(),
tupleMetadata,
getQueryOptions()
);
}
private <T> SelectQueryPlan<T> buildConcreteSelectQueryPlan(
SqmSelectStatement<T> concreteSqmStatement,
Class<T> resultType,
TupleMetadata tupleMetadata,
QueryOptions queryOptions) {
return new ConcreteSqmSelectQueryPlan<>(
concreteSqmStatement,
getQueryString(),
getDomainParameterXref(),
resultType,
tupleMetadata,
queryOptions
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Update / delete / insert query execution
@ -721,7 +670,7 @@ public class QuerySqmImpl<R>
final QueryInterpretationCache.Key cacheKey = generateNonSelectKey( this );
final QueryInterpretationCache interpretationCache =
getSession().getFactory().getQueryEngine().getInterpretationCache();
getSessionFactory().getQueryEngine().getInterpretationCache();
if ( cacheKey != null ) {
queryPlan = interpretationCache.getNonSelectQueryPlan( cacheKey );
}
@ -762,7 +711,7 @@ public class QuerySqmImpl<R>
: buildConcreteDeleteQueryPlan( concreteSqmStatements[0] );
}
private NonSelectQueryPlan buildConcreteDeleteQueryPlan(@SuppressWarnings("rawtypes") SqmDeleteStatement sqmDelete) {
private NonSelectQueryPlan buildConcreteDeleteQueryPlan(SqmDeleteStatement<?> sqmDelete) {
final EntityDomainType<?> entityDomainType = sqmDelete.getTarget().getModel();
final String entityNameToDelete = entityDomainType.getHibernateEntityName();
final EntityPersister persister = getSessionFactory().getMappingMetamodel().getEntityDescriptor( entityNameToDelete );
@ -776,13 +725,11 @@ public class QuerySqmImpl<R>
}
}
private NonSelectQueryPlan buildAggregatedDeleteQueryPlan(@SuppressWarnings("rawtypes") SqmDeleteStatement[] concreteSqmStatements) {
private NonSelectQueryPlan buildAggregatedDeleteQueryPlan(SqmDeleteStatement<?>[] concreteSqmStatements) {
final NonSelectQueryPlan[] aggregatedQueryPlans = new NonSelectQueryPlan[ concreteSqmStatements.length ];
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
aggregatedQueryPlans[i] = buildConcreteDeleteQueryPlan( concreteSqmStatements[i] );
}
return new AggregatedNonSelectQueryPlanImpl( aggregatedQueryPlans );
}

View File

@ -24,10 +24,8 @@ import jakarta.persistence.TemporalType;
import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.ScrollMode;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.spi.AppliedGraph;
@ -35,16 +33,13 @@ import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.query.BindableType;
import org.hibernate.query.Order;
import org.hibernate.query.Page;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.criteria.internal.NamedCriteriaQueryMementoImpl;
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
import org.hibernate.query.hql.internal.QuerySplitter;
import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext;
import org.hibernate.query.internal.ParameterMetadataImpl;
import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.query.spi.AbstractSelectionQuery;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.HqlInterpretation;
import org.hibernate.query.spi.MutableQueryOptions;
@ -87,7 +82,7 @@ import static org.hibernate.query.sqm.internal.SqmUtil.sortSpecification;
/**
* @author Steve Ebersole
*/
public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
public class SqmSelectionQueryImpl<R> extends AbstractSqmSelectionQuery<R>
implements SqmSelectionQueryImplementor<R>, InterpretationsKeySource {
private final String hql;
private SqmSelectStatement<R> sqm;
@ -240,6 +235,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
}
}
@Override
public TupleMetadata getTupleMetadata() {
return tupleMetadata;
}
@ -249,6 +245,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
return sqm;
}
@Override
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
@ -328,33 +325,51 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
}
protected List<R> doList() {
final SqmSelectStatement<?> sqmStatement = getSqmStatement();
final boolean containsCollectionFetches = sqmStatement.containsCollectionFetches();
final boolean hasLimit = hasLimit( sqmStatement, getQueryOptions() );
final boolean needsDistinct = containsCollectionFetches
&& ( sqmStatement.usesDistinct() || hasAppliedGraph( getQueryOptions() ) || hasLimit );
final SqmSelectStatement<?> statement = getSqmStatement();
final boolean containsCollectionFetches =
//TODO: why is this different from QuerySqmImpl.doList()?
statement.containsCollectionFetches();
final boolean hasLimit = hasLimit( statement, getQueryOptions() );
final boolean needsDistinct = needsDistinct( containsCollectionFetches, hasLimit, statement );
final List<R> list = resolveQueryPlan()
.performList( executionContext( hasLimit, containsCollectionFetches ) );
return needsDistinct ? handleDistinct( hasLimit, statement, list ) : list;
}
final DomainQueryExecutionContext executionContextToUse;
private List<R> handleDistinct(boolean hasLimit, SqmSelectStatement<?> statement, List<R> list) {
int includedCount = -1;
// NOTE: 'firstRow' is zero-based
final int first = first( hasLimit, statement );
final int max = max( hasLimit, statement, list );
final List<R> distinctList = new ArrayList<>( list.size() );
final IdentitySet<Object> distinction = new IdentitySet<>( list.size() );
for ( final R result : list) {
if ( distinction.add( result ) ) {
includedCount++;
if ( includedCount >= first ) {
distinctList.add( result );
// NOTE: 'max-1' because 'first' is zero-based while 'max' is not
if ( max >= 0 && includedCount - first >= max - 1 ) {
break;
}
}
}
}
return distinctList;
}
// TODO: very similar to QuerySqmImpl.executionContextForDoList()
private DomainQueryExecutionContext executionContext(boolean hasLimit, boolean containsCollectionFetches) {
if ( hasLimit && containsCollectionFetches ) {
if ( getSessionFactory().getSessionFactoryOptions().isFailOnPaginationOverCollectionFetchEnabled() ) {
throw new HibernateException(
"setFirstResult() or setMaxResults() specified with collection fetch join "
+ "(in-memory pagination was about to be applied, but '"
+ AvailableSettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH
+ "' is enabled)"
);
}
else {
QueryLogging.QUERY_MESSAGE_LOGGER.firstOrMaxResultsSpecifiedWithCollectionFetch();
}
errorOrLogForPaginationWithCollectionFetch();
final MutableQueryOptions originalQueryOptions = getQueryOptions();
final QueryOptions normalizedQueryOptions = omitSqlQueryOptions( originalQueryOptions, true, false );
if ( originalQueryOptions == normalizedQueryOptions ) {
executionContextToUse = this;
return this;
}
else {
executionContextToUse = new DelegatingDomainQueryExecutionContext( this ) {
return new DelegatingDomainQueryExecutionContext( this ) {
@Override
public QueryOptions getQueryOptions() {
return normalizedQueryOptions;
@ -363,39 +378,8 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
}
}
else {
executionContextToUse = this;
return this;
}
final List<R> list = resolveQueryPlan().performList( executionContextToUse );
if ( needsDistinct ) {
int includedCount = -1;
// NOTE : firstRow is zero-based
final int first = !hasLimit || getQueryOptions().getLimit().getFirstRow() == null
? getIntegerLiteral( sqmStatement.getOffset(), 0 )
: getQueryOptions().getLimit().getFirstRow();
final int max = !hasLimit || getQueryOptions().getLimit().getMaxRows() == null
? getMaxRows( sqmStatement, list.size() )
: getQueryOptions().getLimit().getMaxRows();
final List<R> tmp = new ArrayList<>( list.size() );
final IdentitySet<Object> distinction = new IdentitySet<>( list.size() );
for ( final R result : list ) {
if ( !distinction.add( result ) ) {
continue;
}
includedCount++;
if ( includedCount < first ) {
continue;
}
tmp.add( result );
// NOTE : ( max - 1 ) because first is zero-based while max is not...
if ( max >= 0 && includedCount - first >= max - 1 ) {
break;
}
}
return tmp;
}
return list;
}
@Override
@ -408,58 +392,25 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
return resolveQueryPlan().executeQuery( this, resultsConsumer );
}
@Override
public Class<R> getExpectedResultType() {
return expectedResultType;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Query plan
private SelectQueryPlan<R> resolveQueryPlan() {
final QueryInterpretationCache.Key cacheKey = createInterpretationsKey( this );
if ( cacheKey != null ) {
return getSession().getFactory().getQueryEngine().getInterpretationCache()
.resolveSelectQueryPlan( cacheKey, this::buildQueryPlan );
return getSessionFactory().getQueryEngine().getInterpretationCache()
.resolveSelectQueryPlan( cacheKey, this::buildSelectQueryPlan );
}
else {
return buildQueryPlan();
return buildSelectQueryPlan();
}
}
private SelectQueryPlan<R> buildQueryPlan() {
final SqmSelectStatement<R>[] concreteSqmStatements = QuerySplitter.split( getSqmStatement() );
return concreteSqmStatements.length > 1
? buildAggregatedQueryPlan( concreteSqmStatements )
: buildConcreteQueryPlan( concreteSqmStatements[0], getQueryOptions() );
}
private SelectQueryPlan<R> buildAggregatedQueryPlan(SqmSelectStatement<R>[] concreteSqmStatements) {
//noinspection unchecked
final SelectQueryPlan<R>[] aggregatedQueryPlans = new SelectQueryPlan[ concreteSqmStatements.length ];
// todo (6.0) : we want to make sure that certain thing (ResultListTransformer, etc) only get applied at the aggregator-level
for ( int i = 0, x = concreteSqmStatements.length; i < x; i++ ) {
aggregatedQueryPlans[i] = buildConcreteQueryPlan( concreteSqmStatements[i], getQueryOptions() );
}
return new AggregatedSelectQueryPlanImpl<>( aggregatedQueryPlans );
}
private SelectQueryPlan<R> buildConcreteQueryPlan(
SqmSelectStatement<R> concreteSqmStatement,
QueryOptions queryOptions) {
return buildConcreteQueryPlan( concreteSqmStatement, expectedResultType, tupleMetadata, queryOptions);
}
private <T> ConcreteSqmSelectQueryPlan<T> buildConcreteQueryPlan(
SqmSelectStatement<T> concreteSqmStatement,
Class<T> expectedResultType,
TupleMetadata tupleMetadata,
QueryOptions queryOptions) {
return new ConcreteSqmSelectQueryPlan<>(
concreteSqmStatement,
getQueryString(),
getDomainParameterXref(),
expectedResultType,
tupleMetadata,
queryOptions
);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// InterpretationsKeySource

View File

@ -7,7 +7,7 @@
package org.hibernate.sql.results.internal;
import org.hibernate.TypeMismatchException;
import org.hibernate.query.QueryTypeMismatchException;
import org.hibernate.sql.results.spi.RowTransformer;
/**
@ -29,7 +29,7 @@ public class RowTransformerCheckingImpl<R> implements RowTransformer<R> {
return (R) result;
}
else {
throw new TypeMismatchException( "Result type is '" + type.getSimpleName()
throw new QueryTypeMismatchException( "Result type is '" + type.getSimpleName()
+ "' but the query returned a '" + result.getClass().getSimpleName() + "'" );
}
}