Skip to content

Commit e9d08ca

Browse files
committed
HHH-17410 Support creating count query from existing query
1 parent 7a5219b commit e9d08ca

File tree

10 files changed

+364
-7
lines changed

10 files changed

+364
-7
lines changed

hibernate-core/src/main/java/org/hibernate/query/criteria/CriteriaDefinition.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,9 @@ public <X> JpaDerivedRoot<X> from(Subquery<X> subquery) {
400400
public <X> JpaRoot<X> from(JpaCteCriteria<X> cte) {
401401
return query.from(cte);
402402
}
403+
404+
@Override
405+
public JpaCriteriaQuery<Long> createCountQuery() {
406+
return query.createCountQuery();
407+
}
403408
}

hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCriteriaQuery.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
*/
2727
public interface JpaCriteriaQuery<T> extends CriteriaQuery<T>, JpaQueryableCriteria<T>, JpaSelectCriteria<T> {
2828

29+
/**
30+
* Wraps this query in a subquery and returns a count query based on that subquery in the from clause.
31+
*
32+
* @since 6.4
33+
*/
34+
JpaCriteriaQuery<Long> createCountQuery();
35+
2936
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3037
// Limit/Offset/Fetch clause
3138

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/SqmCopyContext.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.query.sqm.tree;
88

9+
import org.hibernate.Incubating;
910
import org.hibernate.query.sqm.internal.NoParamSqmCopyContext;
1011
import org.hibernate.query.sqm.internal.SimpleSqmCopyContext;
1112

@@ -18,6 +19,16 @@ public interface SqmCopyContext {
1819

1920
<T> T registerCopy(T original, T copy);
2021

22+
/**
23+
* Returns whether the {@code fetch} flag for attribute joins should be copied over.
24+
*
25+
* @since 6.4
26+
*/
27+
@Incubating
28+
default boolean copyFetchedFlag() {
29+
return true;
30+
}
31+
2132
static SqmCopyContext simpleContext() {
2233
return new SimpleSqmCopyContext();
2334
}

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public SqmBagJoin<O, E> copy(SqmCopyContext context) {
6565
getAttribute(),
6666
getExplicitAlias(),
6767
getSqmJoinType(),
68-
isFetched(),
68+
context.copyFetchedFlag() && isFetched(),
6969
nodeBuilder()
7070
)
7171
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public SqmListJoin<O, E> copy(SqmCopyContext context) {
6868
getAttribute(),
6969
getExplicitAlias(),
7070
getSqmJoinType(),
71-
isFetched(),
71+
context.copyFetchedFlag() && isFetched(),
7272
nodeBuilder()
7373
)
7474
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import java.util.Map;
1010
import jakarta.persistence.criteria.Expression;
11-
import jakarta.persistence.criteria.Path;
1211
import jakarta.persistence.criteria.Predicate;
1312

1413
import org.hibernate.metamodel.model.domain.EntityDomainType;
@@ -67,7 +66,7 @@ public SqmMapJoin<O, K, V> copy(SqmCopyContext context) {
6766
getAttribute(),
6867
getExplicitAlias(),
6968
getSqmJoinType(),
70-
isFetched(),
69+
context.copyFetchedFlag() && isFetched(),
7170
nodeBuilder()
7271
)
7372
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public SqmSetJoin<O, E> copy(SqmCopyContext context) {
6767
getModel(),
6868
getExplicitAlias(),
6969
getSqmJoinType(),
70-
isFetched(),
70+
context.copyFetchedFlag() && isFetched(),
7171
nodeBuilder()
7272
)
7373
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.hibernate.metamodel.model.domain.EntityDomainType;
1212
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
1313
import org.hibernate.query.sqm.SemanticQueryWalker;
14-
import org.hibernate.query.sqm.SqmPathSource;
1514
import org.hibernate.spi.NavigablePath;
1615
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
1716
import org.hibernate.query.sqm.NodeBuilder;
@@ -76,7 +75,7 @@ public SqmSingularJoin<O, T> copy(SqmCopyContext context) {
7675
getAttribute(),
7776
getExplicitAlias(),
7877
getSqmJoinType(),
79-
isFetched(),
78+
context.copyFetchedFlag() && isFetched(),
8079
nodeBuilder()
8180
)
8281
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.query.sqm.tree.select;
88

9+
import java.util.ArrayList;
910
import java.util.Collections;
1011
import java.util.LinkedHashSet;
1112
import java.util.List;
@@ -27,10 +28,12 @@
2728
import org.hibernate.query.sqm.NodeBuilder;
2829
import org.hibernate.query.sqm.SemanticQueryWalker;
2930
import org.hibernate.query.sqm.SqmQuerySource;
31+
import org.hibernate.query.sqm.internal.NoParamSqmCopyContext;
3032
import org.hibernate.query.sqm.internal.SqmUtil;
3133
import org.hibernate.query.sqm.tree.SqmCopyContext;
3234
import org.hibernate.query.sqm.tree.SqmStatement;
3335
import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
36+
import org.hibernate.query.sqm.tree.expression.SqmStar;
3437
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
3538
import org.hibernate.query.sqm.tree.expression.SqmParameter;
3639
import org.hibernate.query.sqm.tree.from.SqmFromClause;
@@ -455,4 +458,83 @@ private void validateComplianceFetchOffset() {
455458
"Please disable the JPA query compliance if you want to use this feature." );
456459
}
457460
}
461+
462+
@Override
463+
public JpaCriteriaQuery<Long> createCountQuery() {
464+
final SqmCopyContext context = new NoParamSqmCopyContext() {
465+
@Override
466+
public boolean copyFetchedFlag() {
467+
return false;
468+
}
469+
};
470+
final NodeBuilder nodeBuilder = nodeBuilder();
471+
final Set<SqmParameter<?>> parameters;
472+
if ( this.parameters == null ) {
473+
parameters = null;
474+
}
475+
else {
476+
parameters = new LinkedHashSet<>( this.parameters.size() );
477+
for ( SqmParameter<?> parameter : this.parameters ) {
478+
parameters.add( parameter.copy( context ) );
479+
}
480+
}
481+
final SqmSelectStatement<Long> selectStatement = new SqmSelectStatement<>(
482+
nodeBuilder,
483+
copyCteStatements( context ),
484+
Long.class,
485+
SqmQuerySource.CRITERIA,
486+
parameters
487+
);
488+
final SqmQuerySpec<Long> querySpec = new SqmQuerySpec<>( nodeBuilder );
489+
490+
final SqmSubQuery<Tuple> subquery = new SqmSubQuery<>( selectStatement, Tuple.class, nodeBuilder );
491+
final SqmQueryPart<T> queryPart = getQueryPart().copy( context );
492+
resetSelections( queryPart );
493+
// Reset the
494+
if ( queryPart.getFetch() == null && queryPart.getOffset() == null ) {
495+
queryPart.setOrderByClause( null );
496+
}
497+
//noinspection unchecked
498+
subquery.setQueryPart( (SqmQueryPart<Tuple>) queryPart );
499+
500+
querySpec.setFromClause( new SqmFromClause( 1 ) );
501+
querySpec.setSelectClause( new SqmSelectClause( false, 1, nodeBuilder ) );
502+
selectStatement.setQueryPart( querySpec );
503+
selectStatement.select( nodeBuilder.count( new SqmStar( nodeBuilder ) ) );
504+
selectStatement.from( subquery );
505+
return selectStatement;
506+
}
507+
508+
private void resetSelections(SqmQueryPart<?> queryPart) {
509+
if ( queryPart instanceof SqmQuerySpec<?> ) {
510+
resetSelections( (SqmQuerySpec<?>) queryPart );
511+
}
512+
else {
513+
final SqmQueryGroup<?> group = (SqmQueryGroup<?>) queryPart;
514+
for ( SqmQueryPart<?> part : group.getQueryParts() ) {
515+
resetSelections( part );
516+
}
517+
}
518+
}
519+
520+
private void resetSelections(SqmQuerySpec<?> querySpec) {
521+
final NodeBuilder nodeBuilder = nodeBuilder();
522+
final List<SqmSelection<?>> selections = querySpec.getSelectClause().getSelections();
523+
final List<SqmSelectableNode<?>> subSelections = new ArrayList<>();
524+
525+
if ( selections.isEmpty() ) {
526+
subSelections.add( (SqmSelectableNode<?>) nodeBuilder.literal( 1 ).alias( "c0" ) );
527+
}
528+
else {
529+
for ( SqmSelection<?> selection : selections ) {
530+
selection.getSelectableNode().visitSubSelectableNodes( e -> {
531+
e.alias( "c" + subSelections.size() );
532+
subSelections.add( e );
533+
} );
534+
}
535+
}
536+
537+
querySpec.getSelectClause().setSelection( (SqmSelectableNode<?>) nodeBuilder.tuple( subSelections ) );
538+
}
539+
458540
}

0 commit comments

Comments
 (0)