36
36
import oracle .r2dbc .impl .ReactiveJdbcAdapter .JdbcReadable ;
37
37
import oracle .r2dbc .impl .ReadablesMetadata .OutParametersMetadataImpl ;
38
38
import oracle .r2dbc .impl .ReadablesMetadata .RowMetadataImpl ;
39
- import oracle .sql .DatumWithConnection ;
40
39
41
40
import java .math .BigDecimal ;
42
41
import java .nio .ByteBuffer ;
@@ -388,6 +387,15 @@ private Object getJavaArray(int index, Class<?> javaType) {
388
387
: convertOracleArray (oracleArray , javaType );
389
388
}
390
389
390
+ /**
391
+ * Converts an {@code OracleArray} from Oracle JDBC into the Java array
392
+ * variant of the specified {@code javaType}. If the {@code javaType} is
393
+ * {@code Object.class}, this method converts to the R2DBC standard mapping
394
+ * for the SQL type of the ARRAY elements.
395
+ * @param oracleArray Array from Oracle JDBC. Not null.
396
+ * @param javaType Type to convert to. Not null.
397
+ * @return The converted array.
398
+ */
391
399
private Object convertOracleArray (
392
400
OracleArray oracleArray , Class <?> javaType ) {
393
401
try {
@@ -399,7 +407,7 @@ private Object convertOracleArray(
399
407
// In this case, the default mapping is declared as Object[] in
400
408
// SqlTypeMap, and this method gets called with Object.class. A default
401
409
// mapping is used in this case.
402
- Class <?> convertedType = getJavaArrayType (oracleArray , javaType );
410
+ Class <?> convertedType = getArrayTypeMapping (oracleArray , javaType );
403
411
404
412
// Attempt to have Oracle JDBC convert to the desired type
405
413
Object [] javaArray =
@@ -420,51 +428,55 @@ private Object convertOracleArray(
420
428
}
421
429
422
430
/**
423
- * Returns the Java array type that an ARRAY will be mapped to. If the
424
- * {@code javaType} argument is {@code Object.class}, then this method returns
425
- * a default mapping based on the element type of the ARRAY. Otherwise, this
426
- * method just returns the {@code javaType}.
431
+ * Returns the Java array type that an ARRAY will be mapped to. This method
432
+ * is used to determine a default type mapping when {@link #get(int)} or
433
+ * {@link #get(String)} are called. In this case, the {@code javaType}
434
+ * argument is expected to be {@code Object.class} and this method returns
435
+ * a default mapping based on the element type of the ARRAY. Otherwise, if
436
+ * the {@code javaType} is something more specific, this method just returns
437
+ * it.
427
438
*/
428
- private Class <?> getJavaArrayType (
439
+ private Class <?> getArrayTypeMapping (
429
440
OracleArray oracleArray , Class <?> javaType ) {
430
441
431
442
if (!Object .class .equals (javaType ))
432
443
return javaType ;
433
444
434
- // Determine a default Java type mapping for the element type of the ARRAY.
435
- // If the element type is DATE, handle it as if it were TIMESTAMP.
436
- // This is consistent with how DATE columns are usually handled, and
437
- // reflects the fact that Oracle DATE values have a time component.
438
445
int jdbcType = fromJdbc (oracleArray ::getBaseType );
446
+
447
+ // Check if the array is multi-dimensional
439
448
if (jdbcType == Types .ARRAY ) {
440
449
441
450
Object [] oracleArrays = (Object []) fromJdbc (oracleArray ::getArray );
442
451
443
- // TODO: It should be possible to determine the type of next ARRAY
444
- // dimension by calling
445
- // OracleConnection.createArray(
446
- // oracleArray.getSQLTypeName(), new Object[0]).getBaseType())
452
+ // An instance of OracleArray representing base type is needed in order to
453
+ // know the base type of the next dimension.
447
454
final OracleArray oracleArrayElement ;
448
455
if (oracleArrays .length > 0 ) {
449
456
oracleArrayElement = (OracleArray ) oracleArrays [0 ];
450
457
}
451
458
else {
452
- // Need to create an instance of the OracleArray base type in order to
453
- // know its own base type. The information for the base type ARRAY
454
- // should already be cached by Oracle JDBC, as the type info was
455
- // retrieved when value was returned from the database. If the
456
- // information is cached, then createOracleArray should not perform a
457
- // blocking call.
459
+ // The array is empty, so an OracleArray will need to be created. The
460
+ // type information for the ARRAY should be cached by Oracle JDBC, and
461
+ // so createOracleArray should not perform a blocking call.
458
462
oracleArrayElement = (OracleArray ) fromJdbc (() ->
459
463
jdbcConnection .unwrap (OracleConnection .class )
460
464
.createOracleArray (oracleArray .getBaseTypeName (), new Object [0 ]));
461
465
}
462
466
467
+ // Recursively call getJavaArrayType, creating a Java array at each level
468
+ // of recursion until a non-array SQL type is found. Returning back up the
469
+ // stack, the top level of recursion will then create an array with
470
+ // the right number of dimensions, and the class of this multi-dimensional
471
+ // array is returned.
463
472
return java .lang .reflect .Array .newInstance (
464
- getJavaArrayType (oracleArrayElement , Object .class ), 0 )
473
+ getArrayTypeMapping (oracleArrayElement , Object .class ), 0 )
465
474
.getClass ();
466
475
}
467
476
477
+ // If the element type is DATE, handle it as if it were TIMESTAMP.
478
+ // This is consistent with how DATE columns are usually handled, and
479
+ // reflects the fact that Oracle DATE values have a time component.
468
480
Type r2dbcType = SqlTypeMap .toR2dbcType (
469
481
jdbcType == Types .DATE ? Types .TIMESTAMP : jdbcType );
470
482
@@ -476,6 +488,12 @@ private Class<?> getJavaArrayType(
476
488
: javaType ;
477
489
}
478
490
491
+ /**
492
+ * Converts an array from Oracle JDBC into a Java array of a primitive type,
493
+ * such as int[], boolean[], etc. This method is handles the case where user
494
+ * code explicitly requests a primitive array by passing the class type
495
+ * to {@link #get(int, Class)} or {@link #get(String, Class)}.
496
+ */
479
497
private Object convertPrimitiveArray (
480
498
OracleArray oracleArray , Class <?> primitiveType ) {
481
499
try {
@@ -531,11 +549,11 @@ else if (double.class.equals(primitiveType)) {
531
549
}
532
550
533
551
/**
534
- * Converts a given {@code array} to an array of a specified
535
- * {@code desiredType}. This method handles arrays returned by
536
- * {@link Array#getArray(Map)}, which may contain objects that are the default
537
- * desiredType mapping for a JDBC driver. This method converts the default JDBC
538
- * desiredType mappings to the default R2DBC desiredType mappings.
552
+ * Converts a given {@code array} to an array of a {@code desiredType}. This
553
+ * method handles arrays returned by {@link Array#getArray(Map)}, which may
554
+ * contain objects that are the default desiredType mapping for a JDBC driver.
555
+ * This method converts the default JDBC desiredType mappings to the default
556
+ * R2DBC desiredType mappings.
539
557
*/
540
558
@ SuppressWarnings ("unchecked" )
541
559
private <T > T [] convertArray (Object [] array , Class <T > desiredType ) {
@@ -685,6 +703,7 @@ else if (oracle.sql.INTERVALDS.class.isAssignableFrom(elementType)
685
703
}
686
704
else if (oracle .jdbc .OracleArray .class .isAssignableFrom (elementType )
687
705
&& desiredType .isArray ()) {
706
+ // Recursively convert a multi-dimensional array.
688
707
return (T []) mapArray (
689
708
array ,
690
709
length ->
@@ -699,13 +718,32 @@ else if (oracle.jdbc.OracleArray.class.isAssignableFrom(elementType)
699
718
throw unsupportedArrayConversion (elementType , desiredType );
700
719
}
701
720
721
+ /**
722
+ * Returns an exception indicating a type of elements in a Java array can
723
+ * not be converted to a different type.
724
+ */
702
725
private static IllegalArgumentException unsupportedArrayConversion (
703
726
Class <?> fromType , Class <?> toType ) {
704
727
return new IllegalArgumentException (format (
705
728
"Conversion from array of %s to array of %s is not supported" ,
706
729
fromType .getName (), toType .getName ()));
707
730
}
708
731
732
+ /**
733
+ * <p>
734
+ * Maps the elements of a given {@code array} using a {@code mappingFunction},
735
+ * and returns an array generated by an {@code arrayAllocator} that stores the
736
+ * mapped elements.
737
+ * </p><p>
738
+ * The {@code array} may contain {@code null} elements. A {@code null} element
739
+ * is automatically converted to {@code null}, and not supplied as input to
740
+ * the {@code mappingFunction}.
741
+ * </p>
742
+ * @param array Array of elements to convert. Not null.
743
+ * @param arrayAllocator Allocates an array of an input length. Not null.
744
+ * @param mappingFunction Maps elements from the {@code array}. Not null.
745
+ * @return Array of mapped elements.
746
+ */
709
747
private static <T ,U > U [] mapArray (
710
748
T [] array , IntFunction <U []> arrayAllocator ,
711
749
Function <T , U > mappingFunction ) {
@@ -722,49 +760,6 @@ private static <T,U> U[] mapArray(
722
760
return result ;
723
761
}
724
762
725
- /**
726
- * Converts an array of {@code BigDecimal} values to objects of a given
727
- * type. This method handles the case where Oracle JDBC does not perform
728
- * conversions specified by the {@code Map} argument to
729
- * {@link Array#getArray(Map)}
730
- */
731
- private <T > T [] convertBigDecimalArray (
732
- BigDecimal [] bigDecimals , Class <T > type ) {
733
-
734
- final Function <BigDecimal , T > mapFunction ;
735
-
736
- if (type .equals (Byte .class )) {
737
- mapFunction = bigDecimal -> type .cast (bigDecimal .byteValue ());
738
- }
739
- else if (type .equals (Short .class )) {
740
- mapFunction = bigDecimal -> type .cast (bigDecimal .shortValue ());
741
- }
742
- else if (type .equals (Integer .class )) {
743
- mapFunction = bigDecimal -> type .cast (bigDecimal .intValue ());
744
- }
745
- else if (type .equals (Long .class )) {
746
- mapFunction = bigDecimal -> type .cast (bigDecimal .longValue ());
747
- }
748
- else if (type .equals (Float .class )) {
749
- mapFunction = bigDecimal -> type .cast (bigDecimal .floatValue ());
750
- }
751
- else if (type .equals (Double .class )) {
752
- mapFunction = bigDecimal -> type .cast (bigDecimal .doubleValue ());
753
- }
754
- else {
755
- throw new IllegalArgumentException (
756
- "Can not convert BigDecimal to " + type );
757
- }
758
-
759
- return Arrays .stream (bigDecimals )
760
- .map (mapFunction )
761
- .toArray (length -> {
762
- @ SuppressWarnings ("unchecked" )
763
- T [] array = (T [])java .lang .reflect .Array .newInstance (type , length );
764
- return array ;
765
- });
766
- }
767
-
768
763
/**
769
764
* Checks if the specified zero-based {@code index} is a valid column index
770
765
* for this row. This method is used to verify index value parameters
@@ -801,7 +796,7 @@ private static final class RowImpl
801
796
* determine the default type mapping of column values.
802
797
* </p>
803
798
* @param jdbcConnection JDBC connection that created the
804
- * {@code jdbcReadable}. Not null.*
799
+ * {@code jdbcReadable}. Not null.
805
800
* @param jdbcReadable Row data from the Oracle JDBC Driver. Not null.
806
801
* @param metadata Meta-data for the specified row. Not null.
807
802
* @param adapter Adapts JDBC calls into reactive streams. Not null.
0 commit comments