3535import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
3636import org .springframework .data .relational .core .query .CriteriaDefinition ;
3737import org .springframework .data .relational .core .query .CriteriaDefinition .Comparator ;
38+ import org .springframework .data .relational .core .query .QueryExpression ;
3839import org .springframework .data .relational .core .query .ValueFunction ;
3940import org .springframework .data .relational .core .sql .*;
4041import org .springframework .data .relational .domain .SqlSort ;
4849import org .springframework .r2dbc .core .binding .MutableBindings ;
4950import org .springframework .util .Assert ;
5051import org .springframework .util .ClassUtils ;
52+ import org .springframework .util .StringUtils ;
5153
5254/**
5355 * Maps {@link CriteriaDefinition} and {@link Sort} objects considering mapping metadata and dialect-specific
@@ -296,31 +298,58 @@ private Condition combine(CriteriaDefinition criteria, @Nullable Condition curre
296298 private Condition mapCondition (CriteriaDefinition criteria , MutableBindings bindings , Table table ,
297299 @ Nullable RelationalPersistentEntity <?> entity ) {
298300
299- Field propertyField = createPropertyField (entity , criteria .getColumn (), this .mappingContext );
300- Column column = table .column (propertyField .getMappedColumnName ());
301- TypeInformation <?> actualType = propertyField .getTypeHint ().getRequiredActualType ();
302-
303301 Object mappedValue ;
304- Class <?> typeHint ;
302+ TypeInformation <?> typeHint ;
303+ Class <?> targetType ;
304+ Expression expression ;
305+ String nameHint ;
306+
307+ if (criteria .hasExpression ()) {
308+
309+ R2dbcEvaluationContext context = new R2dbcEvaluationContext (table , converter , entity , bindings );
310+ QueryExpression queryExpression = criteria .getExpression ();
311+ typeHint = queryExpression .getType (context ).getTargetType ();
312+
313+ expression = queryExpression .evaluate (context );
314+ nameHint = queryExpression .getNameHint ();
315+
316+ if (criteria .getComparator () == null ) {
317+ return Conditions .from (expression );
318+ }
319+
320+ } else if (criteria .hasColumn ()) {
321+
322+ Field propertyField = createPropertyField (entity , criteria .getColumn (), this .mappingContext );
323+ Column column = table .column (propertyField .getMappedColumnName ());
324+ expression = column ;
325+ nameHint = column .getName ().getReference ();
326+ typeHint = propertyField .getTypeHint ();
327+
328+ } else {
329+ throw new IllegalStateException ("Cannot map empty Criteria" );
330+ }
331+
332+ TypeInformation <?> actualType = typeHint .getRequiredActualType ();
305333
306334 Comparator comparator = criteria .getComparator ();
335+
307336 if (criteria .getValue () instanceof Parameter parameter ) {
308337
309- mappedValue = convertValue (comparator , parameter .getValue (), propertyField . getTypeHint () );
310- typeHint = getTypeHint (mappedValue , actualType .getType (), parameter );
338+ mappedValue = convertValue (comparator , parameter .getValue (), typeHint );
339+ targetType = getTypeHint (mappedValue , actualType .getType (), parameter );
311340 } else if (criteria .getValue () instanceof ValueFunction <?> valueFunction ) {
312341
313- mappedValue = valueFunction .map (v -> convertValue (comparator , v , propertyField .getTypeHint ()))
314- .apply (getEscaper (comparator ));
342+ mappedValue = valueFunction .map (v -> convertValue (comparator , v , typeHint )).apply (getEscaper (comparator ));
315343
316- typeHint = actualType .getType ();
344+ targetType = actualType .getType ();
317345 } else {
318346
319- mappedValue = convertValue (comparator , criteria .getValue (), propertyField . getTypeHint () );
320- typeHint = actualType .getType ();
347+ mappedValue = convertValue (comparator , criteria .getValue (), typeHint );
348+ targetType = actualType .getType ();
321349 }
322350
323- return createCondition (column , mappedValue , typeHint , bindings , comparator , criteria .isIgnoreCase ());
351+ return createCondition (expression , nameHint , mappedValue , targetType , bindings , comparator ,
352+ criteria .isIgnoreCase ());
324353 }
325354
326355 private Escaper getEscaper (Comparator comparator ) {
@@ -393,32 +422,32 @@ protected MappingContext<? extends RelationalPersistentEntity<?>, RelationalPers
393422 return this .mappingContext ;
394423 }
395424
396- private Condition createCondition (Column column , @ Nullable Object mappedValue , Class <?> valueType ,
397- MutableBindings bindings , Comparator comparator , boolean ignoreCase ) {
425+ private Condition createCondition (Expression source , String nameHint , @ Nullable Object mappedValue ,
426+ Class <?> valueType , MutableBindings bindings , Comparator comparator , boolean ignoreCase ) {
398427
399428 if (comparator .equals (Comparator .IS_NULL )) {
400- return column .isNull ();
429+ return Conditions .isNull (source );
401430 }
402431
403432 if (comparator .equals (Comparator .IS_NOT_NULL )) {
404- return column . isNotNull ();
433+ return Conditions . isNull ( source ). not ();
405434 }
406435
407436 if (comparator == Comparator .IS_TRUE ) {
408- Expression bind = booleanBind (column , mappedValue , valueType , bindings , ignoreCase );
409437
410- return column .isEqualTo (bind );
438+ Expression bind = booleanBind (nameHint , mappedValue , valueType , bindings , ignoreCase );
439+ return Conditions .isEqual (source , bind );
411440 }
412441
413442 if (comparator == Comparator .IS_FALSE ) {
414- Expression bind = booleanBind (column , mappedValue , valueType , bindings , ignoreCase );
415443
416- return column .isEqualTo (bind );
444+ Expression bind = booleanBind (nameHint , mappedValue , valueType , bindings , ignoreCase );
445+ return Conditions .isEqual (source , bind );
417446 }
418447
419- Expression columnExpression = column ;
448+ Expression columnExpression = source ;
420449 if (ignoreCase ) {
421- columnExpression = Functions .upper (column );
450+ columnExpression = Functions .upper (source );
422451 }
423452
424453 if (comparator == Comparator .NOT_IN || comparator == Comparator .IN ) {
@@ -432,15 +461,16 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, C
432461
433462 for (Object o : (Iterable <?>) mappedValue ) {
434463
435- BindMarker bindMarker = bindings . nextMarker ( column . getName (). getReference () );
464+ BindMarker bindMarker = bindMarker ( nameHint , bindings );
436465 expressions .add (bind (o , valueType , bindings , bindMarker ));
437466 }
438467
439468 condition = Conditions .in (columnExpression , expressions .toArray (new Expression [0 ]));
440469
441470 } else {
442471
443- BindMarker bindMarker = bindings .nextMarker (column .getName ().getReference ());
472+ BindMarker bindMarker = bindMarker (nameHint , bindings );
473+ ;
444474 Expression expression = bind (mappedValue , valueType , bindings , bindMarker );
445475
446476 condition = Conditions .in (columnExpression , expression );
@@ -457,53 +487,51 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, C
457487
458488 Pair <Object , Object > pair = (Pair <Object , Object >) mappedValue ;
459489
460- Expression begin = bind (pair .getFirst (), valueType , bindings ,
461- bindings .nextMarker (column .getName ().getReference ()), ignoreCase );
462- Expression end = bind (pair .getSecond (), valueType , bindings , bindings .nextMarker (column .getName ().getReference ()),
463- ignoreCase );
490+ Expression begin = bind (pair .getFirst (), valueType , bindings , bindMarker (nameHint , bindings ), ignoreCase );
491+ Expression end = bind (pair .getSecond (), valueType , bindings , bindMarker (nameHint , bindings ), ignoreCase );
464492
465493 return comparator == Comparator .BETWEEN ? Conditions .between (columnExpression , begin , end )
466494 : Conditions .notBetween (columnExpression , begin , end );
467495 }
468496
469- BindMarker bindMarker = bindings .nextMarker (column .getName ().getReference ());
497+ BindMarker bindMarker = bindMarker (nameHint , bindings );
498+ ;
470499
471- switch (comparator ) {
472- case EQ : {
500+ return switch (comparator ) {
501+ case EQ -> {
473502 Expression expression = bind (mappedValue , valueType , bindings , bindMarker , ignoreCase );
474- return Conditions .isEqual (columnExpression , expression );
503+ yield Conditions .isEqual (columnExpression , expression );
475504 }
476- case NEQ : {
505+ case NEQ -> {
477506 Expression expression = bind (mappedValue , valueType , bindings , bindMarker , ignoreCase );
478- return Conditions .isEqual (columnExpression , expression ).not ();
507+ yield Conditions .isEqual (columnExpression , expression ).not ();
479508 }
480- case LT : {
509+ case LT -> {
481510 Expression expression = bind (mappedValue , valueType , bindings , bindMarker );
482- return column .isLess (expression );
511+ yield Conditions .isLess (source , expression );
483512 }
484- case LTE : {
513+ case LTE -> {
485514 Expression expression = bind (mappedValue , valueType , bindings , bindMarker );
486- return column .isLessOrEqualTo (expression );
515+ yield Conditions .isLessOrEqualTo (source , expression );
487516 }
488- case GT : {
517+ case GT -> {
489518 Expression expression = bind (mappedValue , valueType , bindings , bindMarker );
490- return column .isGreater (expression );
519+ yield Conditions .isGreater (source , expression );
491520 }
492- case GTE : {
521+ case GTE -> {
493522 Expression expression = bind (mappedValue , valueType , bindings , bindMarker );
494- return column .isGreaterOrEqualTo (expression );
523+ yield Conditions .isGreaterOrEqualTo (source , expression );
495524 }
496- case LIKE : {
525+ case LIKE -> {
497526 Expression expression = bind (mappedValue , valueType , bindings , bindMarker , ignoreCase );
498- return Conditions .like (columnExpression , expression );
527+ yield Conditions .like (columnExpression , expression );
499528 }
500- case NOT_LIKE : {
529+ case NOT_LIKE -> {
501530 Expression expression = bind (mappedValue , valueType , bindings , bindMarker , ignoreCase );
502- return Conditions .notLike (columnExpression , expression );
531+ yield Conditions .notLike (columnExpression , expression );
503532 }
504- default :
505- throw new UnsupportedOperationException ("Comparator " + comparator + " not supported" );
506- }
533+ default -> throw new UnsupportedOperationException ("Comparator " + comparator + " not supported" );
534+ };
507535 }
508536
509537 Field createPropertyField (@ Nullable RelationalPersistentEntity <?> entity , SqlIdentifier key ) {
@@ -515,10 +543,6 @@ Field createPropertyField(@Nullable RelationalPersistentEntity<?> entity, SqlIde
515543 return entity == null ? new Field (key ) : new MetadataBackedField (key , entity , mappingContext );
516544 }
517545
518- Class <?> getTypeHint (@ Nullable Object mappedValue , Class <?> propertyType ) {
519- return propertyType ;
520- }
521-
522546 Class <?> getTypeHint (@ Nullable Object mappedValue , Class <?> propertyType , Parameter parameter ) {
523547
524548 if (mappedValue == null || propertyType .equals (Object .class )) {
@@ -550,13 +574,18 @@ private Expression bind(@Nullable Object mappedValue, Class<?> valueType, Mutabl
550574 : SQL .bindMarker (bindMarker .getPlaceholder ());
551575 }
552576
553- private Expression booleanBind (Column column , Object mappedValue , Class <?> valueType , MutableBindings bindings ,
554- boolean ignoreCase ) {
555- BindMarker bindMarker = bindings .nextMarker (column .getName ().getReference ());
577+ private Expression booleanBind (@ Nullable String nameHint , Object mappedValue , Class <?> valueType ,
578+ MutableBindings bindings , boolean ignoreCase ) {
579+
580+ BindMarker bindMarker = bindMarker (nameHint , bindings );
556581
557582 return bind (mappedValue , valueType , bindings , bindMarker , ignoreCase );
558583 }
559584
585+ private static BindMarker bindMarker (@ Nullable String nameHint , MutableBindings bindings ) {
586+ return StringUtils .hasText (nameHint ) ? bindings .nextMarker (nameHint ) : bindings .nextMarker ();
587+ }
588+
560589 /**
561590 * Value object to represent a field and its meta-information.
562591 */
0 commit comments