Skip to content

Commit 4c5437e

Browse files
committed
HHH-19364 proper implementation of validate()
because @sebersole complained
1 parent 3718329 commit 4c5437e

File tree

10 files changed

+95
-49
lines changed

10 files changed

+95
-49
lines changed

hibernate-core/src/main/java/org/hibernate/query/programmatic/MutationSpecification.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ interface Augmentation<T> {
7676
@Override
7777
MutationQuery createQuery(StatelessSession session);
7878

79+
@Override
80+
MutationSpecification<T> validate(CriteriaBuilder builder);
81+
7982
/**
8083
* Returns a specification reference which can be used to programmatically,
8184
* iteratively build a {@linkplain MutationQuery} based on a base HQL statement,

hibernate-core/src/main/java/org/hibernate/query/programmatic/QuerySpecification.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010

1111
import org.hibernate.Incubating;
1212
import org.hibernate.Session;
13-
import org.hibernate.SessionFactory;
1413
import org.hibernate.StatelessSession;
15-
import org.hibernate.engine.spi.SessionFactoryImplementor;
1614
import org.hibernate.query.CommonQueryContract;
1715
import org.hibernate.query.restriction.Restriction;
1816

@@ -69,15 +67,9 @@ public interface QuerySpecification<T> {
6967

7068
/**
7169
* Validate the query.
70+
*
71+
* @return {@code this} if everything is fine
72+
* @throws Exception if it ain't all good
7273
*/
73-
default void validate(SessionFactory factory) {
74-
// Extremely temporary implementation.
75-
// We don't actually want to open a session here,
76-
// nor create an instance of CommonQueryContract.
77-
final SessionFactoryImplementor factoryImplementor =
78-
(SessionFactoryImplementor) factory;
79-
try ( var session = factoryImplementor.openTemporarySession() ) {
80-
createQuery( session );
81-
}
82-
}
74+
QuerySpecification<T> validate(CriteriaBuilder builder);
8375
}

