Skip to content

Commit 2c6d439

Browse files
committed
sort out typing issues with NamedCriteriaQueryMemento
check the result type
1 parent e9769ec commit 2c6d439

File tree

6 files changed

+107
-66
lines changed

6 files changed

+107
-66
lines changed

hibernate-core/src/main/java/org/hibernate/query/criteria/internal/NamedCriteriaQueryMementoImpl.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package org.hibernate.query.criteria.internal;
66

77
import java.io.Serializable;
8+
import java.util.Locale;
89
import java.util.Map;
910

1011
import org.hibernate.CacheMode;
1112
import org.hibernate.FlushMode;
1213
import org.hibernate.LockOptions;
1314
import org.hibernate.engine.spi.SharedSessionContractImplementor;
15+
import org.hibernate.query.IllegalSelectQueryException;
16+
import org.hibernate.query.QueryTypeMismatchException;
1417
import org.hibernate.query.hql.spi.SqmQueryImplementor;
1518
import org.hibernate.query.named.AbstractNamedQueryMemento;
1619
import org.hibernate.query.spi.QueryEngine;
@@ -21,6 +24,7 @@
2124
import org.hibernate.query.sqm.tree.SqmStatement;
2225

2326
import org.checkerframework.checker.nullness.qual.Nullable;
27+
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
2428

