HHH-18379 Allow passing row count estimate to pre-size collections

This commit is contained in:
Christian Beikov 2024-07-11 19:13:56 +02:00
parent 33b2e36035
commit ba05533a03
41 changed files with 509 additions and 99 deletions

View File

@ -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();
}

View File

@ -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<T> implements NaturalIdLoader<T> {
final long startToken = sessionFactory.getStatistics().isStatisticsEnabled() ? System.nanoTime() : -1;
//noinspection unchecked
final List<T> 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<T> implements NaturalIdLoader<T> {
final Long startToken = statementStartHandler.apply( sessionFactory.getStatistics().isStatisticsEnabled() );
//noinspection unchecked
final List<L> 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<T> implements NaturalIdLoader<T> {
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
RowTransformerSingularReturnImpl.instance(),
null,
ListResultsConsumer.UniqueSemantic.FILTER,
1
);
switch ( results.size() ) {

View File

@ -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() ) {

View File

@ -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();

View File

@ -93,7 +93,9 @@ public class EntityConcreteTypeLoader {
jdbcParamBindings,
new BaseExecutionContext( session ),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.NONE
null,
ListResultsConsumer.UniqueSemantic.NONE,
1
);
if ( results.isEmpty() ) {

View File

@ -243,7 +243,9 @@ public class LoaderHelper {
session
),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
null,
ListResultsConsumer.UniqueSemantic.FILTER,
idsToInitialize.length
);
}
}

View File

@ -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<E> 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++ ) {

View File

@ -267,7 +267,9 @@ public class MultiIdEntityLoaderStandard<T> extends AbstractMultiIdEntityLoader<
jdbcParameterBindings,
new ExecutionContextWithSubselectFetchHandler( session, subSelectFetchableKeysHandler ),
RowTransformerStandardImpl.instance(),
ListResultsConsumer.UniqueSemantic.FILTER
null,
ListResultsConsumer.UniqueSemantic.FILTER,
idsInBatch.size()
);
}

View File

@ -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<K> {
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 );

View File

@ -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<E> batchResults = performLoad( jdbcParamBindings, session );
final List<E> 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<E> batchResults = performLoad( jdbcParamBindings, session );
final List<E> batchResults = performLoad( jdbcParamBindings, session, size );
multiLoadResults.addAll( batchResults );
}
return multiLoadResults;
}
private <E> List<E> performLoad(JdbcParameterBindings jdbcParamBindings, SharedSessionContractImplementor session) {
private <E> List<E> 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
);
}

View File

@ -154,7 +154,9 @@ public class SingleIdLoadPlan<T> implements SingleEntityLoadPlan {
callback
),
getRowTransformer(),
singleResultExpected ? ListResultsConsumer.UniqueSemantic.ASSERT : ListResultsConsumer.UniqueSemantic.FILTER
null,
singleResultExpected ? ListResultsConsumer.UniqueSemantic.ASSERT : ListResultsConsumer.UniqueSemantic.FILTER,
1
);
if ( list.isEmpty() ) {

View File

@ -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<T> 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<T> implements SingleUniqueKeyEn
jdbcSelect,
jdbcParameterBindings,
new NoCallbackExecutionContext( session ),
row -> row[0],
ListResultsConsumer.UniqueSemantic.FILTER
RowTransformerSingularReturnImpl.instance(),
null,
ListResultsConsumer.UniqueSemantic.FILTER,
1
);
assert list.size() == 1;

View File

@ -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<Object[]> 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) {

View File

@ -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;

View File

@ -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;

View File

@ -473,8 +473,8 @@ public class NativeQueryImpl<R>
getTimeout(),
getFetchSize(),
getComment(),
getFirstResult(),
getMaxResults(),
getQueryOptions().getLimit().getFirstRow(),
getQueryOptions().getLimit().getMaxRows(),
getHints()
);
}
@ -636,7 +636,7 @@ public class NativeQueryImpl<R>
return QueryOptions.NONE;
}
};
return createCountQueryPlan().executeQuery( context, new SingleResultConsumer<>() );
return createCountQueryPlan().executeQuery( context, SingleResultConsumer.instance() );
}
@Override

View File

@ -95,10 +95,7 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
null,
null,
sqlString -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareQueryStatement( sqlString, false, null ),
-1,
resultsConsumer
);
}
@ -183,7 +180,8 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
scrollMode,
jdbcParameterBindings,
SqmJdbcExecutionContextAdapter.usingLockingAndPaging( executionContext ),
null
null,
-1
);
}
}

