pull up some duplicated code to AbstractSqmSelectionQuery
This commit is contained in:
parent
f87ea083e6
commit
0502869545
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() + "'" );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue