31
31
import org .springframework .data .r2dbc .dialect .BindMarkersFactory ;
32
32
import org .springframework .data .r2dbc .dialect .BindTarget ;
33
33
import org .springframework .util .Assert ;
34
+ import org .springframework .util .CollectionUtils ;
34
35
35
36
/**
36
37
* Helper methods for named parameter parsing.
37
38
* <p>
38
39
* Only intended for internal use within Spring's Data's R2DBC framework. Partially extracted from Spring's JDBC named
39
40
* parameter support.
40
41
* <p>
42
+ * References to the same parameter name are substituted with the same bind marker placeholder if a
43
+ * {@link BindMarkersFactory} uses {@link BindMarkersFactory#identifiablePlaceholders() identifiable} placeholders.
44
+ * <p>
41
45
* This is a subset of Spring Frameworks's {@code org.springframework.r2dbc.namedparam.NamedParameterUtils}.
42
46
*
43
47
* @author Thomas Risberg
@@ -260,7 +264,7 @@ private static int skipCommentsAndQuotes(char[] statement, int position) {
260
264
public static PreparedOperation <String > substituteNamedParameters (ParsedSql parsedSql ,
261
265
BindMarkersFactory bindMarkersFactory , BindParameterSource paramSource ) {
262
266
263
- BindMarkerHolder markerHolder = new BindMarkerHolder (bindMarkersFactory . create () );
267
+ NamedParameters markerHolder = new NamedParameters (bindMarkersFactory );
264
268
265
269
String originalSql = parsedSql .getOriginalSql ();
266
270
List <String > paramNames = parsedSql .getParameterNames ();
@@ -276,9 +280,11 @@ public static PreparedOperation<String> substituteNamedParameters(ParsedSql pars
276
280
int startIndex = indexes [0 ];
277
281
int endIndex = indexes [1 ];
278
282
actualSql .append (originalSql , lastIndex , startIndex );
283
+ NamedParameters .NamedParameter marker = markerHolder .getOrCreate (paramName );
279
284
if (paramSource .hasValue (paramName )) {
280
285
Object value = paramSource .getValue (paramName );
281
286
if (value instanceof Collection ) {
287
+
282
288
Iterator <?> entryIter = ((Collection <?>) value ).iterator ();
283
289
int k = 0 ;
284
290
while (entryIter .hasNext ()) {
@@ -294,19 +300,19 @@ public static PreparedOperation<String> substituteNamedParameters(ParsedSql pars
294
300
if (m > 0 ) {
295
301
actualSql .append (", " );
296
302
}
297
- actualSql .append (markerHolder . addMarker ( paramName ));
303
+ actualSql .append (marker . addPlaceholder ( ));
298
304
}
299
305
actualSql .append (')' );
300
306
} else {
301
- actualSql .append (markerHolder . addMarker ( paramName ));
307
+ actualSql .append (marker . addPlaceholder ( ));
302
308
}
303
309
304
310
}
305
311
} else {
306
- actualSql .append (markerHolder . addMarker ( paramName ));
312
+ actualSql .append (marker . getPlaceholder ( ));
307
313
}
308
314
} else {
309
- actualSql .append (markerHolder . addMarker ( paramName ));
315
+ actualSql .append (marker . getPlaceholder ( ));
310
316
}
311
317
lastIndex = endIndex ;
312
318
}
@@ -387,22 +393,79 @@ public int hashCode() {
387
393
}
388
394
389
395
/**
390
- * Holder for bind marker progress.
396
+ * Holder for bind markers progress.
391
397
*/
392
- private static class BindMarkerHolder {
398
+ static class NamedParameters {
393
399
394
400
private final BindMarkers bindMarkers ;
395
- private final Map <String , List <BindMarker >> markers = new TreeMap <>();
401
+ private final boolean identifiable ;
402
+ private final Map <String , List <NamedParameter >> references = new TreeMap <>();
403
+
404
+ NamedParameters (BindMarkersFactory factory ) {
405
+ this .bindMarkers = factory .create ();
406
+ this .identifiable = factory .identifiablePlaceholders ();
407
+ }
408
+
409
+ /**
410
+ * Get the {@link NamedParameter} identified by {@code namedParameter}.
411
+ *
412
+ * @param namedParameter
413
+ * @return
414
+ */
415
+ NamedParameter getOrCreate (String namedParameter ) {
416
+
417
+ List <NamedParameter > reference = this .references .computeIfAbsent (namedParameter , ignore -> new ArrayList <>());
418
+
419
+ if (reference .isEmpty ()) {
420
+ NamedParameter param = new NamedParameter (namedParameter );
421
+ reference .add (param );
422
+ return param ;
423
+ }
396
424
397
- BindMarkerHolder (BindMarkers bindMarkers ) {
398
- this .bindMarkers = bindMarkers ;
425
+ if (this .identifiable ) {
426
+ return reference .get (0 );
427
+ }
428
+
429
+ NamedParameter param = new NamedParameter (namedParameter );
430
+ reference .add (param );
431
+ return param ;
399
432
}
400
433
401
- String addMarker (String name ) {
434
+ List <NamedParameter > getMarker (String name ) {
435
+ return this .references .get (name );
436
+ }
437
+
438
+ class NamedParameter {
439
+
440
+ private final String namedParameter ;
441
+ private final List <BindMarker > placeholders = new ArrayList <>();
442
+
443
+ NamedParameter (String namedParameter ) {
444
+ this .namedParameter = namedParameter ;
445
+ }
446
+
447
+ /**
448
+ * Create a placeholder to translate a single value into a bindable parameter.
449
+ * <p>
450
+ * Can be called multiple times to create placeholders for array/collections.
451
+ *
452
+ * @return
453
+ */
454
+ String addPlaceholder () {
455
+
456
+ BindMarker bindMarker = NamedParameters .this .bindMarkers .next (this .namedParameter );
457
+ this .placeholders .add (bindMarker );
458
+ return bindMarker .getPlaceholder ();
459
+ }
460
+
461
+ String getPlaceholder () {
462
+
463
+ if (this .placeholders .isEmpty ()) {
464
+ return addPlaceholder ();
465
+ }
402
466
403
- BindMarker bindMarker = this .bindMarkers .next (name );
404
- this .markers .computeIfAbsent (name , ignore -> new ArrayList <>()).add (bindMarker );
405
- return bindMarker .getPlaceholder ();
467
+ return this .placeholders .get (0 ).getPlaceholder ();
468
+ }
406
469
}
407
470
}
408
471
@@ -414,13 +477,13 @@ private static class ExpandedQuery implements PreparedOperation<String> {
414
477
415
478
private final String expandedSql ;
416
479
417
- private final Map < String , List < BindMarker >> markers ;
480
+ private final NamedParameters parameters ;
418
481
419
482
private final BindParameterSource parameterSource ;
420
483
421
- ExpandedQuery (String expandedSql , BindMarkerHolder bindMarkerHolder , BindParameterSource parameterSource ) {
484
+ ExpandedQuery (String expandedSql , NamedParameters parameters , BindParameterSource parameterSource ) {
422
485
this .expandedSql = expandedSql ;
423
- this .markers = bindMarkerHolder . markers ;
486
+ this .parameters = parameters ;
424
487
this .parameterSource = parameterSource ;
425
488
}
426
489
@@ -435,13 +498,7 @@ public void bind(BindTarget target, String identifier, Object value) {
435
498
return ;
436
499
}
437
500
438
- if (bindMarkers .size () == 1 ) {
439
- bindMarkers .get (0 ).bind (target , value );
440
- } else {
441
-
442
- Assert .isInstanceOf (Collection .class , value ,
443
- () -> String .format ("Value [%s] must be an Collection with a size of [%d]" , value , bindMarkers .size ()));
444
-
501
+ if (value instanceof Collection ) {
445
502
Collection <Object > collection = (Collection <Object >) value ;
446
503
447
504
Iterator <Object > iterator = collection .iterator ();
@@ -460,6 +517,10 @@ public void bind(BindTarget target, String identifier, Object value) {
460
517
bind (target , markers , valueToBind );
461
518
}
462
519
}
520
+ } else {
521
+ for (BindMarker bindMarker : bindMarkers ) {
522
+ bindMarker .bind (target , value );
523
+ }
463
524
}
464
525
}
465
526
@@ -483,16 +544,26 @@ public void bindNull(BindTarget target, String identifier, Class<?> valueType) {
483
544
return ;
484
545
}
485
546
486
- if (bindMarkers .size () == 1 ) {
487
- bindMarkers .get (0 ).bindNull (target , valueType );
488
- return ;
547
+ for (BindMarker bindMarker : bindMarkers ) {
548
+ bindMarker .bindNull (target , valueType );
489
549
}
490
-
491
- throw new UnsupportedOperationException ("bindNull(…) can bind only singular values" );
492
550
}
493
551
494
- private List <BindMarker > getBindMarkers (String identifier ) {
495
- return this .markers .get (identifier );
552
+ List <BindMarker > getBindMarkers (String identifier ) {
553
+
554
+ List <NamedParameters .NamedParameter > parameters = this .parameters .getMarker (identifier );
555
+
556
+ if (parameters == null ) {
557
+ return null ;
558
+ }
559
+
560
+ List <BindMarker > markers = new ArrayList <>();
561
+
562
+ for (NamedParameters .NamedParameter parameter : parameters ) {
563
+ markers .addAll (parameter .placeholders );
564
+ }
565
+
566
+ return markers ;
496
567
}
497
568
498
569
@ Override
0 commit comments