From ca53d8a6cc360f74fbaa8b22254f715ce73c1306 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 17 Jan 2025 09:14:03 +0100 Subject: [PATCH] some cleanups mostly around native query stuff --- .../query/spi/AbstractSelectionQuery.java | 10 +- .../NativeNonSelectQueryPlanImpl.java | 27 +- .../query/sql/internal/NativeQueryImpl.java | 45 +- .../internal/NativeSelectQueryPlanImpl.java | 129 +++--- .../internal/ResultSetMappingProcessor.java | 397 +++++++++--------- .../internal/ConcreteSqmSelectQueryPlan.java | 40 +- 6 files changed, 295 insertions(+), 353 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java index 022c9885fe7c..1eef7d8b8598 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java @@ -282,7 +282,7 @@ public R getSingleResult() { } protected static T uniqueElement(List list) throws NonUniqueResultException { - int size = list.size(); + final int size = list.size(); if ( size == 0 ) { return null; } @@ -381,11 +381,13 @@ public SelectionQuery setEntityGraph(EntityGraph graph, GraphSemantic sema @Override public SelectionQuery enableFetchProfile(String profileName) { - if ( !getSession().getFactory().containsFetchProfileDefinition( profileName ) ) { + if ( getSession().getFactory().containsFetchProfileDefinition( profileName ) ) { + getQueryOptions().enableFetchProfile( profileName ); + return this; + } + else { throw new UnknownProfileException( profileName ); } - getQueryOptions().enableFetchProfile( profileName ); - return this; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java index d5b62c9548c3..fdca3cb00b51 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java @@ -5,7 +5,6 @@ package org.hibernate.query.sql.internal; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -13,15 +12,15 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; -import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutationNative; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBindings; +import static java.util.Collections.emptyList; + /** * @author Steve Ebersole */ @@ -47,37 +46,25 @@ public int executeUpdate(DomainQueryExecutionContext executionContext) { BulkOperationCleanupAction.schedule( session, affectedTableNames ); final List jdbcParameterBinders; final JdbcParameterBindings jdbcParameterBindings; - - final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings(); if ( parameterList == null || parameterList.isEmpty() ) { - jdbcParameterBinders = Collections.emptyList(); + jdbcParameterBinders = emptyList(); jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; } else { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( - queryParameterBindings, + executionContext.getQueryParameterBindings(), parameterList, jdbcParameterBinders, session.getFactory() ); } - final SQLQueryParser parser = new SQLQueryParser( sql, null, session.getSessionFactory() ); - - final JdbcOperationQueryMutation jdbcMutation = new JdbcOperationQueryMutationNative( - parser.process(), - jdbcParameterBinders, - affectedTableNames - ); - + final String processedSql = new SQLQueryParser( sql, null, session.getSessionFactory() ).process(); return session.getJdbcServices().getJdbcMutationExecutor().execute( - jdbcMutation, + new JdbcOperationQueryMutationNative( processedSql, jdbcParameterBinders, affectedTableNames ), jdbcParameterBindings, - sql -> session - .getJdbcCoordinator() - .getStatementPreparer() - .prepareStatement( sql ), + sql -> session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql ), (integer, preparedStatement) -> {}, SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 1c36cf8d5965..23f0c5d6e1a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -269,15 +269,7 @@ public NativeQueryImpl( } - public NativeQueryImpl( - NamedNativeQueryMemento memento, - Supplier resultSetMappingCreator, - ResultSetMappingHandler resultSetMappingHandler, - SharedSessionContractImplementor session) { - this( memento, resultSetMappingCreator, resultSetMappingHandler, null, session ); - } - - public NativeQueryImpl( + private NativeQueryImpl( NamedNativeQueryMemento memento, Supplier resultSetMappingCreator, ResultSetMappingHandler resultSetMappingHandler, @@ -303,29 +295,32 @@ public NativeQueryImpl( resultSetMappingHandler.resolveResultSetMapping( resultSetMapping, querySpaces::add, this ); if ( resultType != null ) { - if ( !isResultTypeAlwaysAllowed( resultType ) ) { - switch ( resultSetMapping.getNumberOfResultBuilders() ) { - case 0: - throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); - case 1: - final Class actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ) - .getJavaType(); - if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { - throw buildIncompatibleException( resultType, actualResultJavaType ); - } - break; - default: - throw new IllegalArgumentException( - "Cannot create TypedQuery for query with more than one return" ); - } + if ( isResultTypeAlwaysAllowed( resultType ) ) { + setTupleTransformerForResultType( resultType ); } else { - setTupleTransformerForResultType( resultType ); + checkResulType( resultType ); } } applyOptions( memento ); } + private void checkResulType(Class resultType) { + switch ( resultSetMapping.getNumberOfResultBuilders() ) { + case 0: + throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); + case 1: + final Class actualResultJavaType = + resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); + if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { + throw buildIncompatibleException( resultType, actualResultJavaType ); + } + break; + default: + throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); + } + } + public NativeQueryImpl( String sqlString, NamedResultSetMappingMemento resultSetMappingMemento, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java index 0af44aaf75df..891b67458b72 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java @@ -5,7 +5,6 @@ package org.hibernate.query.sql.internal; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -29,6 +28,8 @@ import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ResultsConsumer; +import static java.util.Collections.emptyList; + /** * @author Steve Ebersole */ @@ -67,7 +68,7 @@ public T executeQuery(DomainQueryExecutionContext executionContext, ResultsC final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings(); if ( parameterList == null || parameterList.isEmpty() ) { - jdbcParameterBinders = Collections.emptyList(); + jdbcParameterBinders = emptyList(); jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; } else { @@ -102,43 +103,43 @@ public T executeQuery(DomainQueryExecutionContext executionContext, ResultsC public List performList(DomainQueryExecutionContext executionContext) { final QueryOptions queryOptions = executionContext.getQueryOptions(); if ( queryOptions.getEffectiveLimit().getMaxRowsJpa() == 0 ) { - return Collections.emptyList(); - } - final List jdbcParameterBinders; - final JdbcParameterBindings jdbcParameterBindings; - - final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings(); - if ( parameterList == null || parameterList.isEmpty() ) { - jdbcParameterBinders = Collections.emptyList(); - jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; + return emptyList(); } else { - jdbcParameterBinders = new ArrayList<>( parameterList.size() ); - jdbcParameterBindings = new JdbcParameterBindingsImpl( - queryParameterBindings, - parameterList, + final List jdbcParameterBinders; + final JdbcParameterBindings jdbcParameterBindings; + if ( parameterList == null || parameterList.isEmpty() ) { + jdbcParameterBinders = emptyList(); + jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; + } + else { + jdbcParameterBinders = new ArrayList<>( parameterList.size() ); + jdbcParameterBindings = new JdbcParameterBindingsImpl( + executionContext.getQueryParameterBindings(), + parameterList, + jdbcParameterBinders, + executionContext.getSession().getFactory() + ); + } + + final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( + sql, jdbcParameterBinders, - executionContext.getSession().getFactory() + resultSetMapping, + affectedTableNames ); - } - final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( - sql, - jdbcParameterBinders, - resultSetMapping, - affectedTableNames - ); - - executionContext.getSession().autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); - return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().list( - jdbcSelect, - jdbcParameterBindings, - SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), - null, - queryOptions.getUniqueSemantic() == null ? - ListResultsConsumer.UniqueSemantic.NEVER : - queryOptions.getUniqueSemantic() - ); + executionContext.getSession().autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); + return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().list( + jdbcSelect, + jdbcParameterBindings, + SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), + null, + queryOptions.getUniqueSemantic() == null + ? ListResultsConsumer.UniqueSemantic.NEVER + : queryOptions.getUniqueSemantic() + ); + } } @Override @@ -146,39 +147,39 @@ public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, Doma if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { return EmptyScrollableResults.instance(); } - final List jdbcParameterBinders; - final JdbcParameterBindings jdbcParameterBindings; - - final QueryParameterBindings queryParameterBindings = executionContext.getQueryParameterBindings(); - if ( parameterList == null || parameterList.isEmpty() ) { - jdbcParameterBinders = Collections.emptyList(); - jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; - } else { - jdbcParameterBinders = new ArrayList<>( parameterList.size() ); - jdbcParameterBindings = new JdbcParameterBindingsImpl( - queryParameterBindings, - parameterList, + final List jdbcParameterBinders; + final JdbcParameterBindings jdbcParameterBindings; + if ( parameterList == null || parameterList.isEmpty() ) { + jdbcParameterBinders = emptyList(); + jdbcParameterBindings = JdbcParameterBindings.NO_BINDINGS; + } + else { + jdbcParameterBinders = new ArrayList<>( parameterList.size() ); + jdbcParameterBindings = new JdbcParameterBindingsImpl( + executionContext.getQueryParameterBindings(), + parameterList, + jdbcParameterBinders, + executionContext.getSession().getFactory() + ); + } + + final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( + sql, jdbcParameterBinders, - executionContext.getSession().getFactory() + resultSetMapping, + affectedTableNames ); - } - final JdbcOperationQuerySelect jdbcSelect = new JdbcOperationQuerySelect( - sql, - jdbcParameterBinders, - resultSetMapping, - affectedTableNames - ); - - executionContext.getSession().autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); - return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().scroll( - jdbcSelect, - scrollMode, - jdbcParameterBindings, - SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), - null, - -1 - ); + executionContext.getSession().autoFlushIfRequired( jdbcSelect.getAffectedTableNames() ); + return executionContext.getSession().getJdbcServices().getJdbcSelectExecutor().scroll( + jdbcSelect, + scrollMode, + jdbcParameterBindings, + SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), + null, + -1 + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java index e92204c2cb2a..d58365cb7d2f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ResultSetMappingProcessor.java @@ -5,21 +5,17 @@ package org.hibernate.query.sql.internal; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.internal.CoreLogging; -import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.loader.internal.AliasConstantsHelper; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.PluralAttributeMapping; @@ -30,6 +26,7 @@ import org.hibernate.query.NativeQuery; import org.hibernate.query.results.FetchBuilder; import org.hibernate.query.results.LegacyFetchBuilder; +import org.hibernate.query.results.ResultBuilder; import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.results.internal.complete.CompleteResultBuilderCollectionStandard; import org.hibernate.query.results.internal.dynamic.DynamicFetchBuilderContainer; @@ -42,6 +39,12 @@ import org.hibernate.type.EntityType; import org.hibernate.type.Type; +import static java.util.Arrays.asList; +import static java.util.Collections.addAll; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.hibernate.internal.util.collections.ArrayHelper.slice; +import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping; @@ -55,7 +58,6 @@ * @author Steve Ebersole */ public class ResultSetMappingProcessor implements SQLQueryParser.ParserContext { - private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ResultSetMappingProcessor.class ); private final ResultSetMapping resultSetMapping; @@ -83,23 +85,25 @@ public ResultSetMappingProcessor(ResultSetMapping resultSetMapping, SessionFacto } private Map internalGetPropertyResultsMap(String alias) { - Map propertyResultMaps = collectionPropertyResultMaps.get( alias ); - if ( propertyResultMaps == null ) { - propertyResultMaps = entityPropertyResultMaps.get( alias ); - } - if ( propertyResultMaps != null ) { - return propertyResultMaps; - } - NativeQuery.ResultNode rtn = alias2Return.get( alias ); - if ( rtn instanceof NativeQuery.ReturnProperty && !( rtn instanceof NativeQuery.FetchReturn ) ) { - return null; + final Map propertyResultMap = getPropertyResultMap( alias ); + if ( propertyResultMap != null ) { + return propertyResultMap; } else { - // todo (6.0): access property results map somehow which was on NativeSQLQueryNonScalarReturn before - return Collections.emptyMap(); + final NativeQuery.ResultNode resultNode = alias2Return.get( alias ); + return resultNode instanceof NativeQuery.ReturnProperty + && !( resultNode instanceof NativeQuery.FetchReturn ) + ? null + // todo (6.0): access property results map somehow which was on NativeSQLQueryNonScalarReturn before + : emptyMap(); } } + private Map getPropertyResultMap(String alias) { + final Map propertyResultMap = collectionPropertyResultMaps.get( alias ); + return propertyResultMap == null ? entityPropertyResultMaps.get( alias ) : propertyResultMap; + } + public SQLQueryParser.ParserContext process() { // first, break down the returns into maps keyed by alias // so that role returns can be more easily resolved to their owners @@ -111,7 +115,7 @@ public SQLQueryParser.ParserContext process() { } else if ( resultBuilder instanceof NativeQuery.CollectionReturn collectionReturn ) { alias2Return.put( collectionReturn.getTableAlias(), collectionReturn ); - Map propertyResultsMap = Collections.emptyMap();//fetchReturn.getPropertyResultsMap() + Map propertyResultsMap = emptyMap(); //fetchReturn.getPropertyResultsMap() addCollection( collectionReturn.getNavigablePath().getFullPath(), collectionReturn.getTableAlias(), @@ -136,8 +140,8 @@ else if ( resultBuilder instanceof NativeQuery.CollectionReturn collectionReturn } private void processFetchBuilder(Fetchable attributeName, FetchBuilder fetchBuilder) { - if ( fetchBuilder instanceof LegacyFetchBuilder ) { - resultSetMapping.addLegacyFetchBuilder( (LegacyFetchBuilder) fetchBuilder ); + if ( fetchBuilder instanceof LegacyFetchBuilder legacyFetchBuilder ) { + resultSetMapping.addLegacyFetchBuilder( legacyFetchBuilder ); } else if ( fetchBuilder instanceof NativeQuery.FetchReturn fetchReturn ) { alias2Return.put( fetchReturn.getTableAlias(), fetchReturn ); @@ -147,55 +151,50 @@ else if ( fetchBuilder instanceof NativeQuery.FetchReturn fetchReturn ) { } public ResultSetMapping generateResultMapping(boolean queryHadAliases) { - if ( !queryHadAliases ) { - return this.resultSetMapping; + if ( queryHadAliases ) { + final ResultSetMapping mapping = resolveResultSetMapping( null, false, factory ); + final Set visited = new HashSet<>(); + resultSetMapping.visitResultBuilders( (i, builder) -> visitResultSetBuilder( builder, visited, mapping ) ); + resultSetMapping.visitLegacyFetchBuilders( builder -> applyFetchBuilder( mapping, builder, visited ) ); + return mapping; + } + else { + return resultSetMapping; } - final ResultSetMapping resultSetMapping = resolveResultSetMapping( null, false, factory ); - final Set visited = new HashSet<>(); - this.resultSetMapping.visitResultBuilders( - (i, resultBuilder) -> { - if ( resultBuilder instanceof NativeQuery.RootReturn rootReturn ) { - final String suffix = alias2Suffix.get( rootReturn.getTableAlias() ); - visited.add( rootReturn.getTableAlias() ); - if ( suffix == null ) { - resultSetMapping.addResultBuilder( resultBuilder ); - } - else { - final DynamicResultBuilderEntityStandard resultBuilderEntity = createSuffixedResultBuilder( - rootReturn, - suffix - ); - - resultSetMapping.addResultBuilder( resultBuilderEntity ); - alias2Return.put( rootReturn.getTableAlias(), resultBuilderEntity ); - } - } - else if ( resultBuilder instanceof NativeQuery.CollectionReturn collectionReturn ) { - final String suffix = alias2CollectionSuffix.get( collectionReturn.getTableAlias() ); - if ( suffix == null ) { - resultSetMapping.addResultBuilder( resultBuilder ); - } - else { - final CompleteResultBuilderCollectionStandard resultBuilderCollection = createSuffixedResultBuilder( - collectionReturn, - suffix, - alias2Suffix.get( collectionReturn.getTableAlias() ) - ); - - resultSetMapping.addResultBuilder( resultBuilderCollection ); - alias2Return.put( collectionReturn.getTableAlias(), resultBuilderCollection ); - } - } - else { - resultSetMapping.addResultBuilder( resultBuilder ); - } - } - ); - this.resultSetMapping.visitLegacyFetchBuilders( - fetchBuilder -> applyFetchBuilder( resultSetMapping, fetchBuilder, visited ) - ); - return resultSetMapping; + } + + private void visitResultSetBuilder(ResultBuilder resultBuilder, Set visited, ResultSetMapping resultSetMapping) { + if ( resultBuilder instanceof NativeQuery.RootReturn rootReturn ) { + final String suffix = alias2Suffix.get( rootReturn.getTableAlias() ); + visited.add( rootReturn.getTableAlias() ); + if ( suffix == null ) { + resultSetMapping.addResultBuilder( resultBuilder ); + } + else { + final DynamicResultBuilderEntityStandard resultBuilderEntity = + createSuffixedResultBuilder( rootReturn, suffix ); + resultSetMapping.addResultBuilder( resultBuilderEntity ); + alias2Return.put( rootReturn.getTableAlias(), resultBuilderEntity ); + } + } + else if ( resultBuilder instanceof NativeQuery.CollectionReturn collectionReturn ) { + final String suffix = alias2CollectionSuffix.get( collectionReturn.getTableAlias() ); + if ( suffix == null ) { + resultSetMapping.addResultBuilder( resultBuilder ); + } + else { + final CompleteResultBuilderCollectionStandard resultBuilderCollection = + createSuffixedResultBuilder( collectionReturn, suffix, + alias2Suffix.get( collectionReturn.getTableAlias() ) ); + + resultSetMapping.addResultBuilder( resultBuilderCollection ); + alias2Return.put( collectionReturn.getTableAlias(), resultBuilderCollection ); + } + } + else { + resultSetMapping.addResultBuilder( resultBuilder ); + } } private void applyFetchBuilder( @@ -205,6 +204,7 @@ private void applyFetchBuilder( if ( !visited.add( fetchBuilder.getTableAlias() ) ) { return; } + final String suffix = alias2Suffix.get( fetchBuilder.getTableAlias() ); if ( suffix == null ) { resultSetMapping.addLegacyFetchBuilder( fetchBuilder ); @@ -218,10 +218,9 @@ private void applyFetchBuilder( visited ); } - // At this point, the owner builder must be a DynamicResultBuilderEntityStandard to which we can add this builder to - final DynamicResultBuilderEntityStandard ownerBuilder = (DynamicResultBuilderEntityStandard) alias2Return.get( - fetchBuilder.getOwnerAlias() - ); + // At this point, the owner builder must be a DynamicResultBuilderEntityStandard to which we can add this builder + final DynamicResultBuilderEntityStandard ownerBuilder = + (DynamicResultBuilderEntityStandard) alias2Return.get( fetchBuilder.getOwnerAlias() ); final DynamicResultBuilderEntityStandard resultBuilderEntity = createSuffixedResultBuilder( alias2Persister.get( fetchBuilder.getTableAlias() ).findContainingEntityMapping(), fetchBuilder.getTableAlias(), @@ -229,40 +228,14 @@ private void applyFetchBuilder( null, determineNavigablePath( fetchBuilder ) ); - final EntityPersister loadable = alias2Persister.get( fetchBuilder.getOwnerAlias() ); - final List columnNames; - final String[] columnAliases = loadable.getSubclassPropertyColumnAliases( - fetchBuilder.getFetchable().getFetchableName(), - alias2Suffix.get( fetchBuilder.getOwnerAlias() ) - ); - if ( columnAliases.length == 0 ) { - final CollectionPersister collectionPersister = alias2CollectionPersister.get( fetchBuilder.getTableAlias() ); - if ( collectionPersister == null ) { - columnNames = Collections.emptyList(); - } - else { - final String collectionSuffix = alias2CollectionSuffix.get( fetchBuilder.getTableAlias() ); - final String[] keyColumnAliases = collectionPersister.getKeyColumnAliases( collectionSuffix ); - columnNames = Arrays.asList( keyColumnAliases ); - if ( collectionPersister.hasIndex() ) { - resultBuilderEntity.addProperty( - ((PluralAttributeMapping) fetchBuilder.getFetchable()).getIndexDescriptor(), - collectionPersister.getIndexColumnAliases( collectionSuffix ) - ); - } - } - } - else { - columnNames = Arrays.asList( columnAliases ); - } ownerBuilder.addFetchBuilder( fetchBuilder.getFetchable(), new DynamicFetchBuilderLegacy( fetchBuilder.getTableAlias(), fetchBuilder.getOwnerAlias(), fetchBuilder.getFetchable(), - columnNames, - Collections.emptyMap(), + columnNames( resultBuilderEntity, fetchBuilder ), + emptyMap(), resultBuilderEntity ) ); @@ -271,16 +244,48 @@ private void applyFetchBuilder( } } + private List columnNames( + DynamicResultBuilderEntityStandard resultBuilder, LegacyFetchBuilder fetchBuilder) { + final String[] columnAliases = + alias2Persister.get( fetchBuilder.getOwnerAlias() ) + .getSubclassPropertyColumnAliases( fetchBuilder.getFetchable().getFetchableName(), + alias2Suffix.get( fetchBuilder.getOwnerAlias() ) ); + if ( columnAliases.length == 0 ) { + final CollectionPersister collectionPersister = + alias2CollectionPersister.get( fetchBuilder.getTableAlias() ); + if ( collectionPersister == null ) { + return emptyList(); + } + else { + final String collectionSuffix = alias2CollectionSuffix.get( fetchBuilder.getTableAlias() ); + if ( collectionPersister.hasIndex() ) { + final PluralAttributeMapping fetchable = (PluralAttributeMapping) fetchBuilder.getFetchable(); + resultBuilder.addProperty( + fetchable.getIndexDescriptor(), + collectionPersister.getIndexColumnAliases( collectionSuffix ) + ); + } + return asList( collectionPersister.getKeyColumnAliases( collectionSuffix ) ); + } + } + else { + return asList( columnAliases ); + } + } + private NavigablePath determineNavigablePath(LegacyFetchBuilder fetchBuilder) { final NativeQuery.ResultNode ownerResult = alias2Return.get( fetchBuilder.getOwnerAlias() ); - if ( ownerResult instanceof NativeQuery.RootReturn ) { - return ( (NativeQuery.RootReturn) ownerResult ).getNavigablePath() - .append( fetchBuilder.getFetchable().getFetchableName() ); + final NavigablePath path; + if ( ownerResult instanceof NativeQuery.RootReturn rootReturn ) { + path = rootReturn.getNavigablePath(); + } + else if ( ownerResult instanceof DynamicFetchBuilderLegacy dynamicFetchBuilderLegacy ) { + path = determineNavigablePath( dynamicFetchBuilderLegacy ); } else { - return determineNavigablePath( ( DynamicFetchBuilderLegacy) ownerResult ) - .append( fetchBuilder.getFetchable().getFetchableName() ); + throw new AssertionFailure( "Unexpected fetch builder" ); } + return path.append( fetchBuilder.getFetchable().getFetchableName() ); } private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( @@ -302,11 +307,8 @@ private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( LockMode lockMode, NavigablePath navigablePath) { final EntityPersister loadable = entityMapping.getEntityPersister(); - final DynamicResultBuilderEntityStandard resultBuilderEntity = new DynamicResultBuilderEntityStandard( - entityMapping, - tableAlias, - navigablePath - ); + final DynamicResultBuilderEntityStandard resultBuilderEntity = + new DynamicResultBuilderEntityStandard( entityMapping, tableAlias, navigablePath ); resultBuilderEntity.setLockMode( lockMode ); final String[] identifierAliases = loadable.getIdentifierAliases( suffix ); @@ -319,13 +321,6 @@ private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( loadable.visitFetchables( (index, fetchable) -> { if ( fetchable.isSelectable() ) { - final Type propertyType; - if ( loadable instanceof SingleTableEntityPersister singleTableEntityPersister ) { - propertyType = singleTableEntityPersister.getSubclassPropertyType( index ); - } - else { - propertyType = loadable.getPropertyType( fetchable.getFetchableName() ); - } addFetchBuilder( suffix, loadable, @@ -334,7 +329,9 @@ private DynamicResultBuilderEntityStandard createSuffixedResultBuilder( identifierAliases, fetchable, loadable.getSubclassPropertyColumnAliases( fetchable.getFetchableName(), suffix ), - propertyType + loadable instanceof SingleTableEntityPersister singleTableEntityPersister + ? singleTableEntityPersister.getSubclassPropertyType( index ) + : loadable.getPropertyType( fetchable.getFetchableName() ) ); } }, @@ -353,26 +350,19 @@ private void addFetchBuilder( String[] columnAliases, Type propertyType) { if ( propertyType instanceof CollectionType collectionType ) { - final String[] keyColumnAliases; - if ( collectionType.useLHSPrimaryKey() ) { - keyColumnAliases = identifierAliases; - } - else { - keyColumnAliases = loadable.getSubclassPropertyColumnAliases( - collectionType.getLHSPropertyName(), - suffix - ); - } + final String[] keyColumnAliases = + collectionType.useLHSPrimaryKey() + ? identifierAliases + : loadable.getSubclassPropertyColumnAliases( collectionType.getLHSPropertyName(), suffix ); resultBuilderEntity.addProperty( fetchable, keyColumnAliases ); } else if ( propertyType instanceof ComponentType componentType ) { - final Map fetchBuilderMap = new HashMap<>(); final DynamicFetchBuilderLegacy fetchBuilder = new DynamicFetchBuilderLegacy( "", tableAlias, fetchable, - Arrays.asList( columnAliases ), - fetchBuilderMap + asList( columnAliases ), + new HashMap<>() ); final String[] propertyNames = componentType.getPropertyNames(); final Type[] propertyTypes = componentType.getSubtypes(); @@ -386,7 +376,7 @@ else if ( propertyType instanceof ComponentType componentType ) { tableAlias, identifierAliases, fetchable, - ArrayHelper.slice( columnAliases, aliasIndex, columnSpan ), + slice( columnAliases, aliasIndex, columnSpan ), propertyTypes[i] ); aliasIndex += columnSpan; @@ -415,7 +405,20 @@ private CompleteResultBuilderCollectionStandard createSuffixedResultBuilder( String suffix, String entitySuffix) { final CollectionPersister collectionPersister = collectionReturn.getPluralAttribute().getCollectionDescriptor(); - final String[] elementColumnAliases; + return new CompleteResultBuilderCollectionStandard( + collectionReturn.getTableAlias(), + collectionReturn.getNavigablePath(), + collectionReturn.getPluralAttribute(), + collectionPersister.getKeyColumnAliases( suffix ), + collectionPersister.hasIndex() + ? collectionPersister.getIndexColumnAliases( suffix ) + : null, + getElementColumnAliases( suffix, entitySuffix, collectionPersister ) + ); + } + + private static String[] getElementColumnAliases( + String suffix, String entitySuffix, CollectionPersister collectionPersister) { if ( collectionPersister.getElementType() instanceof EntityType ) { final EntityPersister elementPersister = collectionPersister.getElementPersister(); final String[] propertyNames = elementPersister.getPropertyNames(); @@ -424,28 +427,18 @@ private CompleteResultBuilderCollectionStandard createSuffixedResultBuilder( final List aliases = new ArrayList<>( propertyNames.length + identifierAliases.length + ( discriminatorAlias == null ? 0 : 1 ) ); - Collections.addAll( aliases, identifierAliases ); + addAll( aliases, identifierAliases ); if ( discriminatorAlias != null ) { aliases.add( discriminatorAlias ); } for ( int i = 0; i < propertyNames.length; i++ ) { - Collections.addAll( aliases, elementPersister.getPropertyAliases( entitySuffix, i ) ); + addAll( aliases, elementPersister.getPropertyAliases( entitySuffix, i ) ); } - elementColumnAliases = ArrayHelper.toStringArray( aliases ); + return toStringArray( aliases ); } else { - elementColumnAliases = collectionPersister.getElementColumnAliases( suffix ); + return collectionPersister.getElementColumnAliases( suffix ); } - return new CompleteResultBuilderCollectionStandard( - collectionReturn.getTableAlias(), - collectionReturn.getNavigablePath(), - collectionReturn.getPluralAttribute(), - collectionPersister.getKeyColumnAliases( suffix ), - collectionPersister.hasIndex() - ? collectionPersister.getIndexColumnAliases( suffix ) - : null, - elementColumnAliases - ); } private EntityPersister getSQLLoadable(String entityName) throws MappingException { @@ -460,34 +453,32 @@ private String generateCollectionSuffix() { return collectionSuffixSeed++ + "__"; } - private void processReturn(NativeQuery.ResultNode rtn) { - if ( rtn instanceof NativeQuery.RootReturn ) { - processRootReturn( (NativeQuery.RootReturn) rtn ); + private void processReturn(NativeQuery.ResultNode resultNode) { + if ( resultNode instanceof NativeQuery.RootReturn rootReturn ) { + processRootReturn( rootReturn ); } - else if ( rtn instanceof NativeQuery.FetchReturn ) { - processFetchReturn( (NativeQuery.FetchReturn) rtn ); + else if ( resultNode instanceof NativeQuery.FetchReturn fetchReturn ) { + processFetchReturn( fetchReturn ); } - else if ( rtn instanceof NativeQuery.InstantiationResultNode ) { - processConstructorReturn( (NativeQuery.InstantiationResultNode) rtn ); + else if ( resultNode instanceof NativeQuery.InstantiationResultNode instantiationResultNode ) { + processConstructorReturn( instantiationResultNode ); } - else if ( rtn instanceof NativeQuery.ReturnProperty ) { - processScalarReturn( (NativeQuery.ReturnProperty) rtn ); + else if ( resultNode instanceof NativeQuery.ReturnProperty returnProperty ) { + processScalarReturn( returnProperty ); } - else if ( rtn instanceof NativeQuery.ReturnableResultNode ) { - processPropertyReturn( (NativeQuery.ReturnableResultNode) rtn ); + else if ( resultNode instanceof NativeQuery.ReturnableResultNode returnableResultNode ) { + processPropertyReturn( returnableResultNode ); } else { - throw new IllegalStateException( - "Unrecognized NativeSQLQueryReturn concrete type encountered : " + rtn - ); + throw new AssertionFailure( "Unrecognized ResultNode concrete type: " + resultNode ); } } - private void processPropertyReturn(NativeQuery.ReturnableResultNode rtn) { + private void processPropertyReturn(NativeQuery.ReturnableResultNode returnableResultNode) { //nothing to do } - private void processConstructorReturn(NativeQuery.InstantiationResultNode rtn) { + private void processConstructorReturn(NativeQuery.InstantiationResultNode instantiationResultNode) { //nothing to do } @@ -497,34 +488,27 @@ private void processScalarReturn(NativeQuery.ReturnProperty typeReturn) { } private void processRootReturn(NativeQuery.RootReturn rootReturn) { - if ( alias2Persister.containsKey( rootReturn.getTableAlias() ) ) { - // already been processed... - return; + if ( !alias2Persister.containsKey( rootReturn.getTableAlias() ) ) { + final EntityPersister persister = rootReturn.getEntityMapping().getEntityPersister(); + final Map propertyResultsMap = emptyMap(); //rootReturn.getPropertyResultsMap() + addPersister( rootReturn.getTableAlias(), propertyResultsMap, persister ); } - - EntityPersister persister = rootReturn.getEntityMapping().getEntityPersister(); - Map propertyResultsMap = Collections.emptyMap();//rootReturn.getPropertyResultsMap() - addPersister( rootReturn.getTableAlias(), propertyResultsMap, persister ); + // else already processed } private void addPersister(String alias, Map propertyResult, EntityPersister persister) { alias2Persister.put( alias, persister ); - String suffix = generateEntitySuffix(); - LOG.tracev( "Mapping alias [{0}] to entity-suffix [{1}]", alias, suffix ); - alias2Suffix.put( alias, suffix ); + alias2Suffix.put( alias, generateEntitySuffix() ); entityPropertyResultMaps.put( alias, propertyResult ); } private void addCollection(String role, String alias, Map propertyResults) { final CollectionPersister collectionDescriptor = - factory.getMappingMetamodel() - .getCollectionDescriptor( role ); + factory.getMappingMetamodel().getCollectionDescriptor( role ); alias2CollectionPersister.put( alias, collectionDescriptor ); - String suffix = generateCollectionSuffix(); - LOG.tracev( "Mapping alias [{0}] to collection-suffix [{1}]", alias, suffix ); - alias2CollectionSuffix.put( alias, suffix ); + alias2CollectionSuffix.put( alias, generateCollectionSuffix() ); collectionPropertyResultMaps.put( alias, propertyResults ); if ( collectionDescriptor.isOneToMany() || collectionDescriptor.isManyToMany() ) { @@ -535,51 +519,46 @@ private void addCollection(String role, String alias, Map prop private Map filter(Map propertyResults) { final Map result = new HashMap<>( propertyResults.size() ); final String keyPrefix = "element."; - for ( Map.Entry element : propertyResults.entrySet() ) { final String path = element.getKey(); if ( path.startsWith( keyPrefix ) ) { result.put( path.substring( keyPrefix.length() ), element.getValue() ); } } - return result; } private void processFetchReturn(NativeQuery.FetchReturn fetchReturn) { - String alias = fetchReturn.getTableAlias(); - if ( alias2Persister.containsKey( alias ) || alias2CollectionPersister.containsKey( alias ) ) { - // already been processed... - return; - } - - String ownerAlias = fetchReturn.getOwnerAlias(); + final String alias = fetchReturn.getTableAlias(); + if ( !alias2Persister.containsKey( alias ) && !alias2CollectionPersister.containsKey( alias ) ) { + final String ownerAlias = fetchReturn.getOwnerAlias(); - // Make sure the owner alias is known... - if ( !alias2Return.containsKey( ownerAlias ) ) { - throw new HibernateException( "Owner alias [" + ownerAlias + "] is unknown for alias [" + alias + "]" ); - } - - // If this return's alias has not been processed yet, do so before further processing of this return - if ( !alias2Persister.containsKey( ownerAlias ) ) { - processReturn( alias2Return.get( ownerAlias ) ); - } + // Make sure the owner alias is known... + if ( !alias2Return.containsKey( ownerAlias ) ) { + throw new HibernateException( "Owner alias [" + ownerAlias + "] is unknown for alias [" + alias + "]" ); + } - EntityPersister ownerPersister = alias2Persister.get( ownerAlias ); - Type returnType = ownerPersister.getPropertyType( fetchReturn.getFetchable().getFetchableName() ); + // If this return's alias has not been processed yet, do so before further processing of this return + if ( !alias2Persister.containsKey( ownerAlias ) ) { + processReturn( alias2Return.get( ownerAlias ) ); + } - if ( returnType instanceof CollectionType ) { - String role = ownerPersister.getEntityName() + '.' + fetchReturn.getFetchable().getFetchableName(); - Map propertyResultsMap = Collections.emptyMap();//fetchReturn.getPropertyResultsMap() - addCollection( role, alias, propertyResultsMap ); -// collectionOwnerAliases.add( ownerAlias ); - } - else if ( returnType instanceof EntityType eType ) { - String returnEntityName = eType.getAssociatedEntityName(); - EntityPersister persister = getSQLLoadable( returnEntityName ); - Map propertyResultsMap = Collections.emptyMap();//fetchReturn.getPropertyResultsMap() - addPersister( alias, propertyResultsMap, persister ); + final EntityPersister ownerPersister = alias2Persister.get( ownerAlias ); + final Type returnType = ownerPersister.getPropertyType( fetchReturn.getFetchable().getFetchableName() ); + if ( returnType instanceof CollectionType ) { + final String role = ownerPersister.getEntityName() + '.' + fetchReturn.getFetchable().getFetchableName(); + final Map propertyResultsMap = emptyMap(); //fetchReturn.getPropertyResultsMap() + addCollection( role, alias, propertyResultsMap ); + // collectionOwnerAliases.add( ownerAlias ); + } + else if ( returnType instanceof EntityType entityType ) { + final String returnEntityName = entityType.getAssociatedEntityName(); + final EntityPersister persister = getSQLLoadable( returnEntityName ); + final Map propertyResultsMap = emptyMap(); //fetchReturn.getPropertyResultsMap() + addPersister( alias, propertyResultsMap, persister ); + } } + // else already processed } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java index fdf8c1460ab0..b1303fd8f85f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java @@ -5,7 +5,6 @@ package org.hibernate.query.sqm.internal; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -59,6 +58,7 @@ import org.hibernate.sql.results.spi.ResultsConsumer; import org.hibernate.sql.results.spi.RowTransformer; +import static java.util.Collections.emptyList; import static org.hibernate.internal.util.ReflectHelper.isClass; import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray; import static org.hibernate.query.sqm.internal.QuerySqmImpl.CRITERIA_HQL_STRING; @@ -362,18 +362,16 @@ public T executeQuery(DomainQueryExecutionContext executionContext, ResultsC @Override public List performList(DomainQueryExecutionContext executionContext) { - if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { - return Collections.emptyList(); - } - return withCacheableSqmInterpretation( executionContext, null, listInterpreter ); + return executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 + ? emptyList() + : withCacheableSqmInterpretation( executionContext, null, listInterpreter ); } @Override public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { - if ( executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ) { - return EmptyScrollableResults.instance(); - } - return withCacheableSqmInterpretation( executionContext, scrollMode, scrollInterpreter ); + return executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 + ? EmptyScrollableResults.instance() + : withCacheableSqmInterpretation( executionContext, scrollMode, scrollInterpreter ); } private T withCacheableSqmInterpretation(DomainQueryExecutionContext executionContext, X context, SqmInterpreter interpreter) { @@ -500,7 +498,8 @@ private static CacheableSqmInterpretation buildCacheableSqmInterpretation( new SqmParameterMappingModelResolutionAccess() { @Override @SuppressWarnings("unchecked") public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { - return (MappingModelExpressible) sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get(parameter); + return (MappingModelExpressible) + sqmInterpretation.getSqmParameterMappingModelTypeResolutions().get( parameter ); } }, session @@ -509,7 +508,6 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter, Map, List>> jdbcParamsXref; private final Map, MappingModelExpressible> sqmParameterMappingModelTypes; private transient JdbcParameterBindings firstParameterBindings; @@ -535,30 +532,20 @@ private static class CacheableSqmInterpretation { CacheableSqmInterpretation( SelectStatement selectStatement, JdbcOperationQuerySelect jdbcSelect, - FromClauseAccess tableGroupAccess, Map, Map, List>> jdbcParamsXref, Map, MappingModelExpressible> sqmParameterMappingModelTypes, JdbcParameterBindings firstParameterBindings) { this.selectStatement = selectStatement; this.jdbcSelect = jdbcSelect; - this.tableGroupAccess = tableGroupAccess; this.jdbcParamsXref = jdbcParamsXref; this.sqmParameterMappingModelTypes = sqmParameterMappingModelTypes; this.firstParameterBindings = firstParameterBindings; } - SelectStatement getSelectStatement() { - return selectStatement; - } - JdbcOperationQuerySelect getJdbcSelect() { return jdbcSelect; } - FromClauseAccess getTableGroupAccess() { - return tableGroupAccess; - } - Map, Map, List>> getJdbcParamsXref() { return jdbcParamsXref; } @@ -566,14 +553,6 @@ Map, Map, List> public Map, MappingModelExpressible> getSqmParameterMappingModelTypes() { return sqmParameterMappingModelTypes; } - - JdbcParameterBindings getFirstParameterBindings() { - return firstParameterBindings; - } - - void setFirstParameterBindings(JdbcParameterBindings firstParameterBindings) { - this.firstParameterBindings = firstParameterBindings; - } } private static class MySqmJdbcExecutionContextAdapter extends SqmJdbcExecutionContextAdapter { @@ -602,6 +581,5 @@ public String getQueryIdentifier(String sql) { } return hql; } - } }