2529
public class NamedCriteriaQueryMementoImpl<E> extends AbstractNamedQueryMemento<E>
2630
implements NamedSqmQueryMemento<E>, Serializable {
@@ -60,12 +64,32 @@ public NamedCriteriaQueryMementoImpl(
6064

6165
@Override
6266
public void validate(QueryEngine queryEngine) {
67+
// nothing to do
68+
}
6369

70+
private static <T> void checkResultType(Class<T> resultType, SqmSelectStatement<?> selectStatement) {
71+
final Class<?> expectedResultType = selectStatement.getResultType();
72+
if ( expectedResultType != null
73+
&& !resultType.isAssignableFrom( expectedResultType ) ) {
74+
throw new QueryTypeMismatchException(
75+
String.format(
76+
Locale.ROOT,
77+
"Incorrect query result type: query produces '%s' but type '%s' was given",
78+
expectedResultType.getName(),
79+
resultType.getName()
80+
)
81+
);
82+
}
6483
}
6584

6685
@Override
6786
public <T> SqmQueryImplementor<T> toQuery(SharedSessionContractImplementor session, Class<T> resultType) {
68-
return new QuerySqmImpl<>( this, resultType, session );
87+
if ( sqmStatement instanceof SqmSelectStatement<?> selectStatement ) {
88+
checkResultType( resultType, selectStatement );
89+
}
90+
@SuppressWarnings("unchecked") // we just checked the result type
91+
final SqmStatement<T> statement = (SqmStatement<T>) sqmStatement;
92+
return new QuerySqmImpl<>( this, statement, resultType, session );
6993
}
7094

7195
@Override
@@ -75,7 +99,13 @@ public SqmQueryImplementor<E> toQuery(SharedSessionContractImplementor session)
7599

76100
@Override
77101
public <T> SqmSelectionQuery<T> toSelectionQuery(Class<T> resultType, SharedSessionContractImplementor session) {
78-
return new SqmSelectionQueryImpl<>( this, resultType, session );
102+
if ( !( sqmStatement instanceof SqmSelectStatement<?> selectStatement ) ) {
103+
throw new IllegalSelectQueryException( "Named query is not a SELECT statement: " + getName() );
104+
}
105+
checkResultType( resultType, selectStatement );
106+
@SuppressWarnings("unchecked") // we just checked the result type
107+
final SqmSelectStatement<T> statement = (SqmSelectStatement<T>) selectStatement;
108+
return new SqmSelectionQueryImpl<>( this, statement, resultType, session );
79109
}
80110

81111
@Override
@@ -110,7 +140,7 @@ public Map<String, String> getParameterTypes() {
110140

111141
@Override
112142
public NamedSqmQueryMemento<E> makeCopy(String name) {
113-
return new NamedCriteriaQueryMementoImpl<E>(
143+
return new NamedCriteriaQueryMementoImpl<>(
114144
name,
115145
getResultType(),
116146
sqmStatement,

hibernate-core/src/main/java/org/hibernate/query/named/NameableQuery.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,25 @@
44
*/
55
package org.hibernate.query.named;
66

7+
import jakarta.persistence.Query;
78
import org.hibernate.Incubating;
89

910
/**
10-
* Contract for Query impls that can be converted to a named query memento to be
11-
* stored in the {@link NamedObjectRepository}
11+
* Contract for {@linkplain org.hibernate.query.spi.QueryImplementor query implementations}
12+
* which can be converted to {@linkplain NamedQueryMemento named query mementos} for storage
13+
* in the {@link NamedObjectRepository}.
1214
*
1315
* @author Steve Ebersole
16+
*
17+
* @see NamedQueryMemento
1418
*/
1519
@Incubating
1620
public interface NameableQuery {
1721
/**
18-
* Convert the query into the memento
22+
* Convert this query into the memento.
23+
*
24+
* @see org.hibernate.SessionFactory#addNamedQuery(String, Query)
25+
* @see NamedObjectRepository#registerNamedQuery(String, Query)
1926
*/
2027
NamedQueryMemento<?> toMemento(String name);
2128
}

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.hibernate.query.restriction.Restriction;
1717
import org.hibernate.query.SelectionQuery;
1818
import org.hibernate.query.criteria.JpaSelection;
19-
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
2019
import org.hibernate.query.hql.internal.QuerySplitter;
2120
import org.hibernate.query.spi.AbstractSelectionQuery;
2221
import org.hibernate.query.spi.HqlInterpretation;
@@ -169,7 +168,7 @@ public KeyedResultList<R> getKeyedResultList(KeyedPage<R> keyedPage) {
169168
final List<KeyedResult<R>> results =
170169
new SqmSelectionQueryImpl<KeyedResult<R>>( this, keyedPage )
171170
.getResultList();
172-
int pageSize = keyedPage.getPage().getSize();
171+
final int pageSize = keyedPage.getPage().getSize();
173172
return new KeyedResultList<>(
174173
collectResults( results, pageSize, keyedPage.getKeyInterpretation() ),
175174
collectKeys( results, pageSize ),
@@ -388,7 +387,7 @@ else if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
388387
}
389388

390389
protected static <T> HqlInterpretation<T> interpretation(
391-
NamedHqlQueryMementoImpl<?> memento,
390+
NamedSqmQueryMemento<?> memento,
392391
Class<T> expectedResultType,
393392
SharedSessionContractImplementor session) {
394393
final QueryEngine queryEngine = session.getFactory().getQueryEngine();

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.ArrayList;
99
import java.util.Calendar;
1010
import java.util.Collection;
11-
import java.util.Collections;
1211
import java.util.Date;
1312
import java.util.HashSet;
1413
import java.util.List;
@@ -93,6 +92,7 @@
9392
import jakarta.persistence.TemporalType;
9493
import org.hibernate.sql.results.spi.SingleResultConsumer;
9594

95+
import static java.util.Collections.emptyMap;
9696
import static org.hibernate.jpa.HibernateHints.HINT_CACHEABLE;
9797
import static org.hibernate.jpa.HibernateHints.HINT_CACHE_MODE;
9898
import static org.hibernate.jpa.HibernateHints.HINT_CACHE_REGION;
@@ -132,27 +132,31 @@ public class QuerySqmImpl<R>
132132
private final TupleMetadata tupleMetadata;
133133

134134
/**
135-
* Creates a Query instance from a named HQL memento
135+
* Creates a {@link org.hibernate.query.Query}
136+
* instance from a named HQL memento.
137+
* Form used from {@link NamedHqlQueryMementoImpl}.
136138
*/
137139
public QuerySqmImpl(
138-
NamedHqlQueryMementoImpl<?> memento,
140+
NamedSqmQueryMemento<?> memento,
139141
Class<R> expectedResultType,
140142
SharedSessionContractImplementor session) {
141-
this(
142-
memento.getHqlString(),
143+
this( memento.getHqlString(),
143144
interpretation( memento, expectedResultType, session ),
144-
expectedResultType,
145-
session
146-
);
145+
expectedResultType, session );
147146
applySqmOptions( memento );
148147
}
149148

149+
/**
150+
* Creates a {@link org.hibernate.query.Query}
151+
* instance from a named criteria query memento.
152+
* Form used from {@link NamedCriteriaQueryMementoImpl}
153+
*/
150154
public QuerySqmImpl(
151-
NamedCriteriaQueryMementoImpl<?> memento,
155+
NamedSqmQueryMemento<?> memento,
156+
SqmStatement<R> statement,
152157
Class<R> resultType,
153158
SharedSessionContractImplementor session) {
154-
this( (SqmStatement<R>) memento.getSqmStatement(), resultType, session );
155-
159+
this( statement, resultType, session );
156160
applySqmOptions( memento );
157161
}
158162

@@ -177,10 +181,8 @@ public QuerySqmImpl(
177181
if ( sqm instanceof SqmSelectStatement<?> ) {
178182
hqlInterpretation.validateResultType( resultType );
179183
}
180-
else {
181-
if ( resultType != null ) {
182-
throw new IllegalQueryOperationException( "Result type given for a non-SELECT Query", hql, null );
183-
}
184+
else if ( resultType != null ) {
185+
throw new IllegalQueryOperationException( "Result type given for a non-SELECT Query", hql, null );
184186
}
185187
setComment( hql );
186188

@@ -882,18 +884,12 @@ public SqmQueryImplementor<R> applyGraph(@SuppressWarnings("rawtypes") RootGraph
882884
@Override
883885
public NamedSqmQueryMemento<R> toMemento(String name) {
884886
if ( CRITERIA_HQL_STRING.equals( getQueryString() ) ) {
885-
final SqmStatement<R> sqmStatement;
886-
if ( !getSession().isCriteriaCopyTreeEnabled() ) {
887-
sqmStatement = getSqmStatement().copy( SqmCopyContext.simpleContext() );
888-
}
889-
else {
890-
// the statement has already been copied
891-
sqmStatement = getSqmStatement();
892-
}
893887
return new NamedCriteriaQueryMementoImpl<>(
894888
name,
895889
getResultType(),
896-
sqmStatement,
890+
getSession().isCriteriaCopyTreeEnabled()
891+
? getSqmStatement() // the statement has already been copied
892+
: getSqmStatement().copy( SqmCopyContext.simpleContext() ),
897893
getQueryOptions().getLimit().getFirstRow(),
898894
getQueryOptions().getLimit().getMaxRows(),
899895
isCacheable(),
@@ -905,29 +901,30 @@ public NamedSqmQueryMemento<R> toMemento(String name) {
905901
getTimeout(),
906902
getFetchSize(),
907903
getComment(),
908-
Collections.emptyMap(),
904+
emptyMap(),
905+
getHints()
906+
);
907+
}
908+
else {
909+
return new NamedHqlQueryMementoImpl<>(
910+
name,
911+
getResultType(),
912+
getQueryString(),
913+
getQueryOptions().getLimit().getFirstRow(),
914+
getQueryOptions().getLimit().getMaxRows(),
915+
isCacheable(),
916+
getCacheRegion(),
917+
getCacheMode(),
918+
getQueryOptions().getFlushMode(),
919+
isReadOnly(),
920+
getLockOptions(),
921+
getTimeout(),
922+
getFetchSize(),
923+
getComment(),
924+
emptyMap(),
909925
getHints()
910926
);
911927
}
912-
913-
return new NamedHqlQueryMementoImpl<>(
914-
name,
915-
getResultType(),
916-
getQueryString(),
917-
getQueryOptions().getLimit().getFirstRow(),
918-
getQueryOptions().getLimit().getMaxRows(),
919-
isCacheable(),
920-
getCacheRegion(),
921-
getCacheMode(),
922-
getQueryOptions().getFlushMode(),
923-
isReadOnly(),
924-
getLockOptions(),
925-
getTimeout(),
926-
getFetchSize(),
927-
getComment(),
928-
Collections.emptyMap(),
929-
getHints()
930-
);
931928
}
932929

933930

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmSelectionQueryImpl.java

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ public class SqmSelectionQueryImpl<R> extends AbstractSqmSelectionQuery<R>
9999
private final Class<?> resultType;
100100
private final TupleMetadata tupleMetadata;
101101

102+
/**
103+
* Form used for HQL queries
104+
*/
102105
public SqmSelectionQueryImpl(
103106
String hql,
104107
HqlInterpretation<R> hqlInterpretation,
@@ -114,7 +117,6 @@ public SqmSelectionQueryImpl(
114117
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
115118
this.parameterBindings = parameterMetadata.createBindings( session.getFactory() );
116119

117-
118120
this.expectedResultType = expectedResultType;
119121
this.resultType = determineResultType( sqm, expectedResultType );
120122
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
@@ -123,29 +125,38 @@ public SqmSelectionQueryImpl(
123125
setComment( hql );
124126
}
125127

128+
/**
129+
* Creates a {@link org.hibernate.query.SelectionQuery}
130+
* instance from a named HQL memento.
131+
* Form used from {@link NamedHqlQueryMementoImpl}.
132+
*/
126133
public SqmSelectionQueryImpl(
127134
NamedHqlQueryMementoImpl<?> memento,
128135
Class<R> resultType,
129136
SharedSessionContractImplementor session) {
130-
this(
131-
memento.getHqlString(),
137+
this( memento.getHqlString(),
132138
interpretation( memento, resultType, session ),
133-
resultType,
134-
session
135-
);
136-
139+
resultType, session );
137140
applySqmOptions( memento );
138141
}
139142

143+
/**
144+
* Creates a {@link org.hibernate.query.SelectionQuery}
145+
* instance from a named criteria query memento.
146+
* Form used from {@link NamedCriteriaQueryMementoImpl}
147+
*/
140148
public SqmSelectionQueryImpl(
141149
NamedCriteriaQueryMementoImpl<?> memento,
150+
SqmSelectStatement<R> selectStatement,
142151
Class<R> expectedResultType,
143152
SharedSessionContractImplementor session) {
144-
//noinspection unchecked
145-
this( (SqmSelectStatement<R>) memento.getSqmStatement(), expectedResultType, session );
153+
this( selectStatement, expectedResultType, session );
146154
applySqmOptions( memento );
147155
}
148156

157+
/**
158+
* Form used for criteria queries
159+
*/
149160
public SqmSelectionQueryImpl(
150161
SqmSelectStatement<R> criteria,
151162
Class<R> expectedResultType,

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,7 @@ private SqmSelectStatement(
124124
@Override
125125
public SqmSelectStatement<T> copy(SqmCopyContext context) {
126126
final SqmSelectStatement<T> existing = context.getCopy( this );
127-
if ( existing != null ) {
128-
return existing;
129-
}
130-
return createCopy( context, getResultType() );
127+
return existing != null ? existing : createCopy( context, getResultType() );
131128
}
132129

133130
@Internal

0 commit comments

Comments
 (0)