From ba05533a036e73ac119169392b0da49273dc2e5b Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 11 Jul 2024 19:13:56 +0200 Subject: [PATCH] HHH-18379 Allow passing row count estimate to pre-size collections --- .../java/org/hibernate/dialect/Dialect.java | 5 +- .../ast/internal/AbstractNaturalIdLoader.java | 26 ++--- .../CollectionElementLoaderByIndex.java | 4 +- .../internal/DatabaseSnapshotExecutor.java | 4 +- .../internal/EntityConcreteTypeLoader.java | 4 +- .../loader/ast/internal/LoaderHelper.java | 4 +- .../MultiIdEntityLoaderArrayParam.java | 8 +- .../internal/MultiIdEntityLoaderStandard.java | 4 +- .../ast/internal/MultiKeyLoadChunker.java | 7 +- .../MultiNaturalIdLoadingBatcher.java | 21 ++-- .../loader/ast/internal/SingleIdLoadPlan.java | 4 +- .../SingleUniqueKeyEntityLoaderStandard.java | 13 ++- .../internal/GeneratedValuesProcessor.java | 12 ++- .../query/results/JdbcValuesMappingImpl.java | 2 + .../query/results/ResultSetMappingImpl.java | 2 +- .../query/sql/internal/NativeQueryImpl.java | 6 +- .../internal/NativeSelectQueryPlanImpl.java | 8 +- .../internal/ConcreteSqmSelectQueryPlan.java | 44 +++++++-- .../query/sqm/internal/QuerySqmImpl.java | 2 +- .../sqm/internal/SqmSelectionQueryImpl.java | 2 +- .../internal/MatchingIdSelectionHelper.java | 11 ++- .../cte/AbstractCteMutationHandler.java | 9 +- .../internal/cte/CteInsertHandler.java | 8 +- .../temptable/InsertExecutionDelegate.java | 4 +- .../JdbcSelectExecutorStandardImpl.java | 29 +++++- .../sql/exec/spi/JdbcSelectExecutor.java | 99 +++++++++++++++++-- .../results/caching/QueryCachePutManager.java | 11 +++ .../QueryCachePutManagerEnabledImpl.java | 8 ++ .../RowTransformerSingularReturnImpl.java | 1 + .../internal/DeferredResultSetAccess.java | 19 +++- .../jdbc/internal/JdbcValuesCacheHit.java | 10 +- .../JdbcValuesMappingProducerStandard.java | 6 +- .../internal/JdbcValuesResultSetImpl.java | 30 +++++- .../jdbc/internal/ResultSetAccess.java | 8 ++ .../internal/StandardJdbcValuesMapping.java | 1 + .../sql/results/jdbc/spi/JdbcValues.java | 7 ++ .../results/jdbc/spi/JdbcValuesMapping.java | 3 +- .../sql/results/spi/ListResultsConsumer.java | 27 +++-- .../results/spi/ManagedResultConsumer.java | 80 +++++++++++++++ .../spi/ScrollableResultsConsumer.java | 1 + .../sql/results/spi/SingleResultConsumer.java | 54 ++++++++-- 41 files changed, 509 insertions(+), 99 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/sql/results/spi/ManagedResultConsumer.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index 413823e2f2..597c2d2486 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -197,6 +197,7 @@ import org.hibernate.type.spi.TypeConfiguration; import org.jboss.logging.Logger; import jakarta.persistence.TemporalType; +import org.checkerframework.checker.nullness.qual.Nullable; import static java.lang.Math.ceil; import static java.lang.Math.log; @@ -3189,7 +3190,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun */ public IdentifierHelper buildIdentifierHelper( IdentifierHelperBuilder builder, - DatabaseMetaData dbMetaData) throws SQLException { + @Nullable DatabaseMetaData dbMetaData) throws SQLException { builder.applyIdentifierCasing( dbMetaData ); builder.applyReservedWords( sqlKeywords ); builder.setNameQualifierSupport( getNameQualifierSupport() ); @@ -4356,7 +4357,7 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun * an exception. Just rethrow and Hibernate will * handle it. */ - public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { + public boolean supportsNamedParameters(@Nullable DatabaseMetaData databaseMetaData) throws SQLException { return databaseMetaData != null && databaseMetaData.supportsNamedParameters(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java index bf2aecef0b..e4603403bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/AbstractNaturalIdLoader.java @@ -50,6 +50,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.graph.*; import org.hibernate.sql.results.graph.internal.ImmutableFetchList; +import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import static java.util.Collections.emptyList; @@ -134,13 +135,14 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { final long startToken = sessionFactory.getStatistics().isStatisticsEnabled() ? System.nanoTime() : -1; - //noinspection unchecked final List results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, jdbcParamBindings, new NaturalIdLoaderWithOptionsExecutionContext( session, queryOptions ), - row -> (T) row[0], - ListResultsConsumer.UniqueSemantic.FILTER + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); if ( results.size() > 1 ) { @@ -230,13 +232,14 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { final Long startToken = statementStartHandler.apply( sessionFactory.getStatistics().isStatisticsEnabled() ); - //noinspection unchecked final List results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, jdbcParamBindings, new NaturalIdLoaderWithOptionsExecutionContext( session, queryOptions ), - row -> (L) row[0], - ListResultsConsumer.UniqueSemantic.FILTER + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); if ( results.size() > 1 ) { @@ -364,12 +367,11 @@ public abstract class AbstractNaturalIdLoader implements NaturalIdLoader { jdbcSelect, jdbcParamBindings, new NoCallbackExecutionContext( session ), - (row) -> { - // because we select the natural-id we want to "reduce" the result - assert row.length == 1; - return row[0]; - }, - ListResultsConsumer.UniqueSemantic.FILTER + // because we select the natural-id we want to "reduce" the result + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); switch ( results.size() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionElementLoaderByIndex.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionElementLoaderByIndex.java index e3de020477..42126c2e12 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionElementLoaderByIndex.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionElementLoaderByIndex.java @@ -150,7 +150,9 @@ public class CollectionElementLoaderByIndex implements Loader { jdbcParameterBindings, new BaseExecutionContext( session ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); if ( list.isEmpty() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java index b58483a51e..399c859d23 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/DatabaseSnapshotExecutor.java @@ -177,7 +177,9 @@ class DatabaseSnapshotExecutor { jdbcParameterBindings, new BaseExecutionContext( session ), RowTransformerDatabaseSnapshotImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); final int size = list.size(); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityConcreteTypeLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityConcreteTypeLoader.java index c58551f207..c14414202f 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityConcreteTypeLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/EntityConcreteTypeLoader.java @@ -93,7 +93,9 @@ public class EntityConcreteTypeLoader { jdbcParamBindings, new BaseExecutionContext( session ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.NONE + null, + ListResultsConsumer.UniqueSemantic.NONE, + 1 ); if ( results.isEmpty() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java index 62050a7492..1828a2d5fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderHelper.java @@ -243,7 +243,9 @@ public class LoaderHelper { session ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + ListResultsConsumer.UniqueSemantic.FILTER, + idsToInitialize.length ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java index 20cc4f9f06..c2fd880413 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderArrayParam.java @@ -39,7 +39,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; -import org.hibernate.sql.results.spi.ListResultsConsumer; +import org.hibernate.sql.results.spi.ManagedResultConsumer; import org.checkerframework.checker.nullness.qual.NonNull; @@ -190,12 +190,14 @@ public class MultiIdEntityLoaderArrayParam extends AbstractMultiIdEntityLoade jdbcParameterBindings ); - session.getJdbcServices().getJdbcSelectExecutor().list( + session.getJdbcServices().getJdbcSelectExecutor().executeQuery( jdbcSelectOperation, jdbcParameterBindings, new ExecutionContextWithSubselectFetchHandler( session, subSelectFetchableKeysHandler ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + idsToLoadFromDatabase.size(), + ManagedResultConsumer.INSTANCE ); for ( int i = 0; i < idsToLoadFromDatabaseResultIndexes.size(); i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java index 2d51672a94..6fe6016096 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiIdEntityLoaderStandard.java @@ -267,7 +267,9 @@ public class MultiIdEntityLoaderStandard extends AbstractMultiIdEntityLoader< jdbcParameterBindings, new ExecutionContextWithSubselectFetchHandler( session, subSelectFetchableKeysHandler ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + ListResultsConsumer.UniqueSemantic.FILTER, + idsInBatch.size() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java index 5939a8471e..f7b40b0c3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiKeyLoadChunker.java @@ -19,6 +19,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; +import org.hibernate.sql.results.spi.ManagedResultConsumer; /** * When the number of ids to initialize exceeds a certain threshold, IN-predicate based @@ -149,12 +150,14 @@ public class MultiKeyLoadChunker { return; } - session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( + session.getFactory().getJdbcServices().getJdbcSelectExecutor().executeQuery( jdbcSelect, jdbcParameterBindings, sqlExecutionContextCreator.createContext( jdbcParameterBindings, session ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + nonNullCounter, + ManagedResultConsumer.INSTANCE ); boundaryListener.chunkBoundaryNotification( startIndex, nonNullCounter ); diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java index efa83d6482..bf880c0249 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/MultiNaturalIdLoadingBatcher.java @@ -10,8 +10,6 @@ import java.util.ArrayList; import java.util.List; import org.hibernate.LockOptions; -import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -20,10 +18,8 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.loader.ast.spi.MultiNaturalIdLoadOptions; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.SqlAstTranslatorFactory; -import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; @@ -97,6 +93,7 @@ public class MultiNaturalIdLoadingBatcher { final JdbcParameterBindingsImpl jdbcParamBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() ); int offset = 0; + int size = 0; for ( int i = 0; i < naturalIdValues.length; i++ ) { final Object bindValue = keyValueResolver.resolveKeyToLoad( naturalIdValues[ i ], session ); @@ -108,14 +105,16 @@ public class MultiNaturalIdLoadingBatcher { jdbcParameters, session ); + size++; } if ( offset == jdbcParameters.size() ) { // we've hit the batch mark - final List batchResults = performLoad( jdbcParamBindings, session ); + final List batchResults = performLoad( jdbcParamBindings, session, size ); multiLoadResults.addAll( batchResults ); jdbcParamBindings.clear(); offset = 0; + size = 0; } } @@ -129,15 +128,19 @@ public class MultiNaturalIdLoadingBatcher { jdbcParameters, session ); + size++; } - final List batchResults = performLoad( jdbcParamBindings, session ); + final List batchResults = performLoad( jdbcParamBindings, session, size ); multiLoadResults.addAll( batchResults ); } return multiLoadResults; } - private List performLoad(JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session) { + private List performLoad( + JdbcParameterBindings jdbcParamBindings, + SharedSessionContractImplementor session, + int size) { final SubselectFetch.RegistrationHandler subSelectFetchableKeysHandler; if ( session.getLoadQueryInfluencers().hasSubselectLoadableCollections( entityDescriptor.getEntityPersister() ) ) { @@ -160,7 +163,9 @@ public class MultiNaturalIdLoadingBatcher { jdbcParamBindings, new ExecutionContextWithSubselectFetchHandler( session, subSelectFetchableKeysHandler ), RowTransformerStandardImpl.instance(), - ListResultsConsumer.UniqueSemantic.FILTER + null, + ListResultsConsumer.UniqueSemantic.FILTER, + size ); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java index 7e952322fd..6cac0c4dbc 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleIdLoadPlan.java @@ -154,7 +154,9 @@ public class SingleIdLoadPlan implements SingleEntityLoadPlan { callback ), getRowTransformer(), - singleResultExpected ? ListResultsConsumer.UniqueSemantic.ASSERT : ListResultsConsumer.UniqueSemantic.FILTER + null, + singleResultExpected ? ListResultsConsumer.UniqueSemantic.ASSERT : ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); if ( list.isEmpty() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java index 242f7b1e0c..48d060cc21 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/SingleUniqueKeyEntityLoaderStandard.java @@ -35,6 +35,7 @@ import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import static java.util.Collections.singletonList; @@ -128,8 +129,10 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn jdbcSelect, jdbcParameterBindings, new SingleUKEntityLoaderExecutionContext( uniqueKeyAttributePath, ukValue, session, readOnly ), - row -> row[0], - ListResultsConsumer.UniqueSemantic.FILTER + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); switch ( list.size() ) { @@ -183,8 +186,10 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEn jdbcSelect, jdbcParameterBindings, new NoCallbackExecutionContext( session ), - row -> row[0], - ListResultsConsumer.UniqueSemantic.FILTER + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.FILTER, + 1 ); assert list.size() == 1; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java index 97e48348b6..a4a67035dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/GeneratedValuesProcessor.java @@ -30,6 +30,7 @@ import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParametersList; +import org.hibernate.sql.results.internal.RowTransformerArrayImpl; import static org.hibernate.internal.util.NullnessUtil.castNonNull; import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER; @@ -171,8 +172,15 @@ public class GeneratedValuesProcessor { private List executeSelect(Object id, SharedSessionContractImplementor session) { final JdbcParameterBindings jdbcParamBindings = getJdbcParameterBindings( id, session ); - return session.getFactory().getJdbcServices().getJdbcSelectExecutor() - .list( jdbcSelect, jdbcParamBindings, new NoCallbackExecutionContext(session), (row) -> row, FILTER ); + return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( + jdbcSelect, + jdbcParamBindings, + new NoCallbackExecutionContext( session ), + RowTransformerArrayImpl.INSTANCE, + null, + FILTER, + 1 + ); } private JdbcParameterBindings getJdbcParameterBindings(Object id, SharedSessionContractImplementor session) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java index 7aa0935805..973c2bdcfb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/JdbcValuesMappingImpl.java @@ -9,6 +9,7 @@ package org.hibernate.query.results; import java.util.List; import java.util.Map; +import org.hibernate.Internal; import org.hibernate.LockMode; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.results.graph.DomainResult; @@ -21,6 +22,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; * * @author Steve Ebersole */ +@Internal public class JdbcValuesMappingImpl extends StandardJdbcValuesMapping { private final int rowSize; diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java index 132d5ad2df..bed3143a19 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingImpl.java @@ -30,8 +30,8 @@ import org.hibernate.query.named.NamedResultSetMappingMemento; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.basic.BasicResult; -import org.hibernate.sql.results.graph.basic.BasicResultAssembler; import org.hibernate.sql.results.graph.entity.EntityResult; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; 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 d96639eccc..991f5a342e 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 @@ -473,8 +473,8 @@ public class NativeQueryImpl getTimeout(), getFetchSize(), getComment(), - getFirstResult(), - getMaxResults(), + getQueryOptions().getLimit().getFirstRow(), + getQueryOptions().getLimit().getMaxRows(), getHints() ); } @@ -636,7 +636,7 @@ public class NativeQueryImpl return QueryOptions.NONE; } }; - return createCountQueryPlan().executeQuery( context, new SingleResultConsumer<>() ); + return createCountQueryPlan().executeQuery( context, SingleResultConsumer.instance() ); } @Override 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 9a71271731..c68cc1c97d 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 @@ -95,10 +95,7 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), null, null, - sqlString -> executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - .prepareQueryStatement( sqlString, false, null ), + -1, resultsConsumer ); } @@ -183,7 +180,8 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { scrollMode, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ), - null + null, + -1 ); } } 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 d9cf9cc5f5..eedb762ccd 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 @@ -32,12 +32,16 @@ import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess; import org.hibernate.query.sqm.sql.SqmTranslation; +import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation; import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation; import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.query.sqm.tree.select.SqmSelection; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.spi.FromClauseAccess; +import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.expression.JdbcParameter; +import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; @@ -109,16 +113,18 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { jdbcParameterBindings ); session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true ); + final Expression fetchExpression = sqmInterpretation.selectStatement.getQueryPart() + .getFetchClauseExpression(); + final int resultCountEstimate = fetchExpression != null + ? interpretIntExpression( fetchExpression, jdbcParameterBindings ) + : -1; return session.getFactory().getJdbcServices().getJdbcSelectExecutor().executeQuery( jdbcSelect, jdbcParameterBindings, listInterpreterExecutionContext( hql, executionContext, jdbcSelect, subSelectFetchKeyHandler ), rowTransformer, null, - sql -> executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - .prepareQueryStatement( sql, false, null ), + resultCountEstimate, resultsConsumer ); } @@ -137,6 +143,11 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { jdbcParameterBindings ); session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true ); + final Expression fetchExpression = sqmInterpretation.selectStatement.getQueryPart() + .getFetchClauseExpression(); + final int resultCountEstimate = fetchExpression != null + ? interpretIntExpression( fetchExpression, jdbcParameterBindings ) + : -1; //noinspection unchecked return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( jdbcSelect, @@ -144,7 +155,8 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { listInterpreterExecutionContext( hql, executionContext, jdbcSelect, subSelectFetchKeyHandler ), rowTransformer, (Class) executionContext.getResultType(), - uniqueSemantic + uniqueSemantic, + resultCountEstimate ); } finally { @@ -167,12 +179,18 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { .getJdbcServices() .getJdbcSelectExecutor(); session.autoFlushIfRequired( jdbcSelect.getAffectedTableNames(), true ); + final Expression fetchExpression = sqmInterpretation.selectStatement.getQueryPart() + .getFetchClauseExpression(); + final int resultCountEstimate = fetchExpression != null + ? interpretIntExpression( fetchExpression, jdbcParameterBindings ) + : -1; return jdbcSelectExecutor.scroll( jdbcSelect, scrollMode, jdbcParameterBindings, new SqmJdbcExecutionContextAdapter( executionContext, jdbcSelect ), - rowTransformer + rowTransformer, + resultCountEstimate ); } finally { @@ -200,6 +218,20 @@ public class ConcreteSqmSelectQueryPlan implements SelectQueryPlan { return new MySqmJdbcExecutionContextAdapter( executionContext, jdbcSelect, subSelectFetchKeyHandler, hql ); } + protected static int interpretIntExpression(Expression expression, JdbcParameterBindings jdbcParameterBindings) { + if ( expression instanceof Literal ) { + return ( (Number) ( (Literal) expression ).getLiteralValue() ).intValue(); + } + else if ( expression instanceof JdbcParameter ) { + return (int) jdbcParameterBindings.getBinding( (JdbcParameter) expression ).getBindValue(); + } + else if ( expression instanceof SqmParameterInterpretation ) { + return (int) jdbcParameterBindings.getBinding( (JdbcParameter) ( (SqmParameterInterpretation) expression ).getResolvedExpression() ) + .getBindValue(); + } + return -1; + } + private static List> selections(SqmSelectStatement sqm) { return sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index c56c70b652..82b12007bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -493,7 +493,7 @@ public class QuerySqmImpl }; final SqmSelectStatement sqmStatement = (SqmSelectStatement) getSqmStatement(); return buildConcreteQueryPlan( sqmStatement.createCountQuery(), Long.class, null, getQueryOptions() ) - .executeQuery( context, new SingleResultConsumer<>() ); + .executeQuery( context, SingleResultConsumer.instance() ); } protected List doList() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java index 0f9ddf564c..3792553909 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java @@ -295,7 +295,7 @@ public class SqmSelectionQueryImpl extends AbstractSqmSelectionQuery } }; return buildConcreteQueryPlan( getSqmStatement().createCountQuery(), Long.class, null, getQueryOptions() ) - .executeQuery( context, new SingleResultConsumer<>() ); + .executeQuery( context, SingleResultConsumer.instance() ); } protected List doList() { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 97659c1ee1..39457afedd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -48,6 +48,8 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.sql.results.internal.RowTransformerArrayImpl; +import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.RowTransformer; @@ -318,18 +320,19 @@ public class MatchingIdSelectionHelper { ); lockOptions.setLockMode( lockMode ); - final RowTransformer rowTransformer; + final RowTransformer rowTransformer; if ( sqmQuerySpec.getSelectClause().getSelections().size() == 1 ) { - rowTransformer = row -> row[0]; + rowTransformer = RowTransformerSingularReturnImpl.instance(); } else { - rowTransformer = row -> row; + rowTransformer = RowTransformerArrayImpl.INSTANCE; } + //noinspection unchecked return jdbcServices.getJdbcSelectExecutor().list( idSelectJdbcOperation, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), - rowTransformer, + (RowTransformer) rowTransformer, ListResultsConsumer.UniqueSemantic.FILTER ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java index 3c4e201811..430a312302 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java @@ -57,6 +57,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -206,14 +207,16 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler select, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), - row -> row[0], - ListResultsConsumer.UniqueSemantic.NONE + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.NONE, + 1 ); return ( (Number) list.get( 0 ) ).intValue(); } /** - * Used by Hibernate Raective + * Used by Hibernate Reactive */ protected Expression createCountStar( SessionFactoryImplementor factory, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index a9852e693a..92c321c962 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -104,6 +104,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.basic.BasicResult; +import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.generator.Generator; @@ -625,8 +626,10 @@ public class CteInsertHandler implements InsertHandler { select, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.omittingLockingAndPaging( executionContext ), - row -> row[0], - ListResultsConsumer.UniqueSemantic.NONE + RowTransformerSingularReturnImpl.instance(), + null, + ListResultsConsumer.UniqueSemantic.NONE, + 1 ); return ( (Number) list.get( 0 ) ).intValue(); } @@ -635,7 +638,6 @@ public class CteInsertHandler implements InsertHandler { SessionFactoryImplementor factory, MultiTableSqmMutationConverter sqmConverter) { final SqmExpression arg = new SqmStar( factory.getNodeBuilder() ); - final TypeConfiguration typeConfiguration = factory.getJpaMetamodel().getTypeConfiguration(); return factory.getQueryEngine().getSqmFunctionRegistry().findFunctionDescriptor( "count" ).generateSqmExpression( arg, null, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index 5ca044c24b..67ba56a0d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -417,7 +417,9 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio JdbcParameterBindings.NO_BINDINGS, executionContext, null, - ListResultsConsumer.UniqueSemantic.NONE + null, + ListResultsConsumer.UniqueSemantic.NONE, + rows ); entityTableToRootIdentity = new LinkedHashMap<>( list.size() ); for ( Object o : list ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java index 07d7910d63..c5884fdd85 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/JdbcSelectExecutorStandardImpl.java @@ -70,6 +70,28 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { Class domainResultType, Function statementCreator, ResultsConsumer resultsConsumer) { + return executeQuery( + jdbcSelect, + jdbcParameterBindings, + executionContext, + rowTransformer, + domainResultType, + -1, + statementCreator, + resultsConsumer + ); + } + + @Override + public T executeQuery( + JdbcOperationQuerySelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Class domainResultType, + int resultCountEstimate, + Function statementCreator, + ResultsConsumer resultsConsumer) { final PersistenceContext persistenceContext = executionContext.getSession().getPersistenceContext(); boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly(); Boolean readOnly = executionContext.getQueryOptions().isReadOnly(); @@ -85,6 +107,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { executionContext, rowTransformer, domainResultType, + resultCountEstimate, statementCreator, resultsConsumer ); @@ -102,6 +125,7 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { ExecutionContext executionContext, RowTransformer rowTransformer, Class domainResultType, + int resultCountEstimate, Function statementCreator, ResultsConsumer resultsConsumer) { @@ -109,7 +133,8 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { jdbcSelect, jdbcParameterBindings, executionContext, - statementCreator + statementCreator, + resultCountEstimate ); final JdbcValues jdbcValues = resolveJdbcValuesSource( executionContext.getQueryIdentifier( deferredResultSetAccess.getFinalSql() ), @@ -198,8 +223,6 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor { jdbcValues ); - rowReader.startLoading( rowProcessingState ); - final T result = resultsConsumer.consume( jdbcValues, session, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelectExecutor.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelectExecutor.java index 53dda7bd50..cb12a8abea 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelectExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcSelectExecutor.java @@ -51,6 +51,55 @@ public interface JdbcSelectExecutor { Function statementCreator, ResultsConsumer resultsConsumer); + /** + * @since 6.6 + */ + default T executeQuery( + JdbcOperationQuerySelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Class domainResultType, + int resultCountEstimate, + ResultsConsumer resultsConsumer) { + return executeQuery( + jdbcSelect, + jdbcParameterBindings, + executionContext, + rowTransformer, + domainResultType, + resultCountEstimate, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareQueryStatement( sql, false, null ), + resultsConsumer + ); + } + + /** + * @since 6.6 + */ + default T executeQuery( + JdbcOperationQuerySelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Class domainResultType, + int resultCountEstimate, + Function statementCreator, + ResultsConsumer resultsConsumer) { + return executeQuery( + jdbcSelect, + jdbcParameterBindings, + executionContext, + rowTransformer, + domainResultType, + statementCreator, + resultsConsumer + ); + } + default List list( JdbcOperationQuerySelect jdbcSelect, JdbcParameterBindings jdbcParameterBindings, @@ -67,6 +116,28 @@ public interface JdbcSelectExecutor { RowTransformer rowTransformer, Class requestedJavaType, ListResultsConsumer.UniqueSemantic uniqueSemantic) { + return list( + jdbcSelect, + jdbcParameterBindings, + executionContext, + rowTransformer, + requestedJavaType, + uniqueSemantic, + -1 + ); + } + + /** + * @since 6.6 + */ + default List list( + JdbcOperationQuerySelect jdbcSelect, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + Class requestedJavaType, + ListResultsConsumer.UniqueSemantic uniqueSemantic, + int resultCountEstimate) { // Only do auto flushing for top level queries return executeQuery( jdbcSelect, @@ -74,10 +145,7 @@ public interface JdbcSelectExecutor { executionContext, rowTransformer, requestedJavaType, - sql -> executionContext.getSession() - .getJdbcCoordinator() - .getStatementPreparer() - .prepareQueryStatement( sql, false, null ), + resultCountEstimate, ListResultsConsumer.instance( uniqueSemantic ) ); } @@ -88,17 +156,30 @@ public interface JdbcSelectExecutor { JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext, RowTransformer rowTransformer) { + return scroll( jdbcSelect, scrollMode, jdbcParameterBindings, executionContext, rowTransformer, -1 ); + } + + /** + * @since 6.6 + */ + default ScrollableResultsImplementor scroll( + JdbcOperationQuerySelect jdbcSelect, + ScrollMode scrollMode, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext, + RowTransformer rowTransformer, + int resultCountEstimate) { return executeQuery( jdbcSelect, jdbcParameterBindings, getScrollContext( executionContext ), rowTransformer, null, - sql -> executionContext.getSession().getJdbcCoordinator().getStatementPreparer().prepareQueryStatement( - sql, - false, - scrollMode - ), + resultCountEstimate, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareQueryStatement( sql, false, scrollMode ), ScrollableResultsConsumer.instance() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/caching/QueryCachePutManager.java b/hibernate-core/src/main/java/org/hibernate/sql/results/caching/QueryCachePutManager.java index 3b00012f63..816d6eafca 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/caching/QueryCachePutManager.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/caching/QueryCachePutManager.java @@ -14,5 +14,16 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; public interface QueryCachePutManager { void registerJdbcRow(Object values); + /** + * @deprecated Use {@link #finishUp(int, SharedSessionContractImplementor)} instead + */ + @Deprecated(forRemoval = true, since = "6.6") void finishUp(SharedSessionContractImplementor session); + + /** + * @since 6.6 + */ + default void finishUp(int resultCount, SharedSessionContractImplementor session) { + finishUp( session ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/caching/internal/QueryCachePutManagerEnabledImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/caching/internal/QueryCachePutManagerEnabledImpl.java index 0a192723d9..7b0ac300fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/caching/internal/QueryCachePutManagerEnabledImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/caching/internal/QueryCachePutManagerEnabledImpl.java @@ -51,6 +51,14 @@ public class QueryCachePutManagerEnabledImpl implements QueryCachePutManager { @Override public void finishUp(SharedSessionContractImplementor session) { + finishUp( dataToCache.size() - 1, session ); + } + + @Override + public void finishUp(int resultCount, SharedSessionContractImplementor session) { + if ( !dataToCache.isEmpty() ) { + dataToCache.add( resultCount ); + } final boolean put = queryCache.put( queryKey, dataToCache, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerSingularReturnImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerSingularReturnImpl.java index 6d6b8a3b83..434243ee22 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerSingularReturnImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowTransformerSingularReturnImpl.java @@ -32,6 +32,7 @@ public class RowTransformerSingularReturnImpl implements RowTransformer { @Override @SuppressWarnings("unchecked") public R transformRow(Object[] row) { + assert row.length == 1; return (R) row[0]; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java index 4ec2c07eb0..dcc037b688 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/DeferredResultSetAccess.java @@ -52,6 +52,7 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { private final Limit limit; private final LimitHandler limitHandler; private final boolean usesFollowOnLocking; + private final int resultCountEstimate; private PreparedStatement preparedStatement; private ResultSet resultSet; @@ -60,13 +61,15 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { JdbcOperationQuerySelect jdbcSelect, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext, - Function statementCreator) { + Function statementCreator, + int resultCountEstimate) { super( executionContext.getSession() ); this.jdbcParameterBindings = jdbcParameterBindings; this.executionContext = executionContext; this.jdbcSelect = jdbcSelect; this.statementCreator = statementCreator; this.sqlStatementLogger = executionContext.getSession().getJdbcServices().getSqlStatementLogger(); + this.resultCountEstimate = resultCountEstimate; final QueryOptions queryOptions = executionContext.getQueryOptions(); if ( queryOptions == null ) { @@ -334,4 +337,18 @@ public class DeferredResultSetAccess extends AbstractResultSetAccess { logicalConnection.afterStatement(); } + + @Override + public int getResultCountEstimate() { + if ( limit != null && limit.getMaxRows() != null ) { + return limit.getMaxRows(); + } + if ( jdbcSelect.getLimitParameter() != null ) { + return (int) jdbcParameterBindings.getBinding( jdbcSelect.getLimitParameter() ).getBindValue(); + } + if ( resultCountEstimate > 0 ) { + return resultCountEstimate; + } + return super.getResultCountEstimate(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java index 832a904bdf..dccb87f2d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java @@ -27,12 +27,15 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues { private final JdbcValuesMapping resolvedMapping; private final int[] valueIndexesToCacheIndexes; private final int offset; + private final int resultCount; private int position = -1; public JdbcValuesCacheHit(List cachedResults, JdbcValuesMapping resolvedMapping) { + // See QueryCachePutManagerEnabledImpl for what is being put into the cached results this.cachedResults = cachedResults; this.offset = !cachedResults.isEmpty() && cachedResults.get( 0 ) instanceof JdbcValuesMetadata ? 1 : 0; - this.numberOfRows = cachedResults.size() - offset; + this.numberOfRows = cachedResults.size() - offset - 1; + this.resultCount = cachedResults.isEmpty() ? 0 : (int) cachedResults.get( cachedResults.size() - 1 ); this.resolvedMapping = resolvedMapping; final BitSet valueIndexesToCache = new BitSet(); @@ -252,4 +255,9 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues { @Override public void setFetchSize(int fetchSize) {} + + @Override + public int getResultCountEstimate() { + return resultCount; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java index aeb830f208..48ff65a02c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesMappingProducerStandard.java @@ -15,6 +15,7 @@ import org.hibernate.query.NativeQuery; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; @@ -61,6 +62,9 @@ public class JdbcValuesMappingProducerStandard implements JdbcValuesMappingProdu if ( resolvedSelections == null ) { return resolvedMapping; } - return new StandardJdbcValuesMapping( resolvedSelections, resolvedMapping.getDomainResults() ); + return new StandardJdbcValuesMapping( + resolvedSelections, + resolvedMapping.getDomainResults() + ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java index 05d3728fbe..9f0073ccb7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java @@ -19,6 +19,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.exception.DataException; import org.hibernate.exception.LockTimeoutException; +import org.hibernate.query.spi.Limit; import org.hibernate.query.spi.QueryOptions; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.exec.ExecutionException; @@ -42,6 +43,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { private final JdbcValuesMapping valuesMapping; private final ExecutionContext executionContext; private final boolean usesFollowOnLocking; + private final int resultCountEstimate; private final SqlSelection[] sqlSelections; private final BitSet initializedIndexes; @@ -51,6 +53,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { // Contains the size of the row to cache, or if the value is negative, // represents the inverted index of the single value to cache private final int rowToCacheSize; + private int resultCount; public JdbcValuesResultSetImpl( ResultSetAccess resultSetAccess, @@ -72,6 +75,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { this.valuesMapping = valuesMapping; this.executionContext = executionContext; this.usesFollowOnLocking = usesFollowOnLocking; + this.resultCountEstimate = determineResultCountEstimate( resultSetAccess, queryOptions, executionContext ); final int rowSize = valuesMapping.getRowSize(); this.sqlSelections = new SqlSelection[rowSize]; @@ -120,6 +124,22 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { } } + private int determineResultCountEstimate( + ResultSetAccess resultSetAccess, + QueryOptions queryOptions, + ExecutionContext executionContext) { + final Limit limit = queryOptions.getLimit(); + if ( limit != null && limit.getMaxRows() != null ) { + return limit.getMaxRows(); + } + + final int resultCountEstimate = resultSetAccess.getResultCountEstimate(); + if ( resultCountEstimate > 0 ) { + return resultCountEstimate; + } + return -1; + } + private static QueryCachePutManager resolveQueryCachePutManager( ExecutionContext executionContext, QueryOptions queryOptions, @@ -330,7 +350,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { @Override public final void finishUp(SharedSessionContractImplementor session) { if ( queryCachePutManager != null ) { - queryCachePutManager.finishUp( session ); + queryCachePutManager.finishUp( resultCount, session ); } resultSetAccess.release(); } @@ -348,6 +368,9 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { @Override public void finishRowProcessing(RowProcessingState rowProcessingState, boolean wasAdded) { if ( queryCachePutManager != null ) { + if ( wasAdded ) { + resultCount++; + } final Object objectToCache; if ( valueIndexesToCacheIndexes == null ) { objectToCache = Arrays.copyOf( currentRowJdbcValues, currentRowJdbcValues.length ); @@ -405,4 +428,9 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { throw makeExecutionException( "Error calling ResultSet.setFetchSize()", e ); } } + + @Override + public int getResultCountEstimate() { + return resultCountEstimate; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/ResultSetAccess.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/ResultSetAccess.java index cf20910c82..9083a60df9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/ResultSetAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/ResultSetAccess.java @@ -31,6 +31,14 @@ public interface ResultSetAccess extends JdbcValuesMetadata { ResultSet getResultSet(); SessionFactoryImplementor getFactory(); void release(); + /** + * The estimate for the amount of results that can be expected for pre-sizing collections. + * May return zero or negative values if the count can not be reasonably estimated. + * @since 6.6 + */ + default int getResultCountEstimate() { + return -1; + } default int getColumnCount() { try { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java index 042a009e3f..ea833f1b5f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/StandardJdbcValuesMapping.java @@ -35,6 +35,7 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingResolution; * @author Steve Ebersole */ public class StandardJdbcValuesMapping implements JdbcValuesMapping { + private final List sqlSelections; private final List> domainResults; private JdbcValuesMappingResolutionImpl resolution; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java index 3819eacf85..5b4149c75c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java @@ -84,4 +84,11 @@ public interface JdbcValues { void finishUp(SharedSessionContractImplementor session); void setFetchSize(int fetchSize); + + /** + * The estimate for the amount of results that can be expected for pre-sizing collections. + * May return zero or negative values if the count can not be reasonably estimated. + * @since 6.6 + */ + int getResultCountEstimate(); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValuesMapping.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValuesMapping.java index ce31dd93c5..bf1b22c672 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValuesMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValuesMapping.java @@ -10,9 +10,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.ast.spi.SqlSelection; /** @@ -38,4 +36,5 @@ public interface JdbcValuesMapping { JdbcValuesMappingResolution resolveAssemblers(SessionFactoryImplementor sessionFactory); LockMode determineDefaultLockMode(String alias, LockMode defaultLockMode); + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java index a3b5ebfd7f..f60f7bd084 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java @@ -33,6 +33,13 @@ import org.checkerframework.checker.nullness.qual.Nullable; * @author Steve Ebersole */ public class ListResultsConsumer implements ResultsConsumer, R> { + + /** + * Let's be reasonable, a row estimate greater than 1M rows is probably either a mis-estimation or bug, + * so let's set 2^20 which is a bit above 1M as maximum collection size. + */ + private static final int INITIAL_COLLECTION_SIZE_LIMIT = 1 << 20; + private static final ListResultsConsumer NEVER_DE_DUP_CONSUMER = new ListResultsConsumer<>( UniqueSemantic.NEVER ); private static final ListResultsConsumer ALLOW_DE_DUP_CONSUMER = new ListResultsConsumer<>( UniqueSemantic.ALLOW ); private static final ListResultsConsumer IGNORE_DUP_CONSUMER = new ListResultsConsumer<>( UniqueSemantic.NONE ); @@ -98,11 +105,12 @@ public class ListResultsConsumer implements ResultsConsumer, R> { } private static class Results { - private final List results = new ArrayList<>(); + private final List results; private final JavaType resultJavaType; - public Results(JavaType resultJavaType) { + public Results(JavaType resultJavaType, int initialSize) { this.resultJavaType = resultJavaType; + this.results = initialSize > 0 ? new ArrayList<>( initialSize ) : new ArrayList<>(); } public boolean addUnique(R result) { @@ -127,10 +135,11 @@ public class ListResultsConsumer implements ResultsConsumer, R> { private static class EntityResult extends Results { private static final Object DUMP_VALUE = new Object(); - private final IdentityHashMap added = new IdentityHashMap<>(); + private final IdentityHashMap added; - public EntityResult(JavaType resultJavaType) { - super( resultJavaType ); + public EntityResult(JavaType resultJavaType, int initialSize) { + super( resultJavaType, initialSize ); + added = initialSize > 0 ? new IdentityHashMap<>( initialSize ) : new IdentityHashMap<>(); } public boolean addUnique(R result) { @@ -153,6 +162,9 @@ public class ListResultsConsumer implements ResultsConsumer, R> { final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final TypeConfiguration typeConfiguration = session.getTypeConfiguration(); final QueryOptions queryOptions = rowProcessingState.getQueryOptions(); + + rowReader.startLoading( rowProcessingState ); + RuntimeException ex = null; persistenceContext.beforeLoad(); persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); @@ -164,15 +176,16 @@ public class ListResultsConsumer implements ResultsConsumer, R> { ); final boolean isEntityResultType = domainResultJavaType instanceof EntityJavaType; + final int initialCollectionSize = Math.min( jdbcValues.getResultCountEstimate(), INITIAL_COLLECTION_SIZE_LIMIT ); final Results results; if ( isEntityResultType && ( uniqueSemantic == UniqueSemantic.ALLOW || uniqueSemantic == UniqueSemantic.FILTER ) ) { - results = new EntityResult<>( domainResultJavaType ); + results = new EntityResult<>( domainResultJavaType, initialCollectionSize ); } else { - results = new Results<>( domainResultJavaType ); + results = new Results<>( domainResultJavaType, initialCollectionSize ); } int readRows = 0; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ManagedResultConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ManagedResultConsumer.java new file mode 100644 index 0000000000..22d9dabeb8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ManagedResultConsumer.java @@ -0,0 +1,80 @@ +/* + * 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.sql.results.spi; + +import org.hibernate.Incubating; +import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl; +import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl; +import org.hibernate.sql.results.jdbc.spi.JdbcValues; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; + +/** + * Reads rows without producing a result. + * + * @since 6.6 + */ +@Incubating +public class ManagedResultConsumer implements ResultsConsumer { + + public static final ManagedResultConsumer INSTANCE = new ManagedResultConsumer(); + + @Override + public Void consume( + JdbcValues jdbcValues, + SharedSessionContractImplementor session, + JdbcValuesSourceProcessingOptions processingOptions, + JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState, + RowProcessingStateStandardImpl rowProcessingState, + RowReader rowReader) { + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + RuntimeException ex = null; + persistenceContext.beforeLoad(); + persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); + try { + rowReader.startLoading( rowProcessingState ); + while ( rowProcessingState.next() ) { + rowReader.readRow( rowProcessingState ); + rowProcessingState.finishRowProcessing( true ); + } + rowReader.finishUp( rowProcessingState ); + jdbcValuesSourceProcessingState.finishUp( true ); + return null; + } + catch (RuntimeException e) { + ex = e; + } + finally { + try { + jdbcValues.finishUp( session ); + persistenceContext.afterLoad(); + persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState ); + persistenceContext.initializeNonLazyCollections(); + } + catch (RuntimeException e) { + if ( ex != null ) { + ex.addSuppressed( e ); + } + else { + ex = e; + } + } + finally { + if ( ex != null ) { + throw ex; + } + } + } + throw new IllegalStateException( "Should not reach this" ); + } + + @Override + public boolean canResultsBeCached() { + return false; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ScrollableResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ScrollableResultsConsumer.java index 382b033268..7100efeb4d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ScrollableResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ScrollableResultsConsumer.java @@ -42,6 +42,7 @@ public class ScrollableResultsConsumer implements ResultsConsumer rowReader) { + rowReader.startLoading( rowProcessingState ); if ( containsCollectionFetches( jdbcValues.getValuesMapping() ) ) { return new FetchingScrollableResultsImpl<>( jdbcValues, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/SingleResultConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/SingleResultConsumer.java index 28ac3f517e..b0f4dfd1bd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/SingleResultConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/SingleResultConsumer.java @@ -7,6 +7,7 @@ package org.hibernate.sql.results.spi; import org.hibernate.Incubating; +import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.SelectionQuery; import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl; @@ -23,6 +24,14 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; */ @Incubating public class SingleResultConsumer implements ResultsConsumer { + + private static final SingleResultConsumer INSTANCE = new SingleResultConsumer<>(); + + public static SingleResultConsumer instance() { + //noinspection unchecked + return (SingleResultConsumer) INSTANCE; + } + @Override public T consume( JdbcValues jdbcValues, @@ -31,13 +40,44 @@ public class SingleResultConsumer implements ResultsConsumer { JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState, RowProcessingStateStandardImpl rowProcessingState, RowReader rowReader) { - rowReader.startLoading( rowProcessingState ); - rowProcessingState.next(); - final T result = rowReader.readRow( rowProcessingState ); - rowProcessingState.finishRowProcessing( true ); - rowReader.finishUp( rowProcessingState ); - jdbcValuesSourceProcessingState.finishUp( false ); - return result; + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + RuntimeException ex = null; + persistenceContext.beforeLoad(); + persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); + try { + rowReader.startLoading( rowProcessingState ); + rowProcessingState.next(); + final T result = rowReader.readRow( rowProcessingState ); + rowProcessingState.finishRowProcessing( true ); + rowReader.finishUp( rowProcessingState ); + jdbcValuesSourceProcessingState.finishUp( true ); + return result; + } + catch (RuntimeException e) { + ex = e; + } + finally { + try { + jdbcValues.finishUp( session ); + persistenceContext.afterLoad(); + persistenceContext.getLoadContexts().deregister( jdbcValuesSourceProcessingState ); + persistenceContext.initializeNonLazyCollections(); + } + catch (RuntimeException e) { + if ( ex != null ) { + ex.addSuppressed( e ); + } + else { + ex = e; + } + } + finally { + if ( ex != null ) { + throw ex; + } + } + } + throw new IllegalStateException( "Should not reach this" ); } @Override