From 503db5cd892363904c7fd469d2fc914ffcebce5e Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Tue, 11 Jun 2024 10:57:26 +0200 Subject: [PATCH] [#1932] Support ReactiveSelectionQuery#getResultCount for HQL I need some changes in ORM before I can support it for native queries. --- .../org/hibernate/reactive/mutiny/Mutiny.java | 11 ++++ .../reactive/mutiny/impl/MutinyQueryImpl.java | 5 ++ .../mutiny/impl/MutinySelectionQueryImpl.java | 5 ++ .../query/ReactiveSelectionQuery.java | 2 + .../spi/ReactiveAbstractSelectionQuery.java | 16 +++++- .../sql/internal/ReactiveNativeQueryImpl.java | 10 ++++ .../ConcreteSqmSelectReactiveQueryPlan.java | 55 +++++++++++++++++- .../sqm/internal/ReactiveQuerySqmImpl.java | 11 ++++ .../ReactiveSqmSelectionQueryImpl.java | 6 ++ .../sqm/spi/ReactiveSelectQueryPlan.java | 3 +- .../internal/ReactiveInitializersList.java | 6 ++ .../spi/ReactiveSingleResultConsumer.java | 46 +++++++++++++++ .../org/hibernate/reactive/stage/Stage.java | 56 ++++++++++++------- .../reactive/stage/impl/StageQueryImpl.java | 5 ++ .../stage/impl/StageSelectionQueryImpl.java | 5 ++ 15 files changed, 218 insertions(+), 24 deletions(-) create mode 100644 hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java index 22ec48da1c..65b0ad9062 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java @@ -194,6 +194,17 @@ interface SelectionQuery extends AbstractQuery { */ Uni getSingleResultOrNull(); + /** + * Determine the size of the query result list that would be + * returned by calling {@link #getResultList()} with no + * {@linkplain #getFirstResult() offset} or + * {@linkplain #getMaxResults() limit} applied to the query. + * + * @return the size of the list that would be returned + */ + @Incubating + Uni getResultCount(); + /** * Asynchronously execute this query, returning the query results * as a {@link List}, via a {@link Uni}. If the query diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java index 9cb3a0287c..d0608ba5f7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyQueryImpl.java @@ -47,6 +47,11 @@ public int getMaxResults() { return delegate.getMaxResults(); } + @Override + public Uni getResultCount() { + return uni( delegate::getReactiveResultCount ); + } + @Override public Uni> getResultList() { return uni( delegate::getReactiveResultList ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java index 0d037e0ed6..b001e618a9 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinySelectionQueryImpl.java @@ -45,6 +45,11 @@ public int getMaxResults() { return delegate.getMaxResults(); } + @Override + public Uni getResultCount() { + return uni( delegate::getReactiveResultCount ); + } + @Override public Uni> getResultList() { return uni( delegate::getReactiveResultList ); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java index 81508c13b4..2b347b3de7 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/ReactiveSelectionQuery.java @@ -49,6 +49,8 @@ default CompletionStage> getReactiveResultList() { CompletionStage getReactiveSingleResultOrNull(); + CompletionStage getReactiveResultCount(); + CompletionStage reactiveUnique(); CompletionStage> reactiveUniqueResultOptional(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java index 69ebe52a1c..f01b5d3150 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/spi/ReactiveAbstractSelectionQuery.java @@ -21,6 +21,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.hql.internal.QuerySplitter; +import org.hibernate.query.internal.DelegatingDomainQueryExecutionContext; +import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.sqm.internal.DomainParameterXref; @@ -33,6 +35,7 @@ import org.hibernate.reactive.query.sqm.internal.AggregatedSelectReactiveQueryPlan; import org.hibernate.reactive.query.sqm.internal.ConcreteSqmSelectReactiveQueryPlan; import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; +import org.hibernate.reactive.sql.results.spi.ReactiveSingleResultConsumer; import org.hibernate.sql.results.internal.TupleMetadata; import jakarta.persistence.NoResultException; @@ -146,6 +149,17 @@ public CompletionStage getReactiveSingleResult() { .exceptionally( this::convertException ); } + public CompletionStage getReactiveResultsCount(SqmSelectStatement sqmStatement, DomainQueryExecutionContext domainQueryExecutionContext) { + final DelegatingDomainQueryExecutionContext context = new DelegatingDomainQueryExecutionContext( domainQueryExecutionContext ) { + @Override + public QueryOptions getQueryOptions() { + return QueryOptions.NONE; + } + }; + return buildConcreteSelectQueryPlan( sqmStatement.createCountQuery(), Long.class, getQueryOptions() ) + .reactiveExecuteQuery( context, new ReactiveSingleResultConsumer<>() ); + } + private R reactiveSingleResult(List list) { if ( list.isEmpty() ) { throw new NoResultException( String.format( "No result found for query [%s]", getQueryString() ) ); @@ -269,7 +283,7 @@ private ReactiveSelectQueryPlan buildAggregatedSelectQueryPlan(SqmSelectState return new AggregatedSelectReactiveQueryPlan<>( aggregatedQueryPlans ); } - private ReactiveSelectQueryPlan buildConcreteSelectQueryPlan( + public ReactiveSelectQueryPlan buildConcreteSelectQueryPlan( SqmSelectStatement concreteSqmStatement, Class resultType, QueryOptions queryOptions) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java index a441b4b555..1c97f847a3 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sql/internal/ReactiveNativeQueryImpl.java @@ -93,6 +93,16 @@ private T getNull() { return null; } + @Override + public long getResultCount() { + throw LOG.nonReactiveMethodCall( "getReactiveResultCount()" ); + } + + @Override + public CompletionStage getReactiveResultCount() { + throw LOG.notYetImplemented(); + } + private ReactiveAbstractSelectionQuery createSelectionQueryDelegate(SharedSessionContractImplementor session) { return new ReactiveAbstractSelectionQuery<>( this::getQueryOptions, diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java index 16135b9a2c..858e2d2098 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ConcreteSqmSelectReactiveQueryPlan.java @@ -36,6 +36,7 @@ import org.hibernate.reactive.query.sqm.spi.ReactiveSelectQueryPlan; import org.hibernate.reactive.sql.exec.internal.StandardReactiveSelectExecutor; import org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer; +import org.hibernate.reactive.sql.results.spi.ReactiveResultsConsumer; import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.spi.FromClauseAccess; @@ -59,6 +60,7 @@ public class ConcreteSqmSelectReactiveQueryPlan extends ConcreteSqmSelectQueryPlan implements ReactiveSelectQueryPlan { + private final SqmInterpreter> executeQueryInterpreter; private final SqmInterpreter, Void> listInterpreter; private final RowTransformer rowTransformer; @@ -80,6 +82,8 @@ public ConcreteSqmSelectReactiveQueryPlan( this.rowTransformer = determineRowTransformer( sqm, resultType, tupleMetadata, queryOptions ); this.listInterpreter = (unused, executionContext, sqmInterpretation, jdbcParameterBindings) -> listInterpreter( hql, domainParameterXref, executionContext, sqmInterpretation, jdbcParameterBindings, rowTransformer ); + this.executeQueryInterpreter = (resultsConsumer, executionContext, sqmInterpretation, jdbcParameterBindings) -> + executeQueryInterpreter( hql, domainParameterXref, executionContext, sqmInterpretation, jdbcParameterBindings, rowTransformer, resultsConsumer ); } private static CompletionStage> listInterpreter( @@ -110,6 +114,40 @@ private static CompletionStage> listInterpreter( .whenComplete( (rs, t) -> domainParameterXref.clearExpansions() ); } + private static CompletionStage executeQueryInterpreter( + String hql, + DomainParameterXref domainParameterXref, + DomainQueryExecutionContext executionContext, + CacheableSqmInterpretation sqmInterpretation, + JdbcParameterBindings jdbcParameterBindings, + RowTransformer rowTransformer, + ReactiveResultsConsumer resultsConsumer) { + final ReactiveSharedSessionContractImplementor session = (ReactiveSharedSessionContractImplementor) executionContext.getSession(); + final JdbcOperationQuerySelect jdbcSelect = sqmInterpretation.getJdbcSelect(); + // I'm using a supplier so that the whenComplete at the end will catch any errors, like a finally-block + Supplier fetchHandlerSupplier = () -> SubselectFetch + .createRegistrationHandler( session.getPersistenceContext().getBatchFetchQueue(), sqmInterpretation.selectStatement, JdbcParametersList.empty(), jdbcParameterBindings ); + return completedFuture( fetchHandlerSupplier ) + .thenApply( Supplier::get ) + .thenCompose( subSelectFetchKeyHandler -> session + .reactiveAutoFlushIfRequired( jdbcSelect.getAffectedTableNames() ) + .thenCompose( required -> StandardReactiveSelectExecutor.INSTANCE + .executeQuery( jdbcSelect, + jdbcParameterBindings, + ConcreteSqmSelectQueryPlan.listInterpreterExecutionContext( hql, executionContext, jdbcSelect, subSelectFetchKeyHandler ), + rowTransformer, + null, + sql -> executionContext.getSession() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareQueryStatement( sql, false, null ), + resultsConsumer + ) + ) + ) + .whenComplete( (rs, t) -> domainParameterXref.clearExpansions() ); + } + @Override public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, DomainQueryExecutionContext executionContext) { throw new UnsupportedOperationException(); @@ -119,10 +157,21 @@ public ScrollableResultsImplementor performScroll(ScrollMode scrollMode, Doma public CompletionStage> reactivePerformList(DomainQueryExecutionContext executionContext) { return executionContext.getQueryOptions().getEffectiveLimit().getMaxRowsJpa() == 0 ? completedFuture( emptyList() ) - : withCacheableSqmInterpretation( executionContext, listInterpreter ); + : withCacheableSqmInterpretation( executionContext, null, listInterpreter ); + } + + @Override + public CompletionStage reactiveExecuteQuery( + DomainQueryExecutionContext executionContext, + ReactiveResultsConsumer resultsConsumer) { + return withCacheableSqmInterpretation( + executionContext, + resultsConsumer, + (SqmInterpreter>) (SqmInterpreter) executeQueryInterpreter + ); } - private CompletionStage withCacheableSqmInterpretation(DomainQueryExecutionContext executionContext, SqmInterpreter interpreter) { + private CompletionStage withCacheableSqmInterpretation(DomainQueryExecutionContext executionContext, X context, SqmInterpreter interpreter) { // NOTE : VERY IMPORTANT - intentional double-lock checking // The other option would be to leverage `java.util.concurrent.locks.ReadWriteLock` // to protect access. However, synchronized is much simpler here. We will verify @@ -162,7 +211,7 @@ private CompletionStage withCacheableSqmInterpretation(DomainQueryExec jdbcParameterBindings = createJdbcParameterBindings( localCopy, executionContext ); } - return interpreter.interpret( null, executionContext, localCopy, jdbcParameterBindings ); + return interpreter.interpret( context, executionContext, localCopy, jdbcParameterBindings ); } private JdbcParameterBindings createJdbcParameterBindings(CacheableSqmInterpretation sqmInterpretation, DomainQueryExecutionContext executionContext) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java index 3c23e6c765..fbe3295d68 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveQuerySqmImpl.java @@ -139,6 +139,17 @@ public CompletionStage getReactiveSingleResult() { return selectionQueryDelegate.getReactiveSingleResult(); } + @Override + public long getResultCount() { + throw LOG.nonReactiveMethodCall( "getReactiveResultCount()" ); + } + + @Override + public CompletionStage getReactiveResultCount() { + return selectionQueryDelegate + .getReactiveResultsCount( ( (SqmSelectStatement) getSqmStatement() ).createCountQuery(), this ); + } + @Override public CompletionStage getReactiveSingleResultOrNull() { return selectionQueryDelegate.getReactiveSingleResultOrNull(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java index 138f996a4c..13e1676979 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/internal/ReactiveSqmSelectionQueryImpl.java @@ -212,6 +212,12 @@ public R getSingleResultOrNull() { return selectionQueryDelegate.getSingleResultOrNull(); } + @Override + public CompletionStage getReactiveResultCount() { + return selectionQueryDelegate + .getReactiveResultsCount( getSqmStatement().createCountQuery(), this ); + } + @Override public List getResultList() { return selectionQueryDelegate.getResultList(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/spi/ReactiveSelectQueryPlan.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/spi/ReactiveSelectQueryPlan.java index c83a6883f9..6ef50764cb 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/spi/ReactiveSelectQueryPlan.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/query/sqm/spi/ReactiveSelectQueryPlan.java @@ -14,6 +14,7 @@ import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.reactive.logging.impl.Log; +import org.hibernate.reactive.sql.results.spi.ReactiveResultsConsumer; import org.hibernate.sql.results.spi.ResultsConsumer; import static org.hibernate.reactive.logging.impl.LoggerFactory.make; @@ -44,7 +45,7 @@ default T executeQuery(DomainQueryExecutionContext executionContext, Results /** * Execute the query */ - default CompletionStage reactiveExecuteQuery(DomainQueryExecutionContext executionContext, ResultsConsumer resultsConsumer) { + default CompletionStage reactiveExecuteQuery(DomainQueryExecutionContext executionContext, ReactiveResultsConsumer resultsConsumer) { return failedFuture( new UnsupportedOperationException() ); } diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java index d861cddc4c..e752a49ca0 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/internal/ReactiveInitializersList.java @@ -56,6 +56,12 @@ public void finishUpRow(final RowProcessingState rowProcessingState) { } } + public void startLoading(final RowProcessingState rowProcessingState) { + for ( int i = initializers.length - 1; i >= 0; i-- ) { + initializers[i].startLoading( rowProcessingState ); + } + } + public CompletionStage initializeInstance(final ReactiveRowProcessingState rowProcessingState) { return loop( initializers, initializer -> { if ( initializer instanceof ReactiveInitializer ) { diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java new file mode 100644 index 0000000000..ad3d17853a --- /dev/null +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/sql/results/spi/ReactiveSingleResultConsumer.java @@ -0,0 +1,46 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.reactive.sql.results.spi; + +import java.util.concurrent.CompletionStage; + +import org.hibernate.Incubating; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.reactive.sql.exec.spi.ReactiveRowProcessingState; +import org.hibernate.reactive.sql.exec.spi.ReactiveValuesResultSet; +import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl; +import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; + +@Incubating +public class ReactiveSingleResultConsumer implements ReactiveResultsConsumer { + + @Override + public CompletionStage consume( + ReactiveValuesResultSet jdbcValues, + SharedSessionContractImplementor session, + JdbcValuesSourceProcessingOptions processingOptions, + JdbcValuesSourceProcessingStateStandardImpl jdbcValuesSourceProcessingState, + ReactiveRowProcessingState rowProcessingState, + ReactiveRowReader rowReader) { + rowReader.getReactiveInitializersList().startLoading( rowProcessingState ); + return rowProcessingState.next() + .thenCompose( hasNext -> rowReader + .reactiveReadRow( rowProcessingState, processingOptions ) + .thenApply( result -> { + rowProcessingState.finishRowProcessing( true ); + rowReader.finishUp( jdbcValuesSourceProcessingState ); + jdbcValuesSourceProcessingState.finishUp( false ); + return result; + } ) + ); + } + + @Override + public boolean canResultsBeCached() { + return false; + } + +} diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java index 5fc1d671ee..47da9f6cff 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/Stage.java @@ -5,20 +5,18 @@ */ package org.hibernate.reactive.stage; -import jakarta.persistence.CacheRetrieveMode; -import jakarta.persistence.CacheStoreMode; -import jakarta.persistence.EntityGraph; -import jakarta.persistence.FlushModeType; -import jakarta.persistence.LockModeType; -import jakarta.persistence.Parameter; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaDelete; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.CriteriaUpdate; -import jakarta.persistence.metamodel.Attribute; -import jakarta.persistence.metamodel.Metamodel; +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.function.BiFunction; +import java.util.function.Function; + import org.hibernate.Cache; -import org.hibernate.*; +import org.hibernate.CacheMode; +import org.hibernate.Filter; +import org.hibernate.FlushMode; +import org.hibernate.Incubating; +import org.hibernate.LockMode; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.collection.spi.AbstractPersistentCollection; import org.hibernate.collection.spi.PersistentCollection; @@ -38,16 +36,25 @@ import org.hibernate.reactive.util.impl.CompletionStages; import org.hibernate.stat.Statistics; -import java.lang.invoke.MethodHandles; -import java.util.List; -import java.util.concurrent.CompletionStage; -import java.util.function.BiFunction; -import java.util.function.Function; +import jakarta.persistence.CacheRetrieveMode; +import jakarta.persistence.CacheStoreMode; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Parameter; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.Metamodel; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; import static org.hibernate.internal.util.LockModeConverter.convertToLockMode; -import static org.hibernate.jpa.internal.util.CacheModeHelper.*; +import static org.hibernate.jpa.internal.util.CacheModeHelper.interpretCacheMode; +import static org.hibernate.jpa.internal.util.CacheModeHelper.interpretCacheRetrieveMode; +import static org.hibernate.jpa.internal.util.CacheModeHelper.interpretCacheStoreMode; /** * An API for Hibernate Reactive where non-blocking operations are @@ -188,6 +195,17 @@ interface SelectionQuery extends AbstractQuery { */ CompletionStage getSingleResultOrNull(); + /** + * Determine the size of the query result list that would be + * returned by calling {@link #getResultList()} with no + * {@linkplain #getFirstResult() offset} or + * {@linkplain #getMaxResults() limit} applied to the query. + * + * @return the size of the list that would be returned + */ + @Incubating + CompletionStage getResultCount(); + /** * Asynchronously execute this query, returning the query results * as a {@link List}, via a {@link CompletionStage}. If the query diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java index 3a4948275a..fb022c4997 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageQueryImpl.java @@ -38,6 +38,11 @@ public int getMaxResults() { return delegate.getMaxResults(); } + @Override + public CompletionStage getResultCount() { + return delegate.getReactiveResultCount(); + } + @Override public CompletionStage> getResultList() { return delegate.getReactiveResultList(); diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java index f28bfd9d03..6a1be5d084 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/stage/impl/StageSelectionQueryImpl.java @@ -41,6 +41,11 @@ public int getMaxResults() { return delegate.getMaxResults(); } + @Override + public CompletionStage getResultCount() { + return delegate.getReactiveResultCount(); + } + @Override public CompletionStage> getResultList() { return delegate.getReactiveResultList();