44
44
import oracle .r2dbc .impl .ReadablesMetadata .RowMetadataImpl ;
45
45
import oracle .sql .INTERVALDS ;
46
46
import oracle .sql .INTERVALYM ;
47
+ import oracle .sql .TIMESTAMPLTZ ;
48
+ import oracle .sql .TIMESTAMPTZ ;
47
49
48
50
import java .math .BigDecimal ;
49
51
import java .nio .ByteBuffer ;
50
52
import java .sql .Array ;
51
- import java .sql .Connection ;
52
- import java .sql .JDBCType ;
53
53
import java .sql .ResultSet ;
54
54
import java .sql .SQLException ;
55
55
import java .sql .Struct ;
63
63
import java .time .OffsetTime ;
64
64
import java .time .Period ;
65
65
import java .time .temporal .ChronoUnit ;
66
+ import java .util .Arrays ;
67
+ import java .util .List ;
66
68
import java .util .Map ;
67
69
import java .util .NoSuchElementException ;
70
+ import java .util .Objects ;
71
+ import java .util .TreeMap ;
68
72
import java .util .concurrent .TimeUnit ;
69
73
import java .util .function .Function ;
70
74
import java .util .function .IntFunction ;
@@ -277,18 +281,16 @@ else if (Object.class.equals(type)) {
277
281
: convert (index , defaultType );
278
282
}
279
283
else {
280
- ReadableMetadata metadata = readablesMetadata .get (index );
281
- if (type .isArray () && R2dbcType .COLLECTION .equals (metadata .getType ())) {
282
- // Note that a byte[] would be a valid mapping for a RAW column, so this
283
- // branch is only taken if the target type is an array, and the column
284
- // type is a SQL ARRAY (ie: COLLECTION).
285
- value = getJavaArray (index , type .getComponentType ());
284
+ Type sqlType = readablesMetadata .get (index ).getType ();
285
+
286
+ if (sqlType instanceof OracleR2dbcTypes .ArrayType ) {
287
+ value = getOracleArray (index , type );
286
288
}
287
- else if (type .isAssignableFrom (OracleR2dbcObject .class )
288
- && JDBCType .STRUCT .equals (metadata .getNativeTypeMetadata ())) {
289
- value = getOracleR2dbcObject (index );
289
+ else if (sqlType instanceof OracleR2dbcTypes .ObjectType ) {
290
+ value = getOracleObject (index , type );
290
291
}
291
292
else {
293
+ // Fallback to a built-in conversion supported by Oracle JDBC
292
294
value = jdbcReadable .getObject (index , type );
293
295
}
294
296
}
@@ -402,6 +404,22 @@ private LocalDateTime getLocalDateTime(int index) {
402
404
*/
403
405
}
404
406
407
+ /**
408
+ * Returns an ARRAY at the given {@code index} as a specified {@code type}.
409
+ * This method supports conversions to Java arrays of the default Java type
410
+ * mapping for the ARRAY's element type. This conversion is the standard type
411
+ * mapping for a COLLECTION, as defined by the R2DBC Specification.
412
+ */
413
+ private <T > T getOracleArray (int index , Class <T > type ) {
414
+ if (type .isArray ()) {
415
+ return type .cast (getJavaArray (index , type .getComponentType ()));
416
+ }
417
+ else {
418
+ // Fallback to a built-in conversion supported by Oracle JDBC
419
+ return jdbcReadable .getObject (index , type );
420
+ }
421
+ }
422
+
405
423
/**
406
424
* <p>
407
425
* Converts the value of a column at the specified {@code index} to a Java
@@ -623,7 +641,38 @@ private <T> T[] convertArray(Object[] array, Class<T> desiredType) {
623
641
(Function <Object , T >)getMappingFunction (elementType , desiredType ));
624
642
}
625
643
626
- private OracleR2dbcObject getOracleR2dbcObject (int index ) {
644
+ /**
645
+ * Converts the OBJECT column at a given {@code index} to the specified
646
+ * {@code type}. For symmetry with the object types supported by Statement
647
+ * bind methods, this method supports conversions to
648
+ * {@code OracleR2dbcObject}, {@code Object[]}, or
649
+ * {@code Map<String, Object>}.
650
+ */
651
+ private <T > T getOracleObject (int index , Class <T > type ) {
652
+
653
+ if (type .isAssignableFrom (OracleR2dbcObject .class )) {
654
+ // Support conversion to OracleR2dbcObject by default
655
+ return type .cast (getOracleR2dbcObject (index ));
656
+ }
657
+ else if (type .isAssignableFrom (Object [].class )) {
658
+ // Support conversion to Object[] for symmetry with bind types
659
+ return type .cast (toArray (getOracleR2dbcObject (index )));
660
+ }
661
+ else if (type .isAssignableFrom (Map .class )) {
662
+ // Support conversion to Map<String, Object> for symmetry with bind
663
+ // types
664
+ return type .cast (toMap (getOracleR2dbcObject (index )));
665
+ }
666
+ else {
667
+ // Fallback to a built-in conversion of Oracle JDBC
668
+ return jdbcReadable .getObject (index , type );
669
+ }
670
+ }
671
+
672
+ /**
673
+ * Returns the OBJECT at a given {@code index} as an {@code OracleR2dbcObject}
674
+ */
675
+ private OracleR2dbcObjectImpl getOracleR2dbcObject (int index ) {
627
676
628
677
OracleStruct oracleStruct =
629
678
jdbcReadable .getObject (index , OracleStruct .class );
@@ -639,6 +688,42 @@ private OracleR2dbcObject getOracleR2dbcObject(int index) {
639
688
adapter );
640
689
}
641
690
691
+ /**
692
+ * Returns of an array with the default mapping of each value in a
693
+ * {@code readable}
694
+ */
695
+ private static Object [] toArray (OracleReadableImpl readable ) {
696
+ if (readable == null )
697
+ return null ;
698
+
699
+ Object [] array =
700
+ new Object [readable .readablesMetadata .getList ().size ()];
701
+
702
+ for (int i = 0 ; i < array .length ; i ++)
703
+ array [i ] = readable .get (i );
704
+
705
+ return array ;
706
+ }
707
+
708
+ /**
709
+ * Returns a map containing the default mapping of each value in an
710
+ * {@code object}, keyed to the value's name.
711
+ */
712
+ private static Map <String , Object > toMap (OracleReadableImpl readable ) {
713
+ if (readable == null )
714
+ return null ;
715
+
716
+ List <? extends ReadableMetadata > metadataList =
717
+ readable .readablesMetadata .getList ();
718
+
719
+ Map <String , Object > map = new TreeMap <>(String .CASE_INSENSITIVE_ORDER );
720
+
721
+ for (int i = 0 ; i < metadataList .size (); i ++)
722
+ map .put (metadataList .get (i ).getName (), readable .get (i ));
723
+
724
+ return map ;
725
+ }
726
+
642
727
private <T ,U > Function <T ,U > getMappingFunction (
643
728
Class <T > fromType , Class <U > toType ) {
644
729
@@ -712,6 +797,37 @@ else if (toType.isAssignableFrom(LocalTime.class)) {
712
797
offsetDateTime .toLocalTime ();
713
798
}
714
799
}
800
+ else if (TIMESTAMPTZ .class .isAssignableFrom (fromType )) {
801
+ if (toType .isAssignableFrom (OffsetDateTime .class )) {
802
+ mappingFunction = (TIMESTAMPTZ timestampWithTimeZone ) ->
803
+ fromJdbc (() -> timestampWithTimeZone .toOffsetDateTime ());
804
+ }
805
+ else if (toType .isAssignableFrom (OffsetTime .class )) {
806
+ mappingFunction = (TIMESTAMPTZ timestampWithTimeZone ) ->
807
+ fromJdbc (() -> timestampWithTimeZone .toOffsetTime ());
808
+ }
809
+ else if (toType .isAssignableFrom (LocalDateTime .class )) {
810
+ mappingFunction = (TIMESTAMPTZ timestampWithTimeZone ) ->
811
+ fromJdbc (() -> timestampWithTimeZone .toLocalDateTime ());
812
+ }
813
+ }
814
+ else if (TIMESTAMPLTZ .class .isAssignableFrom (fromType )) {
815
+ if (toType .isAssignableFrom (OffsetDateTime .class )) {
816
+ mappingFunction = (TIMESTAMPLTZ timestampWithLocalTimeZone ) ->
817
+ fromJdbc (() ->
818
+ timestampWithLocalTimeZone .offsetDateTimeValue (jdbcConnection ));
819
+ }
820
+ else if (toType .isAssignableFrom (OffsetTime .class )) {
821
+ mappingFunction = (TIMESTAMPLTZ timestampWithLocalTimeZone ) ->
822
+ fromJdbc (() ->
823
+ timestampWithLocalTimeZone .offsetTimeValue (jdbcConnection ));
824
+ }
825
+ else if (toType .isAssignableFrom (LocalDateTime .class )) {
826
+ mappingFunction = (TIMESTAMPLTZ timestampWithLocalTimeZone ) ->
827
+ fromJdbc (() ->
828
+ timestampWithLocalTimeZone .localDateTimeValue (jdbcConnection ));
829
+ }
830
+ }
715
831
else if (INTERVALYM .class .isAssignableFrom (fromType )
716
832
&& toType .isAssignableFrom (Period .class )) {
717
833
mappingFunction = (INTERVALYM intervalym ) -> {
@@ -937,11 +1053,18 @@ public OutParametersMetadata getMetadata() {
937
1053
}
938
1054
}
939
1055
940
- private static final class OracleR2dbcObjectImpl
1056
+ private final class OracleR2dbcObjectImpl
941
1057
extends OracleReadableImpl implements OracleR2dbcObject {
942
1058
943
1059
private final OracleR2dbcObjectMetadata metadata ;
944
1060
1061
+ /**
1062
+ * JDBC readable that backs this object. It is retained with this field to
1063
+ * implement equals and hashcode without having to convert between the
1064
+ * default JDBC object mappings and those of R2DBC.
1065
+ */
1066
+ private final StructJdbcReadable structJdbcReadable ;
1067
+
945
1068
/**
946
1069
* <p>
947
1070
* Constructs a new set of out parameters that supplies values of a
@@ -950,22 +1073,53 @@ private static final class OracleR2dbcObjectImpl
950
1073
* </p>
951
1074
* @param jdbcConnection JDBC connection that created the
952
1075
* {@code jdbcReadable}. Not null.
953
- * @param jdbcReadable Readable values from a JDBC Driver. Not null.
1076
+ * @param structJdbcReadable Readable values from a JDBC Driver. Not null.
954
1077
* @param metadata Metadata of each value. Not null.
955
1078
* @param adapter Adapts JDBC calls into reactive streams. Not null.
956
1079
*/
957
1080
private OracleR2dbcObjectImpl (
958
- java .sql .Connection jdbcConnection , DependentCounter dependentCounter ,
959
- JdbcReadable jdbcReadable , OracleR2dbcObjectMetadataImpl metadata ,
1081
+ java .sql .Connection jdbcConnection ,
1082
+ DependentCounter dependentCounter ,
1083
+ StructJdbcReadable structJdbcReadable ,
1084
+ OracleR2dbcObjectMetadataImpl metadata ,
960
1085
ReactiveJdbcAdapter adapter ) {
961
- super (jdbcConnection , dependentCounter , jdbcReadable , metadata , adapter );
1086
+ super (
1087
+ jdbcConnection , dependentCounter , structJdbcReadable , metadata , adapter );
962
1088
this .metadata = metadata ;
1089
+ this .structJdbcReadable = structJdbcReadable ;
963
1090
}
964
1091
965
1092
@ Override
966
1093
public OracleR2dbcObjectMetadata getMetadata () {
967
1094
return metadata ;
968
1095
}
1096
+
1097
+ @ Override
1098
+ public boolean equals (Object other ) {
1099
+ if (!(other instanceof OracleR2dbcObjectImpl ))
1100
+ return super .equals (other );
1101
+
1102
+ OracleR2dbcObjectImpl otherObject = (OracleR2dbcObjectImpl ) other ;
1103
+ if (! readablesMetadata .equals (otherObject .metadata ))
1104
+ return false ;
1105
+
1106
+ return Arrays .deepEquals (
1107
+ structJdbcReadable .attributes ,
1108
+ otherObject .structJdbcReadable .attributes );
1109
+ }
1110
+
1111
+ @ Override
1112
+ public int hashCode () {
1113
+ return Objects .hash (readablesMetadata , structJdbcReadable );
1114
+ }
1115
+
1116
+ @ Override
1117
+ public String toString () {
1118
+ return format (
1119
+ "%s = %s" ,
1120
+ metadata .getObjectType ().getName (),
1121
+ toMap (this ));
1122
+ }
969
1123
}
970
1124
971
1125
private final class StructJdbcReadable implements JdbcReadable {
0 commit comments