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 */
6068class JpaQueryCreator extends AbstractQueryCreator <String , JpqlQueryBuilder .Predicate > implements JpqlQueryCreator {
6169
@@ -65,8 +73,8 @@ class JpaQueryCreator extends AbstractQueryCreator<String, JpqlQueryBuilder.Pred
6573 private final PartTree tree ;
6674 private final EscapeCharacter escape ;
6775 private final EntityType <?> entityType ;
68- private final From <?, ?> from ;
6976 private final JpqlQueryBuilder .Entity entity ;
77+ private final Metamodel metamodel ;
7078
7179 /**
7280 * Create a new {@link JpaQueryCreator}.
@@ -87,12 +95,12 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvid
8795 this .templates = templates ;
8896 this .escape = provider .getEscape ();
8997 this .entityType = em .getMetamodel ().entity (type .getDomainType ());
90- this .from = em .getCriteriaBuilder ().createQuery ().from (type .getDomainType ());
9198 this .entity = JpqlQueryBuilder .entity (returnedType .getDomainType ());
99+ this .metamodel = em .getMetamodel ();
92100 }
93101
94- From <?, ?> getFrom () {
95- return from ;
102+ Bindable < ?> getFrom () {
103+ return entityType ;
96104 }
97105
98106 JpqlQueryBuilder .Entity getEntity () {
@@ -174,7 +182,7 @@ protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
174182 QueryUtils .checkSortExpression (order );
175183
176184 try {
177- expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (entity , from ,
185+ expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
178186 PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
179187 } catch (PropertyReferenceException e ) {
180188
@@ -209,12 +217,19 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
209217
210218 if (returnedType .needsCustomConstruction ()) {
211219
212- Collection <String > requiredSelection = getRequiredSelection (sort , returnedType );
220+ Collection <String > requiredSelection = null ;
221+ if (returnedType .getReturnedType ().getPackageName ().startsWith ("java.util" )
222+ || returnedType .getReturnedType ().getPackageName ().startsWith ("jakarta.persistence" )) {
223+ requiredSelection = metamodel .managedType (returnedType .getDomainType ()).getAttributes ().stream ()
224+ .map (Attribute ::getName ).collect (Collectors .toList ());
225+ } else {
226+ requiredSelection = getRequiredSelection (sort , returnedType );
227+ }
213228
214229 List <PathAndOrigin > paths = new ArrayList <>(requiredSelection .size ());
215230 for (String selection : requiredSelection ) {
216- paths .add (
217- JpqlUtils . toExpressionRecursively ( entity , from , PropertyPath .from (selection , from . getJavaType ()), true ));
231+ paths .add (JpqlUtils . toExpressionRecursively ( metamodel , entity , entityType ,
232+ PropertyPath .from (selection , returnedType . getDomainType ()), true ));
218233 }
219234
220235 if (useTupleQuery ()) {
@@ -230,14 +245,14 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
230245 if (entityType .hasSingleIdAttribute ()) {
231246
232247 SingularAttribute <?, ?> id = entityType .getId (entityType .getIdType ().getJavaType ());
233- return selectStep .select (
234- JpqlUtils . toExpressionRecursively ( entity , from , PropertyPath .from (id .getName (), from . getJavaType ()), true ));
248+ return selectStep .select (JpqlUtils . toExpressionRecursively ( metamodel , entity , entityType ,
249+ PropertyPath .from (id .getName (), returnedType . getDomainType ()), true ));
235250
236251 } else {
237252
238253 List <PathAndOrigin > paths = entityType .getIdClassAttributes ().stream ()//
239- .map (it -> JpqlUtils .toExpressionRecursively (entity , from ,
240- PropertyPath .from (it .getName (), from . getJavaType ()), true ))
254+ .map (it -> JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
255+ PropertyPath .from (it .getName (), returnedType . getDomainType ()), true ))
241256 .toList ();
242257 return selectStep .select (paths );
243258 }
@@ -254,12 +269,12 @@ Collection<String> getRequiredSelection(Sort sort, ReturnedType returnedType) {
254269 return returnedType .getInputProperties ();
255270 }
256271
257- String render (ParameterBinding binding ) {
258- return render (binding .getRequiredPosition ());
272+ JpqlQueryBuilder . Expression placeholder (ParameterBinding binding ) {
273+ return placeholder (binding .getRequiredPosition ());
259274 }
260275
261- String render (int position ) {
262- return "?" + position ;
276+ JpqlQueryBuilder . Expression placeholder (int position ) {
277+ return JpqlQueryBuilder . parameter ( ParameterPlaceholder . indexed ( position )) ;
263278 }
264279
265280 /**
@@ -304,33 +319,33 @@ public JpqlQueryBuilder.Predicate build() {
304319 PropertyPath property = part .getProperty ();
305320 Type type = part .getType ();
306321
307- PathAndOrigin pas = JpqlUtils .toExpressionRecursively (entity , from , property );
322+ PathAndOrigin pas = JpqlUtils .toExpressionRecursively (metamodel , entity , entityType , property );
308323 JpqlQueryBuilder .WhereStep where = JpqlQueryBuilder .where (pas );
309324 JpqlQueryBuilder .WhereStep whereIgnoreCase = JpqlQueryBuilder .where (potentiallyIgnoreCase (pas ));
310325
311326 switch (type ) {
312327 case BETWEEN :
313328 PartTreeParameterBinding first = provider .next (part );
314329 ParameterBinding second = provider .next (part );
315- return where .between (render (first ), render (second ));
330+ return where .between (placeholder (first ), placeholder (second ));
316331 case AFTER :
317332 case GREATER_THAN :
318- return where .gt (render (provider .next (part )));
333+ return where .gt (placeholder (provider .next (part )));
319334 case GREATER_THAN_EQUAL :
320- return where .gte (render (provider .next (part )));
335+ return where .gte (placeholder (provider .next (part )));
321336 case BEFORE :
322337 case LESS_THAN :
323- return where .lt (render (provider .next (part )));
338+ return where .lt (placeholder (provider .next (part )));
324339 case LESS_THAN_EQUAL :
325- return where .lte (render (provider .next (part )));
340+ return where .lte (placeholder (provider .next (part )));
326341 case IS_NULL :
327342 return where .isNull ();
328343 case IS_NOT_NULL :
329344 return where .isNotNull ();
330345 case NOT_IN :
331- return whereIgnoreCase .notIn (render (provider .next (part , Collection .class )));
346+ return whereIgnoreCase .notIn (placeholder (provider .next (part , Collection .class )));
332347 case IN :
333- return whereIgnoreCase .in (render (provider .next (part , Collection .class )));
348+ return whereIgnoreCase .in (placeholder (provider .next (part , Collection .class )));
334349 case STARTING_WITH :
335350 case ENDING_WITH :
336351 case CONTAINING :
@@ -339,16 +354,16 @@ public JpqlQueryBuilder.Predicate build() {
339354 if (property .getLeafProperty ().isCollection ()) {
340355 where = JpqlQueryBuilder .where (entity , property );
341356
342- return type .equals (NOT_CONTAINING ) ? where .notMemberOf (render (provider .next (part )))
343- : where .memberOf (render (provider .next (part )));
357+ return type .equals (NOT_CONTAINING ) ? where .notMemberOf (placeholder (provider .next (part )))
358+ : where .memberOf (placeholder (provider .next (part )));
344359 }
345360
346361 case LIKE :
347362 case NOT_LIKE :
348363
349364 PartTreeParameterBinding parameter = provider .next (part , String .class );
350365 JpqlQueryBuilder .Expression parameterExpression = potentiallyIgnoreCase (part .getProperty (),
351- JpqlQueryBuilder . parameter ( render ( parameter ) ));
366+ placeholder ( parameter ));
352367 // Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
353368 String escapeChar = Character .toString (escape .getEscapeCharacter ());
354369 return
@@ -361,16 +376,16 @@ public JpqlQueryBuilder.Predicate build() {
361376 case FALSE :
362377 return where .isFalse ();
363378 case SIMPLE_PROPERTY :
379+ case NEGATING_SIMPLE_PROPERTY :
380+
364381 PartTreeParameterBinding metadata = provider .next (part );
365382
366383 if (metadata .isIsNullParameter ()) {
367- return where .isNull ();
384+ return type . equals ( SIMPLE_PROPERTY ) ? where .isNull () : where . isNotNull ();
368385 }
369386
370- return whereIgnoreCase .eq (potentiallyIgnoreCase (property , JpqlQueryBuilder .expression (render (metadata ))));
371- case NEGATING_SIMPLE_PROPERTY :
372- return whereIgnoreCase
373- .neq (potentiallyIgnoreCase (property , JpqlQueryBuilder .expression (render (provider .next (part )))));
387+ JpqlQueryBuilder .Expression expression = potentiallyIgnoreCase (property , placeholder (metadata ));
388+ return type .equals (SIMPLE_PROPERTY ) ? whereIgnoreCase .eq (expression ) : whereIgnoreCase .neq (expression );
374389 case IS_EMPTY :
375390 case IS_NOT_EMPTY :
376391
@@ -404,8 +419,8 @@ private <T> JpqlQueryBuilder.Expression potentiallyIgnoreCase(JpqlQueryBuilder.O
404419 * @param path must not be {@literal null}.
405420 * @return
406421 */
407- private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (PathAndOrigin pas ) {
408- return potentiallyIgnoreCase (pas .path (), JpqlQueryBuilder .expression (pas ));
422+ private <T > JpqlQueryBuilder .Expression potentiallyIgnoreCase (PathAndOrigin path ) {
423+ return potentiallyIgnoreCase (path .path (), JpqlQueryBuilder .expression (path ));
409424 }
410425
411426 /**
0 commit comments