HHH-18379 Allow passing row count estimate to pre-size collections
This commit is contained in:
parent
33b2e36035
commit
ba05533a03
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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() ) {
|
||||
|
|
|
@ -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() ) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -93,7 +93,9 @@ public class EntityConcreteTypeLoader {
|
|||
jdbcParamBindings,
|
||||
new BaseExecutionContext( session ),
|
||||
RowTransformerStandardImpl.instance(),
|
||||
ListResultsConsumer.UniqueSemantic.NONE
|
||||
null,
|
||||
ListResultsConsumer.UniqueSemantic.NONE,
|
||||
1
|
||||
);
|
||||
|
||||
if ( results.isEmpty() ) {
|
||||
|
|
|
@ -243,7 +243,9 @@ public class LoaderHelper {
|
|||
session
|
||||
),
|
||||
RowTransformerStandardImpl.instance(),
|
||||
ListResultsConsumer.UniqueSemantic.FILTER
|
||||
null,
|
||||
ListResultsConsumer.UniqueSemantic.FILTER,
|
||||
idsToInitialize.length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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++ ) {
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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() ) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue