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
*/
60
68
class JpaQueryCreator extends AbstractQueryCreator <String , JpqlQueryBuilder .Predicate > implements JpqlQueryCreator {
61
69
@@ -65,8 +73,8 @@ class JpaQueryCreator extends AbstractQueryCreator<String, JpqlQueryBuilder.Pred
65
73
private final PartTree tree ;
66
74
private final EscapeCharacter escape ;
67
75
private final EntityType <?> entityType ;
68
- private final From <?, ?> from ;
69
76
private final JpqlQueryBuilder .Entity entity ;
77
+ private final Metamodel metamodel ;
70
78
71
79
/**
72
80
* Create a new {@link JpaQueryCreator}.
@@ -87,12 +95,12 @@ public JpaQueryCreator(PartTree tree, ReturnedType type, ParameterMetadataProvid
87
95
this .templates = templates ;
88
96
this .escape = provider .getEscape ();
89
97
this .entityType = em .getMetamodel ().entity (type .getDomainType ());
90
- this .from = em .getCriteriaBuilder ().createQuery ().from (type .getDomainType ());
91
98
this .entity = JpqlQueryBuilder .entity (returnedType .getDomainType ());
99
+ this .metamodel = em .getMetamodel ();
92
100
}
93
101
94
- From <?, ?> getFrom () {
95
- return from ;
102
+ Bindable < ?> getFrom () {
103
+ return entityType ;
96
104
}
97
105
98
106
JpqlQueryBuilder .Entity getEntity () {
@@ -174,7 +182,7 @@ protected JpqlQueryBuilder.Select buildQuery(Sort sort) {
174
182
QueryUtils .checkSortExpression (order );
175
183
176
184
try {
177
- expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (entity , from ,
185
+ expression = JpqlQueryBuilder .expression (JpqlUtils .toExpressionRecursively (metamodel , entity , entityType ,
178
186
PropertyPath .from (order .getProperty (), entityType .getJavaType ())));
179
187
} catch (PropertyReferenceException e ) {
180
188
@@ -209,12 +217,19 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
209
217
210
218
if (returnedType .needsCustomConstruction ()) {
211
219
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
+ }
213
228
214
229
List <PathAndOrigin > paths = new ArrayList <>(requiredSelection .size ());
215
230
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 ));
218
233
}
219
234
220
235
if (useTupleQuery ()) {
@@ -230,14 +245,14 @@ private JpqlQueryBuilder.Select doSelect(Sort sort) {
230
245
if (entityType .hasSingleIdAttribute ()) {
231
246
232
247
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 ));
235
250
236
251
} else {
237
252
238
253
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 ))
241
256
.toList ();
242
257
return selectStep .select (paths );
243
258
}
@@ -254,12 +269,12 @@ Collection<String> getRequiredSelection(Sort sort, ReturnedType returnedType) {
254
269
return returnedType .getInputProperties ();
255
270
}
256
271
257
- String render (ParameterBinding binding ) {
258
- return render (binding .getRequiredPosition ());
272
+ JpqlQueryBuilder . Expression placeholder (ParameterBinding binding ) {
273
+ return placeholder (binding .getRequiredPosition ());
259
274
}
260
275
261
- String render (int position ) {
262
- return "?" + position ;
276
+ JpqlQueryBuilder . Expression placeholder (int position ) {
277
+ return JpqlQueryBuilder . parameter ( ParameterPlaceholder . indexed ( position )) ;
263
278
}
264
279
265
280
/**
@@ -304,33 +319,33 @@ public JpqlQueryBuilder.Predicate build() {
304
319
PropertyPath property = part .getProperty ();
305
320
Type type = part .getType ();
306
321
307
- PathAndOrigin pas = JpqlUtils .toExpressionRecursively (entity , from , property );
322
+ PathAndOrigin pas = JpqlUtils .toExpressionRecursively (metamodel , entity , entityType , property );
308
323
JpqlQueryBuilder .WhereStep where = JpqlQueryBuilder .where (pas );
309
324
JpqlQueryBuilder .WhereStep whereIgnoreCase = JpqlQueryBuilder .where (potentiallyIgnoreCase (pas ));
310
325
311
326
switch (type ) {
312
327
case BETWEEN :
313
328
PartTreeParameterBinding first = provider .next (part );
314
329
ParameterBinding second = provider .next (part );
315
- return where .between (render (first ), render (second ));
330
+ return where .between (placeholder (first ), placeholder (second ));
316
331
case AFTER :
317
332
case GREATER_THAN :
318
- return where .gt (render (provider .next (part )));
333
+ return where .gt (placeholder (provider .next (part )));
319
334
case GREATER_THAN_EQUAL :
320
- return where .gte (render (provider .next (part )));
335
+ return where .gte (placeholder (provider .next (part )));
321
336
case BEFORE :
322
337
case LESS_THAN :
323
- return where .lt (render (provider .next (part )));
338
+ return where .lt (placeholder (provider .next (part )));
324
339
case LESS_THAN_EQUAL :
325
- return where .lte (render (provider .next (part )));
340
+ return where .lte (placeholder (provider .next (part )));
326
341
case IS_NULL :
327
342
return where .isNull ();
328
343
case IS_NOT_NULL :
329
344
return where .isNotNull ();
330
345
case NOT_IN :
331
- return whereIgnoreCase .notIn (render (provider .next (part , Collection .class )));
346
+ return whereIgnoreCase .notIn (placeholder (provider .next (part , Collection .class )));
332
347
case IN :
333
- return whereIgnoreCase .in (render (provider .next (part , Collection .class )));
348
+ return whereIgnoreCase .in (placeholder (provider .next (part , Collection .class )));
334
349
case STARTING_WITH :
335
350
case ENDING_WITH :
336
351
case CONTAINING :
@@ -339,16 +354,16 @@ public JpqlQueryBuilder.Predicate build() {
339
354
if (property .getLeafProperty ().isCollection ()) {
340
355
where = JpqlQueryBuilder .where (entity , property );
341
356
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 )));
344
359
}
345
360
346
361
case LIKE :
347
362
case NOT_LIKE :
348
363
349
364
PartTreeParameterBinding parameter = provider .next (part , String .class );
350
365
JpqlQueryBuilder .Expression parameterExpression = potentiallyIgnoreCase (part .getProperty (),
351
- JpqlQueryBuilder . parameter ( render ( parameter ) ));
366
+ placeholder ( parameter ));
352
367
// Predicate like = builder.like(propertyExpression, parameterExpression, escape.getEscapeCharacter());
353
368
String escapeChar = Character .toString (escape .getEscapeCharacter ());
354
369
return
@@ -361,16 +376,16 @@ public JpqlQueryBuilder.Predicate build() {
361
376
case FALSE :
362
377
return where .isFalse ();
363
378
case SIMPLE_PROPERTY :
379
+ case NEGATING_SIMPLE_PROPERTY :
380
+
364
381
PartTreeParameterBinding metadata = provider .next (part );
365
382
366
383
if (metadata .isIsNullParameter ()) {
367
- return where .isNull ();
384
+ return type . equals ( SIMPLE_PROPERTY ) ? where .isNull () : where . isNotNull ();
368
385
}
369
386
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 );
374
389
case IS_EMPTY :
375
390
case IS_NOT_EMPTY :
376
391
@@ -404,8 +419,8 @@ private <T> JpqlQueryBuilder.Expression potentiallyIgnoreCase(JpqlQueryBuilder.O
404
419
* @param path must not be {@literal null}.
405
420
* @return
406
421
*/
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 ));
409
424
}
410
425
411
426
/**
0 commit comments