View File

@ -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<R> implements SelectQueryPlan<R> {
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<R> implements SelectQueryPlan<R> {
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<R> implements SelectQueryPlan<R> {
listInterpreterExecutionContext( hql, executionContext, jdbcSelect, subSelectFetchKeyHandler ),
rowTransformer,
(Class<R>) executionContext.getResultType(),
uniqueSemantic
uniqueSemantic,
resultCountEstimate
);
}
finally {
@ -167,12 +179,18 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
.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<R> implements SelectQueryPlan<R> {
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<SqmSelection<?>> selections(SqmSelectStatement<?> sqm) {
return sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections();
}

View File

@ -493,7 +493,7 @@ public class QuerySqmImpl<R>
};
final SqmSelectStatement<?> sqmStatement = (SqmSelectStatement<?>) getSqmStatement();
return buildConcreteQueryPlan( sqmStatement.createCountQuery(), Long.class, null, getQueryOptions() )
.executeQuery( context, new SingleResultConsumer<>() );
.executeQuery( context, SingleResultConsumer.instance() );
}
protected List<R> doList() {

View File

@ -295,7 +295,7 @@ public class SqmSelectionQueryImpl<R> extends AbstractSqmSelectionQuery<R>
}
};
return buildConcreteQueryPlan( getSqmStatement().createCountQuery(), Long.class, null, getQueryOptions() )
.executeQuery( context, new SingleResultConsumer<>() );
.executeQuery( context, SingleResultConsumer.instance() );
}
protected List<R> doList() {

View File

@ -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<Object> 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<Object>) rowTransformer,
ListResultsConsumer.UniqueSemantic.FILTER
);
}

View File

@ -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,

View File

@ -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,

View File

@ -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 ) {

View File

@ -70,6 +70,28 @@ public class JdbcSelectExecutorStandardImpl implements JdbcSelectExecutor {
Class<R> domainResultType,
Function<String, PreparedStatement> statementCreator,
ResultsConsumer<T, R> resultsConsumer) {
return executeQuery(
jdbcSelect,
jdbcParameterBindings,
executionContext,
rowTransformer,
domainResultType,
-1,
statementCreator,
resultsConsumer
);
}
@Override
public <T, R> T executeQuery(
JdbcOperationQuerySelect jdbcSelect,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext,
RowTransformer<R> rowTransformer,
Class<R> domainResultType,
int resultCountEstimate,
Function<String, PreparedStatement> statementCreator,
ResultsConsumer<T, R> 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<R> rowTransformer,
Class<R> domainResultType,
int resultCountEstimate,
Function<String, PreparedStatement> statementCreator,
ResultsConsumer<T, R> 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,

View File

@ -51,6 +51,55 @@ public interface JdbcSelectExecutor {
Function<String, PreparedStatement> statementCreator,
ResultsConsumer<T, R> resultsConsumer);
/**
* @since 6.6
*/
default <T, R> T executeQuery(
JdbcOperationQuerySelect jdbcSelect,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext,
RowTransformer<R> rowTransformer,
Class<R> domainResultType,
int resultCountEstimate,
ResultsConsumer<T, R> resultsConsumer) {
return executeQuery(
jdbcSelect,
jdbcParameterBindings,
executionContext,
rowTransformer,
domainResultType,
resultCountEstimate,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareQueryStatement( sql, false, null ),
resultsConsumer
);
}
/**
* @since 6.6
*/
default <T, R> T executeQuery(
JdbcOperationQuerySelect jdbcSelect,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext,
RowTransformer<R> rowTransformer,
Class<R> domainResultType,
int resultCountEstimate,
Function<String, PreparedStatement> statementCreator,
ResultsConsumer<T, R> resultsConsumer) {
return executeQuery(
jdbcSelect,
jdbcParameterBindings,
executionContext,
rowTransformer,
domainResultType,
statementCreator,
resultsConsumer
);
}
default <R> List<R> list(
JdbcOperationQuerySelect jdbcSelect,
JdbcParameterBindings jdbcParameterBindings,
@ -67,6 +116,28 @@ public interface JdbcSelectExecutor {
RowTransformer<R> rowTransformer,
Class<R> requestedJavaType,
ListResultsConsumer.UniqueSemantic uniqueSemantic) {
return list(
jdbcSelect,
jdbcParameterBindings,
executionContext,
rowTransformer,
requestedJavaType,
uniqueSemantic,
-1
);
}
/**
* @since 6.6
*/
default <R> List<R> list(
JdbcOperationQuerySelect jdbcSelect,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext,
RowTransformer<R> rowTransformer,
Class<R> 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<R> rowTransformer) {
return scroll( jdbcSelect, scrollMode, jdbcParameterBindings, executionContext, rowTransformer, -1 );
}
/**
* @since 6.6
*/
default <R> ScrollableResultsImplementor<R> scroll(
JdbcOperationQuerySelect jdbcSelect,
ScrollMode scrollMode,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext,
RowTransformer<R> 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()
);
}

View File

@ -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 );
}
}

