1
1
package com .nordstrom .common .jdbc ;
2
2
3
+ import java .sql .CallableStatement ;
3
4
import java .sql .Connection ;
4
5
import java .sql .Driver ;
5
6
import java .sql .DriverManager ;
6
7
import java .sql .ResultSet ;
7
8
import java .sql .SQLException ;
8
9
import java .util .Arrays ;
9
10
import java .util .Iterator ;
11
+ import java .util .Objects ;
10
12
import java .util .ServiceLoader ;
13
+ import java .util .regex .Matcher ;
14
+ import java .util .regex .Pattern ;
11
15
12
16
import com .nordstrom .common .base .UncheckedThrow ;
17
+ import com .nordstrom .common .jdbc .Param .Mode ;
13
18
14
19
import java .sql .PreparedStatement ;
15
20
16
21
/**
17
- * This utility class provides facilities that enable you to define collections of database queries and
18
- * execute them easily. Query collections are defined as Java enumeration that implement the {@link QueryAPI}
19
- * interface: <ul>
22
+ * This utility class provides facilities that enable you to define collections of database queries and stored
23
+ * procedures in an easy-to-execute format.
24
+ * <p>
25
+ * Query collections are defined as Java enumerations that implement the {@link QueryAPI}
26
+ * interface:
27
+ * <ul>
20
28
* <li>{@link QueryAPI#getQueryStr() getQueryStr} - Get the query string for this constant. This is the actual query
21
29
* that's sent to the database.</li>
22
30
* <li>{@link QueryAPI#getArgNames() getArgNames} - Get the names of the arguments for this query. This provides
23
31
* diagnostic information if the incorrect number of arguments is specified by the client.</li>
24
- * <li>{@link QueryAPI#getArgCount() getArgCount} - Get the number of arguments required by this query. This enables
25
- * {@link #executeQuery(Class, QueryAPI, Object[])} to verify that the correct number of arguments has been
26
- * specified by the client.</li>
27
32
* <li>{@link QueryAPI#getConnection() getConnection} - Get the connection string associated with this query. This
28
33
* eliminates the need for the client to provide this information.</li>
29
34
* <li>{@link QueryAPI#getEnum() getEnum} - Get the enumeration to which this query belongs. This enables {@link
30
35
* #executeQuery(Class, QueryAPI, Object[])} to retrieve the name of the query's enumerated constant for
31
36
* diagnostic messages.</li>
32
37
* </ul>
33
- *
34
- * To maximize usability and configurability, we recommend the following implementation strategy for your query
35
- * collections: <ul>
36
- * <li>Define your query collection as an enumeration that implements {@link QueryAPI}.</li>
37
- * <li>Define each query constant with a property name and a name for each argument (if any).</li>
38
+ * <p>
39
+ * Store procedure collections are defined as Java enumerations that implement the {@link SProcAPI}
40
+ * interface:
41
+ * <ul>
42
+ * <li>{@link SProcAPI#getSignature() getSignature} - Get the signature for this stored procedure object. This defines
43
+ * the name of the stored procedure and the modes of its arguments. If the stored procedure accepts varargs, this
44
+ * will also be indicated.</li>
45
+ * <li>{@link SProcAPI#getArgTypes() getArgTypes} - Get the argument types for this stored procedure object. </li>
46
+ * <li>{@link SProcAPI#getConnection() getConnection} - Get the connection string associated with this stored
47
+ * procedure. This eliminates the need for the client to provide this information.</li>
48
+ * <li>{@link SProcAPI#getEnum() getEnum} - Get the enumeration to which this stored procedure belongs. This enables
49
+ * {@link #executeStoredProcedure(Class, SProcAPI, Object[])} to retrieve the name of the stored procedured's
50
+ * enumerated constant for diagnostic messages.</li>
51
+ * </ul>
52
+ * <p>
53
+ * To maximize usability and configurability, we recommend the following implementation strategy: <ul>
54
+ * <li>Define your collection as an enumeration: <ul>
55
+ * <li>Query collections implement {@link QueryAPI}.</li>
56
+ * <li>Stored procedure collections implement {@link SProcAPI}.</li>
57
+ * </ul></li>
58
+ * <li>Define each constant: <ul>
59
+ * <li>(query) Specify a property name and a name for each argument (if any).</li>
60
+ * <li>(sproc) Declare the signature and the type for each argument (if any).</li>
61
+ * </ul></li>
38
62
* <li>To assist users of your queries, preface their names with a type indicator (<b>GET</b> or <b>UPDATE</b>).</li>
39
- * <li>Back the query collection with a configuration that implements the <b>{@code Settings API}</b>: <ul>
63
+ * <li>Back query collections with configurations that implement the <b>{@code Settings API}</b>: <ul>
40
64
* <li>groupId: com.nordstrom.test-automation.tools</li>
41
65
* <li>artifactId: settings</li>
42
66
* <li>className: com.nordstrom.automation.settings.SettingsCore</li>
43
- * </ul>
44
- * </ li>
45
- * <li>To support execution on multiple endpoints, implement {@link QueryAPI #getConnection() getConnection } with
46
- * sub-configurations or other dynamic data sources (e.g. - web service).</li>
67
+ * </ul></li>
68
+ * <li>To support execution on multiple endpoints, implement {@link QueryAPI#getConnection()} or {@link
69
+ * SProcAPI #getConnection()} with sub-configurations or other dynamic data sources (e.g.
70
+ * - web service).</li>
47
71
* </ul>
48
72
* <b>Query Collection Example</b>
49
- * <br><br>
73
+ *
50
74
* <pre>
51
75
* public class OpctConfig extends {@code SettingsCore<OpctConfig.OpctValues>} {
52
76
*
110
134
* }
111
135
*
112
136
* {@code @Override}
113
- * public int getArgCount() {
114
- * return args.length;
115
- * }
116
- *
117
- * {@code @Override}
118
137
* public String getConnection() {
119
138
* if (rmsQueries.contains(this)) {
120
139
* return getRmsConnect();
166
185
* public static OpctConfig getConfig() {
167
186
* return OpctValues.config;
168
187
* }
188
+ *
189
+ * public enum SProcValues implements SProcAPI {
190
+ * /** args: [ ] */
191
+ * SHOW_SUPPLIERS("SHOW_SUPPLIERS()"),
192
+ * /** args: [ coffee_name, supplier_name ] */
193
+ * GET_SUPPLIER_OF_COFFEE("GET_SUPPLIER_OF_COFFEE(>, <)", Types.VARCHAR, Types.VARCHAR),
194
+ * /** args: [ coffee_name, max_percent, new_price ] */
195
+ * RAISE_PRICE("RAISE_PRICE(>, >, =)", Types.VARCHAR, Types.REAL, Types.NUMERIC),
196
+ * /** args: [ str, val... ] */
197
+ * IN_VARARGS("IN_VARARGS(<, >:)", Types.VARCHAR, Types.INTEGER),
198
+ * /** args: [ val, str... ] */
199
+ * OUT_VARARGS("OUT_VARARGS(>, <:)", Types.INTEGER, Types.VARCHAR);
200
+ *
201
+ * private int[] argTypes;
202
+ * private String signature;
203
+ *
204
+ * SProcValues(String signature, int... argTypes) {
205
+ * this.signature = signature;
206
+ *
207
+ * this.argTypes = argTypes;
208
+ * }
209
+ *
210
+ * {@code @Override}
211
+ * public String getSignature() {
212
+ * return signature;
213
+ * }
214
+ *
215
+ * {@code @Override}
216
+ * public int[] getArgTypes () {
217
+ * return argTypes;
218
+ * }
219
+ *
220
+ * {@code @Override}
221
+ * public String getConnection() {
222
+ return OpctValues.getRmsConnect();
223
+ * }
224
+ *
225
+ * {@code @Override}
226
+ * public {@code Enum<SProcValues>} getEnum() {
227
+ * return this;
228
+ * }
229
+ * }
169
230
* }
170
231
* </pre>
171
232
*/
172
233
public class DatabaseUtils {
173
234
235
+ private static Pattern SPROC_PATTERN =
236
+ Pattern .compile ("([\\ p{Alpha}_][\\ p{Alpha}\\ p{Digit}@$#_]*)(?:\\ (([<>=](?:,\\ s*[<>=])*)?(:)?\\ ))?" );
237
+
174
238
private DatabaseUtils () {
175
239
throw new AssertionError ("DatabaseUtils is a static utility class that cannot be instantiated" );
176
240
}
@@ -211,7 +275,7 @@ public static int getInt(QueryAPI query, Object... queryArgs) {
211
275
*
212
276
* @param query query object to execute
213
277
* @param queryArgs replacement values for query place-holders
214
- * @return row 1 / column 1 as string; ' null' if no rows were returned
278
+ * @return row 1 / column 1 as string; {@code null} if no rows were returned
215
279
*/
216
280
public static String getString (QueryAPI query , Object ... queryArgs ) {
217
281
return (String ) executeQuery (String .class , query , queryArgs );
@@ -230,12 +294,12 @@ public static ResultPackage getResultPackage(QueryAPI query, Object... queryArgs
230
294
231
295
/**
232
296
* Execute the specified query with the supplied arguments, returning a result of the indicated type.
233
- * <br><br >
297
+ * <p >
234
298
* <b>TYPES</b>: Specific result types produce the following behaviors: <ul>
235
- * <li>' null' - The query is executed as an update operation.</li>
299
+ * <li>{@code null} - The query is executed as an update operation.</li>
236
300
* <li>{@link ResultPackage} - An object containing the connection, statement, and result set is returned</li>
237
301
* <li>{@link Integer} - If rows were returned, row 1 / column 1 is returned as an Integer; otherwise -1</li>
238
- * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise ' null' </li>
302
+ * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise {@code null} </li>
239
303
* <li>For other types, {@link ResultSet#getObject(int, Class)} to return row 1 / column 1 as that type</li></ul>
240
304
*
241
305
* @param resultType desired result type (see TYPES above)
@@ -246,7 +310,7 @@ public static ResultPackage getResultPackage(QueryAPI query, Object... queryArgs
246
310
* when you're done with it to free up database and JDBC resources that were allocated for it.
247
311
*/
248
312
private static Object executeQuery (Class <?> resultType , QueryAPI query , Object ... queryArgs ) {
249
- int expectCount = query .getArgCount () ;
313
+ int expectCount = query .getArgNames (). length ;
250
314
int actualCount = queryArgs .length ;
251
315
252
316
if (actualCount != expectCount ) {
@@ -267,51 +331,270 @@ private static Object executeQuery(Class<?> resultType, QueryAPI query, Object..
267
331
268
332
/**
269
333
* Execute the specified query with the supplied arguments, returning a result of the indicated type.
270
- * <br><br >
334
+ * <p >
271
335
* <b>TYPES</b>: Specific result types produce the following behaviors: <ul>
272
- * <li>' null' - The query is executed as an update operation.</li>
336
+ * <li>{@code null} - The query is executed as an update operation.</li>
273
337
* <li>{@link ResultPackage} - An object containing the connection, statement, and result set is returned</li>
274
338
* <li>{@link Integer} - If rows were returned, row 1 / column 1 is returned as an Integer; otherwise -1</li>
275
- * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise ' null' </li>
339
+ * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise {@code null} </li>
276
340
* <li>For other types, {@link ResultSet#getObject(int, Class)} to return row 1 / column 1 as that type</li></ul>
277
341
*
278
342
* @param resultType desired result type (see TYPES above)
279
343
* @param connectionStr database connection string
280
344
* @param queryStr a SQL statement that may contain one or more '?' IN parameter placeholders
281
- * @param param an array of objects containing the input parameter values
345
+ * @param params an array of objects containing the input parameter values
282
346
* @return for update operations, the number of rows affected; for query operations, an object of the indicated type<br>
283
347
* <b>NOTE</b>: If you specify {@link ResultPackage} as the result type, it's recommended that you close this object
284
348
* when you're done with it to free up database and JDBC resources that were allocated for it.
285
349
*/
286
- public static Object executeQuery (Class <?> resultType , String connectionStr , String queryStr , Object ... param ) {
287
- Object result = null ;
288
- boolean failed = false ;
350
+ public static Object executeQuery (Class <?> resultType , String connectionStr , String queryStr , Object ... params ) {
351
+ try {
352
+ Connection connection = getConnection (connectionStr );
353
+ PreparedStatement statement = connection .prepareStatement (queryStr );
354
+
355
+ for (int i = 0 ; i < params .length ; i ++) {
356
+ statement .setObject (i + 1 , params [i ]);
357
+ }
358
+
359
+ return executeStatement (resultType , connection , statement );
360
+ } catch (SQLException e ) {
361
+ throw UncheckedThrow .throwUnchecked (e );
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Execute the specified stored procedure object with supplied parameters
367
+ *
368
+ * @param sproc stored procedure object to execute
369
+ * @param params an array of objects containing the input parameter values
370
+ * @return row 1 / column 1 as integer; -1 if no rows were returned
371
+ */
372
+ public static int getInt (SProcAPI sproc , Object ... params ) {
373
+ Integer result = (Integer ) executeStoredProcedure (Integer .class , sproc , params );
374
+ return (result != null ) ? result .intValue () : -1 ;
375
+ }
376
+
377
+ /**
378
+ * Execute the specified stored procedure object with supplied parameters
379
+ *
380
+ * @param sproc stored procedure object to execute
381
+ * @param params an array of objects containing the input parameter values
382
+ * @return row 1 / column 1 as string; {@code null} if no rows were returned
383
+ */
384
+ public static String getString (SProcAPI sproc , Object ... params ) {
385
+ return (String ) executeStoredProcedure (String .class , sproc , params );
386
+ }
387
+
388
+ /**
389
+ * Execute the specified stored procedure object with supplied parameters
390
+ *
391
+ * @param sproc stored procedure object to execute
392
+ * @param params an array of objects containing the input parameter values
393
+ * @return {@link ResultPackage} object
394
+ */
395
+ public static ResultPackage getResultPackage (SProcAPI sproc , Object ... params ) {
396
+ return (ResultPackage ) executeStoredProcedure (ResultPackage .class , sproc , params );
397
+ }
398
+
399
+ /**
400
+ * Execute the specified stored procedure with the specified arguments, returning a result of the indicated type.
401
+ * <p>
402
+ * <b>TYPES</b>: Specific result types produce the following behaviors: <ul>
403
+ * <li>{@link ResultPackage} - An object containing the connection, statement, and result set is returned</li>
404
+ * <li>{@link Integer} - If rows were returned, row 1 / column 1 is returned as an Integer; otherwise -1</li>
405
+ * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise {@code null}</li>
406
+ * <li>For other types, {@link ResultSet#getObject(int, Class)} to return row 1 / column 1 as that type</li></ul>
407
+ *
408
+ * @param resultType desired result type (see TYPES above)
409
+ * @param sproc stored procedure object to execute
410
+ * @param params an array of objects containing the input parameter values
411
+ * @return an object of the indicated type<br>
412
+ * <b>NOTE</b>: If you specify {@link ResultPackage} as the result type, it's recommended that you close this object
413
+ * when you're done with it to free up database and JDBC resources that were allocated for it.
414
+ */
415
+ public static Object executeStoredProcedure (Class <?> resultType , SProcAPI sproc , Object ... params ) {
416
+ Objects .requireNonNull (resultType , "[resultType] argument must be non-null" );
289
417
290
- Connection connection = null ;
291
- PreparedStatement statement = null ;
292
- ResultSet resultSet = null ;
418
+ String [] args = {};
419
+ String sprocName = null ;
420
+ boolean hasVarArgs = false ;
421
+ int [] argTypes = sproc .getArgTypes ();
422
+ String signature = sproc .getSignature ();
423
+ Matcher matcher = SPROC_PATTERN .matcher (signature );
424
+
425
+ String message = null ;
426
+ if (matcher .matches ()) {
427
+ sprocName = matcher .group (1 );
428
+ hasVarArgs = (matcher .group (3 ) != null );
429
+ if (matcher .group (2 ) != null ) {
430
+ args = matcher .group (2 ).split (",\\ s" );
431
+ } else {
432
+ if (hasVarArgs ) {
433
+ message = String .format ("VarArgs indicated with no placeholder in signature for %s: %s" ,
434
+ sproc .getEnum ().name (), signature );
435
+ }
436
+ }
437
+ } else {
438
+ message = String .format ("Unsupported stored procedure signature for %s: %s" ,
439
+ sproc .getEnum ().name (), signature );
440
+ }
441
+
442
+ if (message != null ) {
443
+ throw new IllegalArgumentException (message );
444
+ }
445
+
446
+ int argsCount = args .length ;
447
+ int typesCount = argTypes .length ;
448
+ int parmsCount = params .length ;
449
+
450
+ int minCount = typesCount ;
451
+
452
+ // if unbalanced args/types
453
+ if (argsCount != typesCount ) {
454
+ message = String .format (
455
+ "Signature argument count differs from declared type count for %s%s: "
456
+ + "signature: %d; declared: %d" ,
457
+ sproc .getEnum ().name (), Arrays .toString (argTypes ), argsCount , typesCount );
458
+ } else if (hasVarArgs ) {
459
+ minCount -= 1 ;
460
+ if (parmsCount < minCount ) {
461
+ message = String .format (
462
+ "Insufficient arguments count for %s%s: minimum: %d; actual: %d" ,
463
+ sproc .getEnum ().name (), Arrays .toString (argTypes ), minCount , parmsCount );
464
+ }
465
+ } else if (parmsCount != typesCount ) {
466
+ if (typesCount == 0 ) {
467
+ message = "No arguments expected for " + sproc .getEnum ().name ();
468
+ } else {
469
+ message = String .format (
470
+ "Incorrect arguments count for %s%s: expect: %d; actual: %d" ,
471
+ sproc .getEnum ().name (), Arrays .toString (argTypes ), typesCount , parmsCount );
472
+ }
473
+ }
474
+
475
+ if (message != null ) {
476
+ throw new IllegalArgumentException (message );
477
+ }
478
+
479
+ Param [] parmArray = Param .array (parmsCount );
480
+
481
+ int i ;
482
+
483
+ // process declared parameters
484
+ for (i = 0 ; i < minCount ; i ++) {
485
+ Mode mode = Mode .fromChar (args [i ].charAt (0 ));
486
+ parmArray [i ] = Param .create (mode , argTypes [i ], params [i ]);
487
+ }
488
+
489
+ // handle varargs parameters
490
+ for (int j = i ; j < parmsCount ; j ++) {
491
+ Mode mode = Mode .fromChar (args [i ].charAt (0 ));
492
+ parmArray [j ] = Param .create (mode , argTypes [i ], params [j ]);
493
+ }
494
+
495
+ return executeStoredProcedure (resultType , sproc .getConnection (), sprocName , parmArray );
496
+ }
497
+
498
+ /**
499
+ * Execute the specified stored procedure with the supplied arguments, returning a result of the indicated type.
500
+ * <p>
501
+ * <b>TYPES</b>: Specific result types produce the following behaviors: <ul>
502
+ * <li>{@link ResultPackage} - An object containing the connection, statement, and result set is returned</li>
503
+ * <li>{@link Integer} - If rows were returned, row 1 / column 1 is returned as an Integer; otherwise -1</li>
504
+ * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise {@code null}</li>
505
+ * <li>For other types, {@link ResultSet#getObject(int, Class)} to return row 1 / column 1 as that type</li></ul>
506
+ *
507
+ * @param resultType desired result type (see TYPES above)
508
+ * @param connectionStr database connection string
509
+ * @param sprocName name of the stored procedure to be executed
510
+ * @param params an array of objects containing the input parameter values
511
+ * @return an object of the indicated type<br>
512
+ * <b>NOTE</b>: If you specify {@link ResultPackage} as the result type, it's recommended that you close this object
513
+ * when you're done with it to free up database and JDBC resources that were allocated for it.
514
+ */
515
+ public static Object executeStoredProcedure (Class <?> resultType , String connectionStr , String sprocName , Param ... params ) {
516
+ Objects .requireNonNull (resultType , "[resultType] argument must be non-null" );
517
+
518
+ StringBuilder sprocStr = new StringBuilder ("{call " ).append (sprocName ).append ("(" );
519
+
520
+ String placeholder = "?" ;
521
+ for (int i = 0 ; i < params .length ; i ++) {
522
+ sprocStr .append (placeholder );
523
+ placeholder = ",?" ;
524
+ }
525
+
526
+ sprocStr .append (")}" );
293
527
294
528
try {
295
- connection = getConnection (connectionStr );
296
- statement = connection .prepareStatement ( queryStr ); //NOSONAR
529
+ Connection connection = getConnection (connectionStr );
530
+ CallableStatement statement = connection .prepareCall ( sprocStr . toString ());
297
531
298
- for (int i = 0 ; i < param .length ; i ++) {
299
- statement . setObject ( i + 1 , param [ i ] );
532
+ for (int i = 0 ; i < params .length ; i ++) {
533
+ params [ i ]. set ( statement , i + 1 );
300
534
}
301
535
536
+ return executeStatement (resultType , connection , statement );
537
+ } catch (SQLException e ) {
538
+ throw UncheckedThrow .throwUnchecked (e );
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Execute the specified prepared statement, returning a result of the indicated type.
544
+ * <p>
545
+ * <b>TYPES</b>: Specific result types produce the following behaviors: <ul>
546
+ * <li>{@code null} - The prepared statement is a query to be executed as an update operation.</li>
547
+ * <li>{@link ResultPackage} - An object containing the connection, statement, and result set is returned</li>
548
+ * <li>{@link Integer} - If rows were returned, row 1 / column 1 is returned as an Integer; otherwise -1</li>
549
+ * <li>{@link String} - If rows were returned, row 1 / column 1 is returned as an String; otherwise {@code null}</li>
550
+ * <li>For other types, {@link ResultSet#getObject(int, Class)} to return row 1 / column 1 as that type</li></ul>
551
+ * <p>
552
+ * <b>NOTE</b>: For all result types except {@link ResultPackage}, the specified connection and statement, as well
553
+ * as the result set from executing the statement, are closed prior to returning the result.
554
+ *
555
+ * @param resultType desired result type (see TYPES above)
556
+ * @param connectionStr database connection string
557
+ * @param statement prepared statement to be executed (query or store procedure)
558
+ * @return for update operations, the number of rows affected; for query operations, an object of the indicated type<br>
559
+ * <b>NOTE</b>: If you specify {@link ResultPackage} as the result type, it's recommended that you close this object
560
+ * when you're done with it to free up database and JDBC resources that were allocated for it.
561
+ */
562
+ private static Object executeStatement (Class <?> resultType , Connection connection , PreparedStatement statement ) {
563
+ Object result = null ;
564
+ boolean failed = false ;
565
+
566
+ ResultSet resultSet = null ;
567
+
568
+ try {
302
569
if (resultType == null ) {
303
570
result = Integer .valueOf (statement .executeUpdate ());
304
571
} else {
305
- resultSet = statement .executeQuery (); //NOSONAR
306
-
307
- if (resultType == ResultPackage .class ) {
308
- result = new ResultPackage (connection , statement , resultSet ); //NOSONAR
309
- } else if (resultType == Integer .class ) {
310
- result = Integer .valueOf ((resultSet .next ()) ? resultSet .getInt (1 ) : -1 );
311
- } else if (resultType == String .class ) {
312
- result = (resultSet .next ()) ? resultSet .getString (1 ) : null ;
572
+ if (statement instanceof CallableStatement ) {
573
+ if (statement .execute ()) {
574
+ resultSet = statement .getResultSet (); //NOSONAR
575
+ }
576
+
577
+ if (resultType == ResultPackage .class ) {
578
+ result = new ResultPackage (connection , statement , resultSet ); //NOSONAR
579
+ } else if (resultType == Integer .class ) {
580
+ result = ((CallableStatement ) statement ).getInt (1 );
581
+ } else if (resultType == String .class ) {
582
+ result = ((CallableStatement ) statement ).getString (1 );
583
+ } else {
584
+ result = ((CallableStatement ) statement ).getObject (1 );
585
+ }
313
586
} else {
314
- result = (resultSet .next ()) ? resultSet .getObject (1 , resultType ) : null ;
587
+ resultSet = statement .executeQuery (); //NOSONAR
588
+
589
+ if (resultType == ResultPackage .class ) {
590
+ result = new ResultPackage (connection , statement , resultSet ); //NOSONAR
591
+ } else if (resultType == Integer .class ) {
592
+ result = Integer .valueOf ((resultSet .next ()) ? resultSet .getInt (1 ) : -1 );
593
+ } else if (resultType == String .class ) {
594
+ result = (resultSet .next ()) ? resultSet .getString (1 ) : null ;
595
+ } else {
596
+ result = (resultSet .next ()) ? resultSet .getObject (1 , resultType ) : null ;
597
+ }
315
598
}
316
599
}
317
600
@@ -375,19 +658,12 @@ public interface QueryAPI {
375
658
String getQueryStr ();
376
659
377
660
/**
378
- * Get the argument name for this query object
661
+ * Get the argument names for this query object.
379
662
*
380
663
* @return query object argument names
381
664
*/
382
665
String [] getArgNames ();
383
666
384
- /**
385
- * Get the count of arguments for this query object.
386
- *
387
- * @return query object argument count
388
- */
389
- int getArgCount ();
390
-
391
667
/**
392
668
* Get the database connection string for this query object.
393
669
*
@@ -403,6 +679,55 @@ public interface QueryAPI {
403
679
Enum <? extends QueryAPI > getEnum (); //NOSONAR
404
680
}
405
681
682
+ /**
683
+ * This interface defines the API supported by database stored procedure collections
684
+ */
685
+ public interface SProcAPI {
686
+
687
+ /**
688
+ * Get the signature for this stored procedure object.
689
+ * <p>
690
+ * Each argument place holder in the stored procedure signature indicates the mode of the corresponding
691
+ * parameter:
692
+ *
693
+ * <ul>
694
+ * <li>'>' : This argument is an IN parameter</li>
695
+ * <li>'<' : This argument is an OUT parameter</li>
696
+ * <li>'=' : This argument is an INOUT parameter</li>
697
+ * </ul>
698
+ *
699
+ * For example:
700
+ *
701
+ * <blockquote>RAISE_PRICE(>, <, =)</blockquote>
702
+ *
703
+ * The first and second arguments are IN parameters, and the third argument is an INOUT parameter.
704
+ *
705
+ * @return stored procedure signature
706
+ */
707
+ String getSignature ();
708
+
709
+ /**
710
+ * Get the argument types for this stored procedure object.
711
+ *
712
+ * @return stored procedure argument types
713
+ */
714
+ int [] getArgTypes ();
715
+
716
+ /**
717
+ * Get the database connection string for this stored procedure object.
718
+ *
719
+ * @return stored procedure connection string
720
+ */
721
+ String getConnection ();
722
+
723
+ /**
724
+ * Get the implementing enumerated constant for this stored procedure object.
725
+ *
726
+ * @return stored procedure enumerated constant
727
+ */
728
+ Enum <? extends SProcAPI > getEnum (); //NOSONAR
729
+ }
730
+
406
731
/**
407
732
* This class defines a package of database objects associated with a query. These include:<ul>
408
733
* <li>{@link Connection} object</li>
@@ -428,6 +753,21 @@ private ResultPackage(Connection connection, PreparedStatement statement, Result
428
753
this .resultSet = resultSet ;
429
754
}
430
755
756
+ public Connection getConnection () {
757
+ return connection ;
758
+ }
759
+
760
+ public PreparedStatement getStatement () {
761
+ return statement ;
762
+ }
763
+
764
+ public CallableStatement getCallable () {
765
+ if (statement instanceof CallableStatement ) {
766
+ return (CallableStatement ) statement ;
767
+ }
768
+ throw new UnsupportedOperationException ("The statement of this package is not a CallableStatement" );
769
+ }
770
+
431
771
/**
432
772
* Get the result set object of this package.
433
773
*
0 commit comments