Skip to content

Commit f967cbf

Browse files
committed
HHH-19364 - Introduce simplified dynamic query builders
1 parent 0122a5a commit f967cbf

19 files changed

+617
-11
lines changed

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java

+12
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
import org.hibernate.persister.entity.EntityPersister;
4747
import org.hibernate.procedure.ProcedureCall;
4848
import org.hibernate.query.MutationQuery;
49+
import org.hibernate.query.DynamicMutation;
4950
import org.hibernate.query.SelectionQuery;
51+
import org.hibernate.query.DynamicSelection;
5052
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
5153
import org.hibernate.query.criteria.JpaCriteriaInsert;
5254
import org.hibernate.query.spi.QueryImplementor;
@@ -656,6 +658,16 @@ public MutationQuery createNamedMutationQuery(String name) {
656658
return delegate.createNamedMutationQuery( name );
657659
}
658660

661+
@Override
662+
public <T> DynamicSelection<T> createDynamicSelection(String hql, Class<T> resultType) {
663+
return delegate.createDynamicSelection( hql, resultType );
664+
}
665+
666+
@Override
667+
public <T> DynamicMutation<T> createDynamicMutation(String hql, Class<T> mutationTarget) {
668+
return delegate.createDynamicMutation( hql, mutationTarget );
669+
}
670+
659671
@Override
660672
public MutationQuery createNativeMutationQuery(String sqlString) {
661673
return delegate.createNativeMutationQuery( sqlString );

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionLazyDelegator.java

+12
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@
3636
import org.hibernate.jdbc.Work;
3737
import org.hibernate.procedure.ProcedureCall;
3838
import org.hibernate.query.MutationQuery;
39+
import org.hibernate.query.DynamicMutation;
3940
import org.hibernate.query.NativeQuery;
4041
import org.hibernate.query.Query;
4142
import org.hibernate.query.SelectionQuery;
43+
import org.hibernate.query.DynamicSelection;
4244
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
4345
import org.hibernate.query.criteria.JpaCriteriaInsert;
4446
import org.hibernate.stat.SessionStatistics;
@@ -749,6 +751,16 @@ public MutationQuery createNamedMutationQuery(String name) {
749751
return this.lazySession.get().createNamedMutationQuery( name );
750752
}
751753

754+
@Override
755+
public <T> DynamicSelection<T> createDynamicSelection(String hql, Class<T> resultType) {
756+
return this.lazySession.get().createDynamicSelection( hql, resultType );
757+
}
758+
759+
@Override
760+
public <T> DynamicMutation<T> createDynamicMutation(String hql, Class<T> mutationTarget) {
761+
return this.lazySession.get().createDynamicMutation( hql, mutationTarget );
762+
}
763+
752764
@SuppressWarnings("rawtypes")
753765
@Override
754766
@Deprecated

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java

+12
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
import org.hibernate.persister.entity.EntityPersister;
3535
import org.hibernate.procedure.ProcedureCall;
3636
import org.hibernate.query.MutationQuery;
37+
import org.hibernate.query.DynamicMutation;
3738
import org.hibernate.query.SelectionQuery;
39+
import org.hibernate.query.DynamicSelection;
3840
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
3941
import org.hibernate.query.criteria.JpaCriteriaInsert;
4042
import org.hibernate.query.spi.QueryImplementor;
@@ -228,6 +230,16 @@ public MutationQuery createNamedMutationQuery(String name) {
228230
return delegate.createNamedMutationQuery( name );
229231
}
230232

233+
@Override
234+
public <T> DynamicSelection<T> createDynamicSelection(String hql, Class<T> resultType) {
235+
return delegate.createDynamicSelection( hql, resultType );
236+
}
237+
238+
@Override
239+
public <T> DynamicMutation<T> createDynamicMutation(String hql, Class<T> mutationTarget) {
240+
return delegate.createDynamicMutation( hql, mutationTarget );
241+
}
242+
231243
@Override
232244
public MutationQuery createNativeMutationQuery(String sqlString) {
233245
return delegate.createNativeMutationQuery( sqlString );

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

+14
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,17 @@
6868
import org.hibernate.query.IllegalNamedQueryOptionsException;
6969
import org.hibernate.query.IllegalSelectQueryException;
7070
import org.hibernate.query.MutationQuery;
71+
import org.hibernate.query.DynamicMutation;
7172
import org.hibernate.query.Query;
7273
import org.hibernate.query.QueryTypeMismatchException;
7374
import org.hibernate.query.SelectionQuery;
75+
import org.hibernate.query.DynamicSelection;
7476
import org.hibernate.query.UnknownNamedQueryException;
7577
import org.hibernate.query.criteria.CriteriaDefinition;
7678
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
7779
import org.hibernate.query.criteria.JpaCriteriaInsert;
80+
import org.hibernate.query.dynamic.internal.DynamicMutationHqlImpl;
81+
import org.hibernate.query.dynamic.internal.DynamicSelectionHqlImpl;
7882
import org.hibernate.query.hql.spi.SqmQueryImplementor;
7983
import org.hibernate.query.named.NamedObjectRepository;
8084
import org.hibernate.query.named.NamedResultSetMappingMemento;
@@ -1257,6 +1261,16 @@ public MutationQuery createNamedMutationQuery(String queryName) {
12571261
memento -> createNativeQueryImplementor( queryName, memento ) );
12581262
}
12591263

1264+
@Override
1265+
public <T> DynamicSelection<T> createDynamicSelection(String hql, Class<T> resultType) {
1266+
return new DynamicSelectionHqlImpl<>( hql, resultType, this );
1267+
}
1268+
1269+
@Override
1270+
public <T> DynamicMutation<T> createDynamicMutation(String hql, Class<T> mutationTarget) {
1271+
return new DynamicMutationHqlImpl<>( hql, mutationTarget, this );
1272+
}
1273+
12601274
protected <T> NativeQueryImplementor<T> createNativeQueryImplementor(String queryName, NamedNativeQueryMemento<T> memento) {
12611275
final NativeQueryImplementor<T> query = memento.toQuery( this );
12621276
final Boolean isUnequivocallySelect = query.isSelectQuery();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query;
6+
7+
import org.hibernate.Incubating;
8+
import org.hibernate.query.restriction.Restriction;
9+
10+
/**
11+
* A dynamic builder for {@linkplain SelectionQuery} instances.
12+
* Once all {@linkplain #addRestriction restrictions} are defined,
13+
* call {@linkplain #createQuery()} to obtain the executable form.
14+
*
15+
* @see QueryProducer#createDynamicMutation(String)
16+
*
17+
* @author Steve Ebersole
18+
*/
19+
@Incubating
20+
public interface DynamicMutation<T> {
21+
DynamicMutation<T> addRestriction(Restriction<T> restriction);
22+
23+
MutationQuery createQuery();
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query;
6+
7+
import org.hibernate.Incubating;
8+
import org.hibernate.query.restriction.Restriction;
9+
10+
/**
11+
* A dynamic builder for {@linkplain SelectionQuery} instances.
12+
* Once all {@linkplain #addOrder sorting} and {@linkplain #addRestriction restrictions}
13+
* are defined, call {@linkplain #createQuery()} to obtain the executable form.
14+
*
15+
* @see QueryProducer#createDynamicSelection(String, Class)
16+
*
17+
* @author Steve Ebersole
18+
*/
19+
@Incubating
20+
public interface DynamicSelection<T> {
21+
/**
22+
* Adds a sort specification to the in-flight selection definition.
23+
*
24+
* @param order The sorting fragment to be added.
25+
*
26+
* @return {@code this} for method chaining.
27+
*/
28+
DynamicSelection<T> addOrder(Order<T> order);
29+
30+
/**
31+
* Adds a restriction to the in-flight selection definition.
32+
*
33+
* @param restriction The restriction predicate to be added.
34+
*
35+
* @return {@code this} for method chaining.
36+
*/
37+
DynamicSelection<T> addRestriction(Restriction<T> restriction);
38+
39+
/**
40+
* Finalize the building and create the {@linkplain SelectionQuery} instance.
41+
*/
42+
SelectionQuery<T> createQuery();
43+
}

hibernate-core/src/main/java/org/hibernate/query/QueryProducer.java

+32
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.query;
66

77
import jakarta.persistence.EntityGraph;
8+
import org.hibernate.Incubating;
89
import org.hibernate.query.criteria.JpaCriteriaInsert;
910

1011
import jakarta.persistence.TypedQueryReference;
@@ -497,6 +498,37 @@ public interface QueryProducer {
497498
*/
498499
MutationQuery createNamedMutationQuery(String name);
499500

501+
/**
502+
* Returns an in-flight delegate which can be used to iteratively build
503+
* a {@linkplain SelectionQuery} based on a base HQL statement, allowing the
504+
* addition of {@linkplain DynamicSelection#addOrder sorting}
505+
* and {@linkplain DynamicSelection#addRestriction restrictions}.
506+
*
507+
* @param hql The base HQL query.
508+
* @param resultType The result type which will ultimately be returned from the {@linkplain SelectionQuery}
509+
*
510+
* @param <T> The root entity type for the query.
511+
*
512+
* @apiNote {@code resultType} and {@code <T>} are both expected to refer to a singular query root.
513+
*/
514+
@Incubating
515+
<T> DynamicSelection<T> createDynamicSelection(String hql, Class<T> resultType);
516+
517+
/**
518+
* Returns an in-flight delegate which can be used to iteratively build
519+
* a {@linkplain MutationQuery} based on a base HQL statement, allowing the
520+
* addition of {@linkplain DynamicSelection#addRestriction restrictions}.
521+
*
522+
* @param hql The base HQL query.
523+
* @param mutationTarget The entity which is the target of the mutation.
524+
*
525+
* @param <T> The root entity type for the mutation (the "target").
526+
*
527+
* @apiNote {@code mutationTarget} and {@code <T>} are both expected to refer to the mutation target.
528+
*/
529+
@Incubating
530+
<T> DynamicMutation<T> createDynamicMutation(String hql, Class<T> mutationTarget);
531+
500532
/**
501533
* Create a {@link Query} instance for the named query.
502534
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.dynamic.internal;
6+
7+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
8+
import org.hibernate.query.DynamicMutation;
9+
import org.hibernate.query.IllegalMutationQueryException;
10+
import org.hibernate.query.MutationQuery;
11+
import org.hibernate.query.internal.ParameterMetadataImpl;
12+
import org.hibernate.query.restriction.Restriction;
13+
import org.hibernate.query.spi.HqlInterpretation;
14+
import org.hibernate.query.spi.ParameterMetadataImplementor;
15+
import org.hibernate.query.spi.QueryEngine;
16+
import org.hibernate.query.spi.QueryInterpretationCache;
17+
import org.hibernate.query.spi.SimpleHqlInterpretationImpl;
18+
import org.hibernate.query.sqm.internal.DomainParameterXref;
19+
import org.hibernate.query.sqm.internal.QuerySqmImpl;
20+
import org.hibernate.query.sqm.internal.SimpleSqmRenderContext;
21+
import org.hibernate.query.sqm.internal.SqmUtil;
22+
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
23+
import org.hibernate.query.sqm.tree.from.SqmRoot;
24+
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
25+
26+
import java.util.Locale;
27+
28+
/**
29+
* Standard implementation for HQL-based DynamicMutation
30+
*
31+
* @author Steve Ebersole
32+
*/
33+
public class DynamicMutationHqlImpl<T> implements DynamicMutation<T> {
34+
private final String hql;
35+
private final SharedSessionContractImplementor session;
36+
37+
private final SqmDeleteOrUpdateStatement<T> sqmStatement;
38+
private final SqmRoot<T> mutationTargetRoot;
39+
40+
private final SimpleSqmRenderContext sqmRenderContext;
41+
private final StringBuilder cacheKeyRestrictions;
42+
43+
public DynamicMutationHqlImpl(
44+
String hql,
45+
Class<T> mutationTarget,
46+
SharedSessionContractImplementor session) {
47+
this.hql = hql;
48+
this.session = session;
49+
50+
final QueryEngine queryEngine = session.getFactory().getQueryEngine();
51+
final HqlInterpretation<T> hqlInterpretation = queryEngine
52+
.getInterpretationCache()
53+
.resolveHqlInterpretation( hql, null, queryEngine.getHqlTranslator() );
54+
55+
if ( !SqmUtil.isRestrictedMutation( hqlInterpretation.getSqmStatement() ) ) {
56+
throw new IllegalMutationQueryException( "Expecting a delete or update query, but found '" + hql + "'", hql);
57+
}
58+
59+
this.sqmStatement = (SqmDeleteOrUpdateStatement<T>) hqlInterpretation.getSqmStatement();
60+
this.mutationTargetRoot = this.sqmStatement.getTarget();
61+
if ( mutationTargetRoot.getJavaType() != null
62+
&& !mutationTarget.isAssignableFrom( mutationTargetRoot.getJavaType() ) ) {
63+
throw new IllegalArgumentException(
64+
String.format(
65+
Locale.ROOT,
66+
"Mutation target types do not match : %s / %s",
67+
mutationTargetRoot.getJavaType().getName(),
68+
mutationTarget.getName()
69+
)
70+
);
71+
}
72+
73+
this.sqmRenderContext = new SimpleSqmRenderContext();
74+
this.cacheKeyRestrictions = new StringBuilder();
75+
}
76+
77+
@Override
78+
public DynamicMutation<T> addRestriction(Restriction<T> restriction) {
79+
final SqmPredicate sqmPredicate = (SqmPredicate) restriction.toPredicate(
80+
mutationTargetRoot,
81+
sqmStatement.nodeBuilder()
82+
);
83+
sqmPredicate.appendHqlString( cacheKeyRestrictions, sqmRenderContext );
84+
sqmStatement.applyPredicate( sqmPredicate );
85+
86+
return this;
87+
}
88+
89+
@Override
90+
public MutationQuery createQuery() {
91+
// todo : much of this is copied from QueryInterpretationCacheStandardImpl
92+
// come up with a proper abstraction and unify the code
93+
// see also HqlDynamicSelectionImpl#createQuery
94+
final ParameterMetadataImplementor parameterMetadata;
95+
final DomainParameterXref domainParameterXref;
96+
97+
if ( sqmStatement.getSqmParameters().isEmpty() ) {
98+
domainParameterXref = DomainParameterXref.EMPTY;
99+
parameterMetadata = ParameterMetadataImpl.EMPTY;
100+
}
101+
else {
102+
domainParameterXref = DomainParameterXref.from( sqmStatement );
103+
parameterMetadata = new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
104+
}
105+
106+
final SimpleHqlInterpretationImpl<T> hqlInterpretation = new SimpleHqlInterpretationImpl<>(
107+
sqmStatement,
108+
parameterMetadata,
109+
domainParameterXref
110+
);
111+
112+
final String cacheKey = hql + " (" + cacheKeyRestrictions + ")";
113+
114+
final QueryInterpretationCache interpretationCache = session.getFactory().getQueryEngine().getInterpretationCache();
115+
interpretationCache.cacheHqlInterpretation( cacheKey, hqlInterpretation );
116+
117+
return new QuerySqmImpl<>(
118+
hql,
119+
cacheKey,
120+
hqlInterpretation,
121+
session
122+
);
123+
}
124+
}

0 commit comments

Comments
 (0)