View File

@ -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,

View File

@ -32,6 +32,7 @@ public class RowTransformerSingularReturnImpl<R> implements RowTransformer<R> {
@Override
@SuppressWarnings("unchecked")
public R transformRow(Object[] row) {
assert row.length == 1;
return (R) row[0];
}

View File

@ -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<String, PreparedStatement> statementCreator) {
Function<String, PreparedStatement> 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();
}
}

View File

@ -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;
}
}

View File

@ -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()
);
}
}

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -35,6 +35,7 @@ import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingResolution;
* @author Steve Ebersole
*/
public class StandardJdbcValuesMapping implements JdbcValuesMapping {
private final List<SqlSelection> sqlSelections;
private final List<DomainResult<?>> domainResults;
private JdbcValuesMappingResolutionImpl resolution;

View File

@ -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();
}

View File

@ -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);
}

View File

@ -33,6 +33,13 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* @author Steve Ebersole
*/
public class ListResultsConsumer<R> implements ResultsConsumer<List<R>, 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<R> implements ResultsConsumer<List<R>, R> {
}
private static class Results<R> {
private final List<R> results = new ArrayList<>();
private final List<R> results;
private final JavaType<R> resultJavaType;
public Results(JavaType<R> resultJavaType) {
public Results(JavaType<R> 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<R> implements ResultsConsumer<List<R>, R> {
private static class EntityResult<R> extends Results<R> {
private static final Object DUMP_VALUE = new Object();
private final IdentityHashMap<R, Object> added = new IdentityHashMap<>();
private final IdentityHashMap<R, Object> added;
public EntityResult(JavaType<R> resultJavaType) {
super( resultJavaType );
public EntityResult(JavaType<R> 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<R> implements ResultsConsumer<List<R>, 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<R> implements ResultsConsumer<List<R>, R> {
);
final boolean isEntityResultType = domainResultJavaType instanceof EntityJavaType;
final int initialCollectionSize = Math.min( jdbcValues.getResultCountEstimate(), INITIAL_COLLECTION_SIZE_LIMIT );
final Results<R> 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;

View File

@ -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<Void, Object> {
public static final ManagedResultConsumer INSTANCE = new ManagedResultConsumer();
@Override
public Void consume(
JdbcValues jdbcValues,
SharedSessionContractImplementor session,
JdbcValuesSourceProcessingOptions processingOptions,
JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState,
RowProcessingStateStandardImpl rowProcessingState,
RowReader<Object> 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;
}
}

View File

@ -42,6 +42,7 @@ public class ScrollableResultsConsumer<R> implements ResultsConsumer<ScrollableR
JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState,
RowProcessingStateStandardImpl rowProcessingState,
RowReader<R> rowReader) {
rowReader.startLoading( rowProcessingState );
if ( containsCollectionFetches( jdbcValues.getValuesMapping() ) ) {
return new FetchingScrollableResultsImpl<>(
jdbcValues,

View File

@ -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<T> implements ResultsConsumer<T, T> {
private static final SingleResultConsumer<?> INSTANCE = new SingleResultConsumer<>();
public static <T> SingleResultConsumer<T> instance() {
//noinspection unchecked
return (SingleResultConsumer<T>) INSTANCE;
}
@Override
public T consume(
JdbcValues jdbcValues,
@ -31,14 +40,45 @@ public class SingleResultConsumer<T> implements ResultsConsumer<T, T> {
JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState,
RowProcessingStateStandardImpl rowProcessingState,
RowReader<T> rowReader) {
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( false );
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
public boolean canResultsBeCached() {