15
15
*/
16
16
package org .springframework .data .jpa .repository .query ;
17
17
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 ;
19
22
20
23
import jakarta .persistence .EntityManager ;
21
24
import jakarta .persistence .criteria .CriteriaQuery ;
22
25
import jakarta .persistence .criteria .Expression ;
23
- import jakarta .persistence .criteria .From ;
24
26
import jakarta .persistence .criteria .Predicate ;
27
+ import jakarta .persistence .metamodel .Attribute ;
28
+ import jakarta .persistence .metamodel .Bindable ;
25
29
import jakarta .persistence .metamodel .EntityType ;
30
+ import jakarta .persistence .metamodel .Metamodel ;
26
31
import jakarta .persistence .metamodel .SingularAttribute ;
27
32
28
33
import java .util .ArrayList ;
29
34
import java .util .Collection ;
30
35
import java .util .Iterator ;
31
36
import java .util .List ;
37
+ import java .util .stream .Collectors ;
32
38
33
39
import org .springframework .data .domain .Sort ;
34
40
import org .springframework .data .jpa .domain .JpaSort ;
41
+ import org .springframework .data .jpa .repository .query .JpqlQueryBuilder .ParameterPlaceholder ;
35
42
import org .springframework .data .jpa .repository .query .JpqlQueryBuilder .PathAndOrigin ;
36
43
import org .springframework .data .jpa .repository .query .ParameterBinding .PartTreeParameterBinding ;
37
44
import org .springframework .data .jpa .repository .support .JpqlQueryTemplates ;
56
63
* @author Moritz Becker
57
64
* @author Andrey Kovalev
58
65
* @author Greg Turnquist
66
+ * @author Christoph Strobl
59
67
* @author Jinmyeong Kim
60
68
*/
61
69
class JpaQueryCreator extends AbstractQueryCreator <String , JpqlQueryBuilder .Predicate > implements JpqlQueryCreator {
@@ -66,8 +74,8 @@ class JpaQueryCreator extends AbstractQueryCreator<String, JpqlQueryBuilder.Pred
66
74
private final PartTree tree ;
67
75
private final EscapeCharacter escape ;
68
76
private final EntityType <?> entityType ;
69
- private final From <?, ?> from ;
70
77
private final JpqlQueryBuilder .Entity entity ;
78
+ private final Metamodel metamodel ;
71
79
72
80
/**
73
81
* Create a new {@link JpaQueryCreator}.
@@ -88,12 +96,12 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvid
88
96
this .templates = templates ;
89
97
this .escape = provider .getEscape ();
90
98
this .entityType = em .getMetamodel ().entity (type .getDomainType ());
91
- this .from = em .getCriteriaBuilder ().createQuery ().from (type .getDomainType ());
92
99
this .entity = JpqlQueryBuilder .entity (returnedType .getDomainType ());
100
+ this .metamodel = em .getMetamodel ();
93
101
}
94
102
95
- From <?, ?> getFrom () {
96
- return from ;
103
+ Bindable < ?> getFrom () {
104
+ return entityType ;
97
105
}
98
106
99
107
JpqlQueryBuilder .Entity getEntity () {
@@ -175,7 +183,7 @@ protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
175
183
QueryUtils .checkSortExpression (order );
176
184
177
185
try {
178
- expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (entity , from ,
186
+ expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
179
187
PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
180
188
} catch (PropertyReferenceException e ) {
181
189
@@ -210,12 +218,19 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
210
218
211
219
if (returnedType .needsCustomConstruction ()) {
212
220
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
+ }
214
229
215
230
List <PathAndOrigin > paths = new ArrayList <>(requiredSelection .size ());
216
231
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 ));
219
234
}
220
235
221
236
if (useTupleQuery ()) {
@@ -231,14 +246,14 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
231
246
if (entityType .hasSingleIdAttribute ()) {
232
247
233
248
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 ));
236
251
237
252
} else {
238
253
239
254
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 ))
242
257
.toList ();
243
258
return selectStep .select (paths );
244
259
}
@@ -255,12 +270,12 @@ Collection<String> getRequiredSelection(Sort sort, ReturnedType returnedType) {
255
270
return returnedType .getInputProperties ();
256
271
}
257
272
258
- String render (ParameterBinding binding ) {
259
- return render (binding .getRequiredPosition ());
273
+ JpqlQueryBuilder . Expression placeholder (ParameterBinding binding ) {
274
+ return placeholder (binding .getRequiredPosition ());
260
275
}
261
276
262
- String render (int position ) {
263
- return "?" + position ;
277
+ JpqlQueryBuilder . Expression placeholder (int position ) {
278
+ return JpqlQueryBuilder . parameter ( ParameterPlaceholder . indexed ( position )) ;
264
279
}
265
280
266
281
/**
@@ -305,33 +320,33 @@ public JpqlQueryBuilder.Predicate build() {
305
320
PropertyPath property = part .getProperty ();
306
321
Type type = part .getType ();
307
322
308
- PathAndOrigin pas = JpqlUtils .toExpressionRecursively (entity , from , property );
323
+ PathAndOrigin pas = JpqlUtils .toExpressionRecursively (metamodel , entity , entityType , property );
309
324
JpqlQueryBuilder .WhereStep where = JpqlQueryBuilder .where (pas );
310
325
JpqlQueryBuilder .WhereStep whereIgnoreCase = JpqlQueryBuilder .where (potentiallyIgnoreCase (pas ));
311
326
312
327
switch (type ) {
313
328
case BETWEEN :
314
329
PartTreeParameterBinding first = provider .next (part );
315
330
ParameterBinding second = provider .next (part );
316
- return where .between (render (first ), render (second ));
331
+ return where .between (placeholder (first ), placeholder (second ));
317
332
case AFTER :
318
333
case GREATER_THAN :
319
- return where .gt (render (provider .next (part )));
334
+ return where .gt (placeholder (provider .next (part )));
320
335
case GREATER_THAN_EQUAL :
321
- return where .gte (render (provider .next (part )));
336
+ return where .gte (placeholder (provider .next (part )));
322
337
case BEFORE :
323
338
case LESS_THAN :
324
- return where .lt (render (provider .next (part )));
339
+ return where .lt (placeholder (provider .next (part )));
325
340
case LESS_THAN_EQUAL :
326
- return where .lte (render (provider .next (part )));
341
+ return where .lte (placeholder (provider .next (part )));
327
342
case IS_NULL :
328
343
return where .isNull ();
329
344
case IS_NOT_NULL :
330
345
return where .isNotNull ();
331
346
case NOT_IN :
332
- return whereIgnoreCase .notIn (render (provider .next (part , Collection .class )));
347
+ return whereIgnoreCase .notIn (placeholder (provider .next (part , Collection .class )));
333
348
case IN :
334
- return whereIgnoreCase .in (render (provider .next (part , Collection .class )));
349
+ return whereIgnoreCase .in (placeholder (provider .next (part , Collection .class )));
335
350
case STARTING_WITH :
336
351
case ENDING_WITH :
337
352
case CONTAINING :
@@ -340,16 +355,16 @@ public JpqlQueryBuilder.Predicate build() {
340
355
if (property .getLeafProperty ().isCollection ()) {
341
356
where = JpqlQueryBuilder .where (entity , property );
342
357
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 )));
345
360
}
346
361
347
362
case LIKE :
348
363
case NOT_LIKE :
349
364
350
365
PartTreeParameterBinding parameter = provider .next (part , String .class );
351
366
JpqlQueryBuilder .Expression parameterExpression = potentiallyIgnoreCase (part .getProperty (),
352
- JpqlQueryBuilder . parameter ( render ( parameter ) ));
367
+ placeholder ( parameter ));
353
368
// Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
354
369
String escapeChar = Character .toString (escape .getEscapeCharacter ());
355
370
return
@@ -362,23 +377,16 @@ public JpqlQueryBuilder.Predicate build() {
362
377
case FALSE :
363
378
return where .isFalse ();
364
379
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 ))));
372
380
case NEGATING_SIMPLE_PROPERTY :
373
381
374
- PartTreeParameterBinding negating = provider .next (part );
382
+ PartTreeParameterBinding simple = provider .next (part );
375
383
376
- if (negating .isIsNullParameter ()) {
377
- return where .isNotNull ();
384
+ if (simple .isIsNullParameter ()) {
385
+ return type . equals ( SIMPLE_PROPERTY ) ? where . isNull () : where .isNotNull ();
378
386
}
379
387
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 );
382
390
case IS_EMPTY :
383
391
case IS_NOT_EMPTY :
384
392
@@ -412,8 +420,8 @@ private <T> JpqlQueryBuilder.Expression potentiallyIgnoreCase(JpqlQueryBuilder.O
412
420
* @param path must not be {@literal null}.
413
421
* @return
414
422
*/
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 ));
417
425
}
418
426
419
427
/**
0 commit comments