15
15
*/
16
16
package org .springframework .data .jpa .repository .query ;
17
17
18
- import static org .assertj .core .api .Assertions .*;
19
- import static org .mockito .Mockito .*;
18
+ import static org .assertj .core .api .Assertions .assertThat ;
19
+ import static org .mockito .Mockito .doReturn ;
20
+ import static org .mockito .Mockito .when ;
21
+
22
+ import jakarta .persistence .EntityManager ;
23
+ import jakarta .persistence .Id ;
24
+ import jakarta .persistence .NamedStoredProcedureQuery ;
25
+ import jakarta .persistence .ParameterMode ;
26
+ import jakarta .persistence .StoredProcedureParameter ;
20
27
21
28
import java .lang .annotation .Retention ;
22
29
import java .lang .annotation .RetentionPolicy ;
23
30
import java .lang .reflect .Method ;
31
+ import java .util .ArrayList ;
32
+ import java .util .Arrays ;
24
33
import java .util .List ;
25
34
import java .util .Map ;
26
35
27
- import jakarta .persistence .EntityManager ;
28
- import jakarta .persistence .ParameterMode ;
29
-
30
36
import org .junit .jupiter .api .BeforeEach ;
31
37
import org .junit .jupiter .api .Test ;
32
38
import org .junit .jupiter .api .extension .ExtendWith ;
39
+ import org .junit .jupiter .params .ParameterizedTest ;
40
+ import org .junit .jupiter .params .provider .ValueSource ;
33
41
import org .mockito .Mock ;
34
42
import org .mockito .junit .jupiter .MockitoExtension ;
35
43
import org .mockito .junit .jupiter .MockitoSettings ;
58
66
class StoredProcedureAttributeSourceUnitTests {
59
67
60
68
private StoredProcedureAttributeSource creator ;
61
- @ Mock JpaEntityMetadata <User > entityMetadata ;
69
+ @ Mock JpaEntityMetadata <? > entityMetadata ;
62
70
63
71
@ BeforeEach
64
72
void setup () {
65
73
66
74
creator = StoredProcedureAttributeSource .INSTANCE ;
67
75
68
- when ( entityMetadata . getJavaType ()). thenReturn ( User . class );
76
+ doReturn ( User . class ). when ( entityMetadata ). getJavaType ( );
69
77
when (entityMetadata .getEntityName ()).thenReturn ("User" );
70
78
}
71
79
@@ -325,6 +333,34 @@ public void testEntityListFromResultSetWithInputAndNamedOutputAndCursor() {
325
333
assertThat (outputParameter .getName ()).isEqualTo ("dummies" );
326
334
}
327
335
336
+ @ ParameterizedTest // GH-3463
337
+ @ ValueSource (
338
+ strings = { "inInOut" , "inoutInOut" , "inoutOut" , "outInIn" , "inInInoutOut" , "inInoutInOut" , "inInoutInoutOut" })
339
+ void storedProcedureParameterInoutAndOutParameterPositionDetection (String methodName ) {
340
+
341
+ String [] paramPattern = methodName .split ("(?=[A-Z])" );
342
+ Class <?>[] methodArgs = new Class <?>[paramPattern .length - 1 ];
343
+ Arrays .fill (methodArgs , Integer .class );
344
+
345
+ List <Integer > expectedOut = new ArrayList <>(2 );
346
+ int position = 0 ;
347
+ for (String s : paramPattern ) {
348
+ position ++;
349
+ switch (s .toLowerCase ()) {
350
+ case "inout" , "out" -> expectedOut .add (position );
351
+ }
352
+ }
353
+
354
+ doReturn (InOut .class ).when (entityMetadata ).getJavaType ();
355
+ when (entityMetadata .getEntityName ()).thenReturn ("InOut" );
356
+
357
+ StoredProcedureAttributes attr = creator .createFrom (method (methodName , methodArgs ), entityMetadata );
358
+ assertThat (attr .getOutputProcedureParameters ()).extracting (ProcedureParameter ::getPosition ) //
359
+ .withFailMessage ("Expecting method %s to have %s out parameters at positions %s but was %s." , methodName ,
360
+ expectedOut .size (), expectedOut , attr .getOutputProcedureParameters ()) //
361
+ .isEqualTo (expectedOut );
362
+ }
363
+
328
364
private static Method method (String name , Class <?>... paramTypes ) {
329
365
return ReflectionUtils .findMethod (DummyRepository .class , name , paramTypes );
330
366
}
@@ -410,6 +446,27 @@ private interface DummyRepository {
410
446
411
447
@ Procedure (value = "1_input_1_resultset" , outputParameterName = "dummies" , refCursor = true ) // DATAJPA-1657
412
448
List <Dummy > entityListFromResultSetWithInputAndNamedOutputAndCursor (Integer arg );
449
+
450
+ @ Procedure (name = "InOut.in_in_out" )
451
+ Map <Object , Object > inInOut (Integer in1 , Integer in2 );
452
+
453
+ @ Procedure (name = "InOut.inout_in_out" )
454
+ Map <Object , Object > inoutInOut (Integer inout1 , Integer in2 );
455
+
456
+ @ Procedure (name = "InOut.inout_out" )
457
+ Map <Object , Object > inoutOut (Integer inout1 );
458
+
459
+ @ Procedure (name = "InOut.out_in_in" )
460
+ Map <Object , Object > outInIn (Integer in1 , Integer in2 );
461
+
462
+ @ Procedure (name = "InOut.in_in_inout_out" )
463
+ Map <Object , Object > inInInoutOut (Integer in1 , Integer in2 , Integer inout );
464
+
465
+ @ Procedure (name = "InOut.in_inout_in_out" )
466
+ Map <Object , Object > inInoutInOut (Integer in1 , Integer inout , Integer in2 );
467
+
468
+ @ Procedure (name = "InOut.in_inout_inout_out" )
469
+ Map <Object , Object > inInoutInoutOut (Integer in1 , Integer inout1 , Integer inout2 );
413
470
}
414
471
415
472
@ SuppressWarnings ("unused" )
@@ -429,4 +486,52 @@ private interface DummyRepository {
429
486
@ AliasFor (annotation = Procedure .class , attribute = "outputParameterName" )
430
487
String outParamName () default "" ;
431
488
}
489
+
490
+ @ NamedStoredProcedureQuery ( //
491
+ name = "InOut.in_in_out" , //
492
+ procedureName = "positional_in_in_out" , //
493
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
494
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
495
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
496
+ @ NamedStoredProcedureQuery ( //
497
+ name = "InOut.inout_in_out" , //
498
+ procedureName = "positional_inout_in_out" , //
499
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
500
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
501
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
502
+ @ NamedStoredProcedureQuery ( //
503
+ name = "InOut.inout_out" , //
504
+ procedureName = "positional_inout_out" , //
505
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
506
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
507
+ @ NamedStoredProcedureQuery ( //
508
+ name = "InOut.out_in_in" , //
509
+ procedureName = "positional_out_in_in" , //
510
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ),
511
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
512
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ) })
513
+ @ NamedStoredProcedureQuery ( //
514
+ name = "InOut.in_in_inout_out" , //
515
+ procedureName = "positional_in_in_inout_out" , //
516
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
517
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
518
+ @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
519
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
520
+ @ NamedStoredProcedureQuery ( //
521
+ name = "InOut.in_inout_in_out" , //
522
+ procedureName = "positional_in_inout_in_out" , //
523
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
524
+ @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
525
+ @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
526
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
527
+ @ NamedStoredProcedureQuery ( //
528
+ name = "InOut.in_inout_inout_out" , //
529
+ procedureName = "positional_in_inout_inout_out" , //
530
+ parameters = { @ StoredProcedureParameter (mode = ParameterMode .IN , type = Integer .class ),
531
+ @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
532
+ @ StoredProcedureParameter (mode = ParameterMode .INOUT , type = Integer .class ),
533
+ @ StoredProcedureParameter (mode = ParameterMode .OUT , type = Integer .class ) })
534
+ private static class InOut {
535
+ @ Id private Long id ;
536
+ }
432
537
}
0 commit comments