hibernate-core/src/main/java/org/hibernate/query/programmatic/SelectionSpecification.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,12 @@ interface Augmentation<T> {
177177
*
178178
* @return a new criteria query
179179
*/
180+
@Override
180181
CriteriaQuery<T> buildCriteriaQuery(CriteriaBuilder builder);
181182

183+
@Override
184+
SelectionSpecification<T> validate(CriteriaBuilder builder);
185+
182186
/**
183187
* Returns a specification reference which can be used to programmatically,
184188
* iteratively build a {@linkplain SelectionQuery} for the given entity type,

hibernate-core/src/main/java/org/hibernate/query/programmatic/internal/MutationSpecificationImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.hibernate.query.sqm.SqmQuerySource;
2626
import org.hibernate.query.sqm.internal.QuerySqmImpl;
2727
import org.hibernate.query.sqm.internal.SqmUtil;
28+
import org.hibernate.query.sqm.tree.AbstractSqmDmlStatement;
2829
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
2930
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
3031
import org.hibernate.query.sqm.tree.from.SqmRoot;
@@ -133,6 +134,14 @@ public CommonAbstractCriteria buildCriteriaQuery(CriteriaBuilder builder) {
133134
return build( nodeBuilder.getQueryEngine() );
134135
}
135136

137+
@Override
138+
public MutationSpecification<T> validate(CriteriaBuilder builder) {
139+
final NodeBuilder nodeBuilder = (NodeBuilder) builder;
140+
final var statement = build( nodeBuilder.getQueryEngine() );
141+
( (AbstractSqmDmlStatement<?>) statement ).validate( hql );
142+
return this;
143+
}
144+
136145
/**
137146
* Used during construction to parse/interpret the incoming HQL
138147
* and produce the corresponding SQM tree.

hibernate-core/src/main/java/org/hibernate/query/programmatic/internal/SelectionSpecificationImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.Set;
3939
import java.util.function.BiConsumer;
4040

41+
import static org.hibernate.query.sqm.internal.SqmUtil.validateCriteriaQuery;
4142
import static org.hibernate.query.sqm.tree.SqmCopyContext.noParamCopyContext;
4243

4344
/**
@@ -176,6 +177,18 @@ public CriteriaQuery<T> buildCriteriaQuery(CriteriaBuilder builder) {
176177
return build( nodeBuilder.getQueryEngine() );
177178
}
178179

180+
@Override
181+
public SelectionSpecification<T> validate(CriteriaBuilder builder) {
182+
final NodeBuilder nodeBuilder = (NodeBuilder) builder;
183+
final var statement = build( nodeBuilder.getQueryEngine() );
184+
final var queryPart = statement.getQueryPart();
185+
// For criteria queries, we have to validate the fetch structure here
186+
queryPart.validateQueryStructureAndFetchOwners();
187+
validateCriteriaQuery( queryPart );
188+
statement.validateResultType( resultType );
189+
return this;
190+
}
191+
179192
/**
180193
* Used during construction to parse/interpret the incoming HQL
181194
* and produce the corresponding SQM tree.

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

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@
3333
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
3434
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
3535
import org.hibernate.query.sqm.tree.expression.SqmParameter;
36-
import org.hibernate.query.sqm.tree.from.SqmRoot;
37-
import org.hibernate.query.sqm.tree.select.SqmQueryGroup;
38-
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
39-
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
4036
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
4137
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
4238
import org.hibernate.query.sqm.tree.select.SqmSelection;
@@ -421,33 +417,6 @@ private static String[] buildTupleAliasArray(List<SqmSelection<?>> selections) {
421417
}
422418
}
423419

424-
protected static void validateCriteriaQuery(SqmQueryPart<?> queryPart) {
425-
if ( queryPart instanceof SqmQuerySpec<?> sqmQuerySpec ) {
426-
if ( sqmQuerySpec.getSelectClause().getSelections().isEmpty() ) {
427-
// make sure there is at least one root
428-
final List<SqmRoot<?>> sqmRoots = sqmQuerySpec.getFromClause().getRoots();
429-
if ( sqmRoots == null || sqmRoots.isEmpty() ) {
430-
throw new IllegalArgumentException( "Criteria did not define any query roots" );
431-
}
432-
// if there is a single root, use that as the selection
433-
if ( sqmRoots.size() == 1 ) {
434-
sqmQuerySpec.getSelectClause().add( sqmRoots.get( 0 ), null );
435-
}
436-
else {
437-
throw new IllegalArgumentException( "Criteria has multiple query roots" );
438-
}
439-
}
440-
}
441-
else if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
442-
for ( SqmQueryPart<?> part : queryGroup.getQueryParts() ) {
443-
validateCriteriaQuery( part );
444-
}
445-
}
446-
else {
447-
assert false;
448-
}
449-
}
450-
451420
protected static <T> HqlInterpretation<T> interpretation(
452421
NamedSqmQueryMemento<?> memento,
453422
Class<T> expectedResultType,

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.createInterpretationsKey;
107107
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.generateNonSelectKey;
108108
import static org.hibernate.query.sqm.internal.SqmUtil.isSelect;
109+
import static org.hibernate.query.sqm.internal.SqmUtil.validateCriteriaQuery;
109110
import static org.hibernate.query.sqm.internal.SqmUtil.verifyIsNonSelectStatement;
110111

111112
/**
@@ -244,6 +245,14 @@ public QuerySqmImpl(
244245
bindCriteriaParameter( wrapper );
245246
}
246247
}
248+
249+
validateQuery( expectedResultType, sqm, hql );
250+
251+
resultType = expectedResultType;
252+
tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
253+
}
254+
255+
private static <R> void validateQuery(Class<R> expectedResultType, SqmStatement<R> sqm, String hql) {
247256
if ( sqm instanceof SqmSelectStatement<R> selectStatement ) {
248257
final SqmQueryPart<R> queryPart = selectStatement.getQueryPart();
249258
// For criteria queries, we have to validate the fetch structure here
@@ -257,9 +266,6 @@ else if ( sqm instanceof AbstractSqmDmlStatement<R> update ) {
257266
}
258267
update.validate( hql );
259268
}
260-
261-
resultType = expectedResultType;
262-
tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
263269
}
264270

265271
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public SqmSelectionQueryImpl(
214214
final SqmQueryPart<R> queryPart = sqm.getQueryPart();
215215
// For criteria queries, we have to validate the fetch structure here
216216
queryPart.validateQueryStructureAndFetchOwners();
217-
validateCriteriaQuery( queryPart );
217+
SqmUtil.validateCriteriaQuery( queryPart );
218218
sqm.validateResultType( resultType );
219219

220220
setComment( hql );

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,33 @@ public static <X> SqmPredicate restriction(
977977
return (SqmPredicate) restriction.toPredicate( root, sqmStatement.nodeBuilder() );
978978
}
979979

980+
public static void validateCriteriaQuery(SqmQueryPart<?> queryPart) {
981+
if ( queryPart instanceof SqmQuerySpec<?> sqmQuerySpec ) {
982+
if ( sqmQuerySpec.getSelectClause().getSelections().isEmpty() ) {
983+
// make sure there is at least one root
984+
final List<SqmRoot<?>> sqmRoots = sqmQuerySpec.getFromClause().getRoots();
985+
if ( sqmRoots == null || sqmRoots.isEmpty() ) {
986+
throw new IllegalArgumentException( "Criteria did not define any query roots" );
987+
}
988+
// if there is a single root, use that as the selection
989+
if ( sqmRoots.size() == 1 ) {
990+
sqmQuerySpec.getSelectClause().add( sqmRoots.get( 0 ), null );
991+
}
992+
else {
993+
throw new IllegalArgumentException( "Criteria has multiple query roots" );
994+
}
995+
}
996+
}
997+
else if ( queryPart instanceof SqmQueryGroup<?> queryGroup ) {
998+
for ( SqmQueryPart<?> part : queryGroup.getQueryParts() ) {
999+
validateCriteriaQuery( part );
1000+
}
1001+
}
1002+
else {
1003+
assert false;
1004+
}
1005+
}
1006+
9801007
private static class CriteriaParameterCollector {
9811008
private Set<SqmParameter<?>> sqmParameters;
9821009
private Map<JpaCriteriaParameter<?>, List<SqmJpaCriteriaParameterWrapper<?>>> jpaCriteriaParamResolutions;

hibernate-core/src/test/java/org/hibernate/orm/test/query/dynamic/SimpleQuerySpecificationTests.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
*/
55
package org.hibernate.orm.test.query.dynamic;
66

7+
import jakarta.persistence.criteria.CommonAbstractCriteria;
8+
import jakarta.persistence.criteria.CriteriaQuery;
9+
710
import org.hibernate.SessionFactory;
811
import org.hibernate.query.IllegalMutationQueryException;
912
import org.hibernate.query.IllegalSelectQueryException;
@@ -255,7 +258,7 @@ void testIllegalSelection(SessionFactoryScope factoryScope) {
255258
final SessionFactory factory = factoryScope.getSessionFactory();
256259
try {
257260
SelectionSpecification.create( BasicEntity.class, "delete BasicEntity" )
258-
.validate( factory );
261+
.validate( factory.getCriteriaBuilder() );
259262
fail( "Expecting a IllegalSelectQueryException, but not thrown" );
260263
}
261264
catch (IllegalSelectQueryException expected) {
@@ -267,10 +270,30 @@ void testIllegalMutation(SessionFactoryScope factoryScope) {
267270
final SessionFactory factory = factoryScope.getSessionFactory();
268271
try {
269272
MutationSpecification.create( BasicEntity.class, "from BasicEntity" )
270-
.validate( factory );
273+
.validate( factory.getCriteriaBuilder() );
271274
fail( "Expecting a IllegalMutationQueryException, but not thrown" );
272275
}
273276
catch (IllegalMutationQueryException expected) {
274277
}
275278
}
279+
280+
@Test
281+
void testBuildCriteriaDelete(SessionFactoryScope factoryScope) {
282+
final SessionFactory factory = factoryScope.getSessionFactory();
283+
CommonAbstractCriteria deleteBasicEntity =
284+
MutationSpecification.create( BasicEntity.class,
285+
"delete BasicEntity" )
286+
.validate( factory.getCriteriaBuilder() )
287+
.buildCriteriaQuery( factory.getCriteriaBuilder() );
288+
}
289+
290+
@Test
291+
void testBuildCriteriaQuery(SessionFactoryScope factoryScope) {
292+
final SessionFactory factory = factoryScope.getSessionFactory();
293+
CriteriaQuery<BasicEntity> query =
294+
SelectionSpecification.create( BasicEntity.class,
295+
"from BasicEntity" )
296+
.validate( factory.getCriteriaBuilder() )
297+
.buildCriteriaQuery( factory.getCriteriaBuilder() );
298+
}
276299
}

0 commit comments

Comments
 (0)