3737import org .hibernate .query .sqm .tree .expression .ValueBindJpaCriteriaParameter ;
3838import org .hibernate .query .sqm .tree .expression .SqmParameter ;
3939import org .hibernate .query .sqm .tree .from .SqmFromClause ;
40+ import org .hibernate .query .sqm .tree .from .SqmRoot ;
4041
42+ import static org .hibernate .query .sqm .tree .SqmCopyContext .noParamCopyContext ;
4143import static org .hibernate .query .sqm .tree .jpa .ParameterCollector .collectParameters ;
4244
4345/**
@@ -119,6 +121,10 @@ public SqmSelectStatement<T> copy(SqmCopyContext context) {
119121 if ( existing != null ) {
120122 return existing ;
121123 }
124+ return createCopy ( context , getResultType () );
125+ }
126+
127+ private <X > SqmSelectStatement <X > createCopy (SqmCopyContext context , Class <X > resultType ) {
122128 final Set <SqmParameter <?>> parameters ;
123129 if ( this .parameters == null ) {
124130 parameters = null ;
@@ -129,17 +135,19 @@ public SqmSelectStatement<T> copy(SqmCopyContext context) {
129135 parameters .add ( parameter .copy ( context ) );
130136 }
131137 }
132- final SqmSelectStatement <T > statement = context .registerCopy (
138+ //noinspection unchecked
139+ final SqmSelectStatement <X > statement = (SqmSelectStatement <X >) context .registerCopy (
133140 this ,
134141 new SqmSelectStatement <>(
135142 nodeBuilder (),
136143 copyCteStatements ( context ),
137- getResultType () ,
144+ resultType ,
138145 getQuerySource (),
139146 parameters
140147 )
141148 );
142- statement .setQueryPart ( getQueryPart ().copy ( context ) );
149+ //noinspection unchecked
150+ statement .setQueryPart ( (SqmQueryPart <X >) getQueryPart ().copy ( context ) );
143151 return statement ;
144152 }
145153
@@ -266,9 +274,6 @@ public SqmSelectStatement<T> select(Selection<? extends T> selection) {
266274 checkSelectionIsJpaCompliant ( selection );
267275 }
268276 getQuerySpec ().setSelection ( (JpaSelection <T >) selection );
269- if ( getResultType () == Object .class ) {
270- setResultType ( (Class <T >) selection .getJavaType () );
271- }
272277 return this ;
273278 }
274279
@@ -309,7 +314,6 @@ public SqmSelectStatement<T> multiselect(List<Selection<?>> selectionList) {
309314 break ;
310315 }
311316 default : {
312- setResultType ( (Class <T >) Object [].class );
313317 resultSelection = ( Selection <? extends T > ) nodeBuilder ().array ( selections );
314318 }
315319 }
@@ -460,49 +464,43 @@ private void validateComplianceFetchOffset() {
460464 }
461465
462466 @ Override
463- public JpaCriteriaQuery <Long > createCountQuery () {
464- final SqmCopyContext context = new NoParamSqmCopyContext () {
465- @ Override
466- public boolean copyFetchedFlag () {
467- return false ;
467+ public SqmSelectStatement <Long > createCountQuery () {
468+ final SqmSelectStatement <?> copy = createCopy ( noParamCopyContext (), Object .class );
469+ final SqmQueryPart <?> queryPart = copy .getQueryPart ();
470+ final SqmQuerySpec <?> querySpec ;
471+ //TODO: detect queries with no 'group by', but aggregate functions
472+ // in 'select' list (we don't even need to hit the database to
473+ // know they return exactly one row)
474+ if ( queryPart .isSimpleQueryPart ()
475+ && !( querySpec = (SqmQuerySpec <?>) queryPart ).isDistinct ()
476+ && querySpec .getGroupingExpressions ().isEmpty () ) {
477+ for ( SqmRoot <?> root : querySpec .getRootList () ) {
478+ root .removeLeftFetchJoins ();
468479 }
469- };
470- final NodeBuilder nodeBuilder = nodeBuilder ();
471- final Set <SqmParameter <?>> parameters ;
472- if ( this .parameters == null ) {
473- parameters = null ;
480+ querySpec .getSelectClause ().setSelection ( nodeBuilder ().count ( new SqmStar ( nodeBuilder () )) );
481+ if ( querySpec .getFetch () == null && querySpec .getOffset () == null ) {
482+ querySpec .setOrderByClause ( null );
483+ }
484+
485+ return (SqmSelectStatement <Long >) copy ;
474486 }
475487 else {
476- parameters = new LinkedHashSet <>( this .parameters .size () );
477- for ( SqmParameter <?> parameter : this .parameters ) {
478- parameters .add ( parameter .copy ( context ) );
488+ final JpaSelection <?> selection = queryPart .getFirstQuerySpec ().getSelection ();
489+ if ( selection .isCompoundSelection () ) {
490+ char c = 'a' ;
491+ for ( JpaSelection <?> item : selection .getSelectionItems () ) {
492+ item .alias ( Character .toString ( ++c ) + '_' );
493+ }
479494 }
495+ else {
496+ selection .alias ( "a_" );
497+ }
498+ final SqmSubQuery <?> subquery = new SqmSubQuery <>( copy , queryPart , null , nodeBuilder () );
499+ final SqmSelectStatement <Long > query = nodeBuilder ().createQuery ( Long .class );
500+ query .from ( subquery );
501+ query .select ( nodeBuilder ().count ( new SqmStar (nodeBuilder ())) );
502+ return query ;
480503 }
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 ;
506504 }
507505
508506 private void resetSelections (SqmQueryPart <?> queryPart ) {
0 commit comments