1515 */
1616package org .springframework .data .jpa .repository .query ;
1717
18- import static org .springframework .data .repository .query .parser .Part .Type .*;
18+ import static org .springframework .data .repository .query .parser .Part .Type .IS_NOT_EMPTY ;
19+ import static org .springframework .data .repository .query .parser .Part .Type .NOT_CONTAINING ;
20+ import static org .springframework .data .repository .query .parser .Part .Type .NOT_LIKE ;
21+ import static org .springframework .data .repository .query .parser .Part .Type .SIMPLE_PROPERTY ;
1922
2023import jakarta .persistence .EntityManager ;
2124import jakarta .persistence .criteria .CriteriaQuery ;
2225import jakarta .persistence .criteria .Expression ;
23- import jakarta .persistence .criteria .From ;
2426import jakarta .persistence .criteria .Predicate ;
27+ import jakarta .persistence .metamodel .Attribute ;
28+ import jakarta .persistence .metamodel .Bindable ;
2529import jakarta .persistence .metamodel .EntityType ;
30+ import jakarta .persistence .metamodel .Metamodel ;
2631import jakarta .persistence .metamodel .SingularAttribute ;
2732
2833import java .util .ArrayList ;
2934import java .util .Collection ;
3035import java .util .Iterator ;
3136import java .util .List ;
37+ import java .util .stream .Collectors ;
3238
3339import org .springframework .data .domain .Sort ;
3440import org .springframework .data .jpa .domain .JpaSort ;
41+ import org .springframework .data .jpa .repository .query .JpqlQueryBuilder .ParameterPlaceholder ;
3542import org .springframework .data .jpa .repository .query .JpqlQueryBuilder .PathAndOrigin ;
3643import org .springframework .data .jpa .repository .query .ParameterBinding .PartTreeParameterBinding ;
3744import org .springframework .data .jpa .repository .support .JpqlQueryTemplates ;
5663 * @author Moritz Becker
5764 * @author Andrey Kovalev
5865 * @author Greg Turnquist
66+ * @author Christoph Strobl
5967 * @author Jinmyeong Kim
6068 */
6169class JpaQueryCreator extends AbstractQueryCreator <String , JpqlQueryBuilder .Predicate > implements JpqlQueryCreator {
@@ -66,8 +74,8 @@ class JpaQueryCreator extends AbstractQueryCreator<String, JpqlQueryBuilder.Pred
6674 private final PartTree tree ;
6775 private final EscapeCharacter escape ;
6876 private final EntityType <?> entityType ;
69- private final From <?, ?> from ;
7077 private final JpqlQueryBuilder .Entity entity ;
78+ private final Metamodel metamodel ;
7179
7280 /**
7381 * Create a new {@link JpaQueryCreator}.
@@ -88,12 +96,12 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvid
8896 this .templates = templates ;
8997 this .escape = provider .getEscape ();
9098 this .entityType = em .getMetamodel ().entity (type .getDomainType ());
91- this .from = em .getCriteriaBuilder ().createQuery ().from (type .getDomainType ());
9299 this .entity = JpqlQueryBuilder .entity (returnedType .getDomainType ());
100+ this .metamodel = em .getMetamodel ();
93101 }
94102
95- From <?, ?> getFrom () {
96- return from ;
103+ Bindable < ?> getFrom () {
104+ return entityType ;
97105 }
98106
99107 JpqlQueryBuilder .Entity getEntity () {
@@ -175,7 +183,7 @@ protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
175183 QueryUtils .checkSortExpression (order );
176184
177185 try {
178- expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (entity , from ,
186+ expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
179187 PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
180188 } catch (PropertyReferenceException e ) {
181189
@@ -210,12 +218,19 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
210218
211219 if (returnedType .needsCustomConstruction ()) {
212220
213- Collection <String > requiredSelection = getRequiredSelection (sort , returnedType );
221+ Collection <String > requiredSelection = null ;
222+ if (returnedType .getReturnedType ().getPackageName ().startsWith ("java.util" )
223+ || returnedType .getReturnedType ().getPackageName ().startsWith ("jakarta.persistence" )) {
224+ requiredSelection = metamodel .managedType (returnedType .getDomainType ()).getAttributes ().stream ()
225+ .map (Attribute ::getName ).collect (Collectors .toList ());
226+ } else {
227+ requiredSelection = getRequiredSelection (sort , returnedType );
228+ }
214229
215230 List <PathAndOrigin > paths = new ArrayList <>(requiredSelection .size ());
216231 for (String selection : requiredSelection ) {
217- paths .add (
218- JpqlUtils . toExpressionRecursively ( entity , from , PropertyPath .from (selection , from . getJavaType ()), true ));
232+ paths .add (JpqlUtils . toExpressionRecursively ( metamodel , entity , entityType ,
233+ PropertyPath .from (selection , returnedType . getDomainType ()), true ));
219234 }
220235
221236 if (useTupleQuery ()) {
@@ -231,14 +246,14 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
231246 if (entityType .hasSingleIdAttribute ()) {
232247
233248 SingularAttribute <?, ?> id = entityType .getId (entityType .getIdType ().getJavaType ());
234- return selectStep .select (
235- JpqlUtils . toExpressionRecursively ( entity , from , PropertyPath .from (id .getName (), from . getJavaType ()), true ));
249+ return selectStep .select (JpqlUtils . toExpressionRecursively ( metamodel , entity , entityType ,
250+ PropertyPath .from (id .getName (), returnedType . getDomainType ()), true ));
236251
237252 } else {
238253
239254 List <PathAndOrigin > paths = entityType .getIdClassAttributes ().stream ()//
240- .map (it -> JpqlUtils .toExpressionRecursively (entity , from ,
241- PropertyPath .from (it .getName (), from . getJavaType ()), true ))
255+ .map (it -> JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
256+ PropertyPath .from (it .getName (), returnedType . getDomainType ()), true ))
242257 .toList ();
243258 return selectStep .select (paths );
244259 }
@@ -255,12 +270,12 @@ Collection<String> getRequiredSelection(Sort sort, ReturnedType returnedType) {
255270 return returnedType .getInputProperties ();
256271 }
257272
258- String render (ParameterBinding binding ) {
259- return render (binding .getRequiredPosition ());
273+ JpqlQueryBuilder . Expression placeholder (ParameterBinding binding ) {
274+ return placeholder (binding .getRequiredPosition ());
260275 }
261276
262- String render (int position ) {
263- return "?" + position ;
277+ JpqlQueryBuilder . Expression placeholder (int position ) {
278+ return JpqlQueryBuilder . parameter ( ParameterPlaceholder . indexed ( position )) ;
264279 }
265280
266281 /**
@@ -305,33 +320,33 @@ public JpqlQueryBuilder.Predicate build() {
305320 PropertyPath property = part .getProperty ();
306321 Type type = part .getType ();
307322
308- PathAndOrigin pas = JpqlUtils .toExpressionRecursively (entity , from , property );
323+ PathAndOrigin pas = JpqlUtils .toExpressionRecursively (metamodel , entity , entityType , property );
309324 JpqlQueryBuilder .WhereStep where = JpqlQueryBuilder .where (pas );
310325 JpqlQueryBuilder .WhereStep whereIgnoreCase = JpqlQueryBuilder .where (potentiallyIgnoreCase (pas ));
311326
312327 switch (type ) {
313328 case BETWEEN :
314329 PartTreeParameterBinding first = provider .next (part );
315330 ParameterBinding second = provider .next (part );
316- return where .between (render (first ), render (second ));
331+ return where .between (placeholder (first ), placeholder (second ));
317332 case AFTER :
318333 case GREATER_THAN :
319- return where .gt (render (provider .next (part )));
334+ return where .gt (placeholder (provider .next (part )));
320335 case GREATER_THAN_EQUAL :
321- return where .gte (render (provider .next (part )));
336+ return where .gte (placeholder (provider .next (part )));
322337 case BEFORE :
323338 case LESS_THAN :
324- return where .lt (render (provider .next (part )));
339+ return where .lt (placeholder (provider .next (part )));
325340 case LESS_THAN_EQUAL :
326- return where .lte (render (provider .next (part )));
341+ return where .lte (placeholder (provider .next (part )));
327342 case IS_NULL :
328343 return where .isNull ();
329344 case IS_NOT_NULL :
330345 return where .isNotNull ();
331346 case NOT_IN :
332- return whereIgnoreCase .notIn (render (provider .next (part , Collection .class )));
347+ return whereIgnoreCase .notIn (placeholder (provider .next (part , Collection .class )));
333348 case IN :
334- return whereIgnoreCase .in (render (provider .next (part , Collection .class )));
349+ return whereIgnoreCase .in (placeholder (provider .next (part , Collection .class )));
335350 case STARTING_WITH :
336351 case ENDING_WITH :
337352 case CONTAINING :
@@ -340,16 +355,16 @@ public JpqlQueryBuilder.Predicate build() {
340355 if (property .getLeafProperty ().isCollection ()) {
341356 where = JpqlQueryBuilder .where (entity , property );
342357
343- return type .equals (NOT_CONTAINING ) ? where .notMemberOf (render (provider .next (part )))
344- : where .memberOf (render (provider .next (part )));
358+ return type .equals (NOT_CONTAINING ) ? where .notMemberOf (placeholder (provider .next (part )))
359+ : where .memberOf (placeholder (provider .next (part )));
345360 }
346361
347362 case LIKE :
348363 case NOT_LIKE :
349364
350365 PartTreeParameterBinding parameter = provider .next (part , String .class );
351366 JpqlQueryBuilder .Expression parameterExpression = potentiallyIgnoreCase (part .getProperty (),
352- JpqlQueryBuilder . parameter ( render ( parameter ) ));
367+ placeholder ( parameter ));
353368 // Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
354369 String escapeChar = Character .toString (escape .getEscapeCharacter ());
355370 return
@@ -362,23 +377,16 @@ public JpqlQueryBuilder.Predicate build() {
362377 case FALSE :
363378 return where .isFalse ();
364379 case SIMPLE_PROPERTY :
365- PartTreeParameterBinding simple = provider .next (part );
366-
367- if (simple .isIsNullParameter ()) {
368- return where .isNull ();
369- }
370-
371- return whereIgnoreCase .eq (potentiallyIgnoreCase (property , JpqlQueryBuilder .expression (render (simple ))));
372380 case NEGATING_SIMPLE_PROPERTY :
373381
374- PartTreeParameterBinding negating = provider .next (part );
382+ PartTreeParameterBinding simple = provider .next (part );
375383
376- if (negating .isIsNullParameter ()) {
377- return where .isNotNull ();
384+ if (simple .isIsNullParameter ()) {
385+ return type . equals ( SIMPLE_PROPERTY ) ? where . isNull () : where .isNotNull ();
378386 }
379387
380- return whereIgnoreCase
381- . neq ( potentiallyIgnoreCase ( property , JpqlQueryBuilder . expression ( render ( negating ))) );
388+ JpqlQueryBuilder . Expression expression = potentiallyIgnoreCase ( property , placeholder ( metadata ));
389+ return type . equals ( SIMPLE_PROPERTY ) ? whereIgnoreCase . eq ( expression ) : whereIgnoreCase . neq ( expression );
382390 case IS_EMPTY :
383391 case IS_NOT_EMPTY :
384392
@@ -412,8 +420,8 @@ private <T> JpqlQueryBuilder.Expression potentiallyIgnoreCase(JpqlQueryBuilder.O
412420 * @param path must not be {@literal null}.
413421 * @return
414422 */
415- private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (PathAndOrigin pas ) {
416- return potentiallyIgnoreCase (pas .path (), JpqlQueryBuilder .expression (pas ));
423+ private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (PathAndOrigin path ) {
424+ return potentiallyIgnoreCase (path .path (), JpqlQueryBuilder .expression (path ));
417425 }
418426
419427 /**
0 commit comments