Skip to content

Commit edca66f

Browse files
Adding LocalDateTime conversion and some code clean up
1 parent 06e1999 commit edca66f

File tree

4 files changed

+80
-52
lines changed

4 files changed

+80
-52
lines changed

src/main/java/oracle/r2dbc/impl/OracleColumnMetadataImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,10 @@ static OracleColumnMetadataImpl fromResultSetMetaData(
204204

205205
case Types.DATE:
206206
// Override JDBC's java.sql.Date type mapping to use LocalDate
207-
return newMetadata(
208-
LocalDate.class, resultSetMetaData, jdbcIndex);
207+
return newMetadata(LocalDate.class, resultSetMetaData, jdbcIndex);
209208

210-
case OracleTypes.TIMESTAMPLTZ:
211209
case Types.TIMESTAMP:
210+
case OracleTypes.TIMESTAMPLTZ:
212211
// Override JDBC's java.sql.Timestamp type mapping, and Oracle JDBC's
213212
// oracle.sql.TIMESTAMPLTZ type mapping to use LocalDateTime, and to
214213
// use an appropriate precision value. Note that the Oracle type

src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525
import io.r2dbc.spi.Option;
2626
import io.r2dbc.spi.R2dbcException;
2727
import io.r2dbc.spi.R2dbcTimeoutException;
28-
import io.r2dbc.spi.Result;
29-
import io.r2dbc.spi.Row;
3028
import oracle.jdbc.OracleBlob;
3129
import oracle.jdbc.OracleClob;
3230
import oracle.jdbc.OracleConnection;
@@ -49,17 +47,12 @@
4947
import java.sql.Connection;
5048
import java.sql.PreparedStatement;
5149
import java.sql.ResultSet;
52-
import java.sql.RowId;
5350
import java.sql.SQLException;
54-
import java.sql.Statement;
55-
import java.sql.Types;
5651
import java.sql.Wrapper;
5752
import java.time.Duration;
58-
import java.util.Map;
5953
import java.util.Objects;
6054
import java.util.Set;
6155
import java.util.concurrent.CompletableFuture;
62-
import java.util.concurrent.ConcurrentHashMap;
6356
import java.util.concurrent.Flow;
6457
import java.util.concurrent.atomic.AtomicBoolean;
6558
import java.util.concurrent.locks.ReentrantLock;

src/main/java/oracle/r2dbc/impl/OracleRowImpl.java

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@
2525
import io.r2dbc.spi.Clob;
2626
import io.r2dbc.spi.R2dbcException;
2727
import io.r2dbc.spi.Row;
28-
import oracle.jdbc.OracleType;
28+
import oracle.jdbc.OracleTypes;
2929

30-
import java.io.IOException;
3130
import java.nio.ByteBuffer;
32-
import java.sql.JDBCType;
3331
import java.sql.ResultSet;
34-
import java.sql.SQLType;
32+
import java.sql.Timestamp;
3533
import java.sql.Types;
36-
import java.util.Objects;
34+
import java.time.LocalDateTime;
3735

3836
import static oracle.r2dbc.impl.OracleR2dbcExceptions.requireNonNull;
3937

@@ -42,7 +40,7 @@
4240
* Implementation of the {@link Row} SPI for Oracle Database.
4341
* </p><p>
4442
* Instances of this class supply the column values of a
45-
* {@link ReactiveJdbcAdapter.JdbcRow} which represents one row of data
43+
* {@link ReactiveJdbcAdapter.JdbcRow} that represents one row of data
4644
* from a JDBC {@link ResultSet}.
4745
* </p>
4846
*
@@ -62,7 +60,7 @@ final class OracleRowImpl implements Row {
6260

6361
/**
6462
* <p>
65-
* Constructs a new row which supplies column values from the specified
63+
* Constructs a new row that supplies column values from the specified
6664
* {@code jdbcRow}, and uses the specified {@code rowMetadata} to determine
6765
* the default type mapping of column values.
6866
* </p>
@@ -112,7 +110,7 @@ public Object get(int index) {
112110
/**
113111
* {@inheritDoc}
114112
* <p>
115-
* Implements the R2DBC SPI method by using the JDBC ResultSet which backs
113+
* Implements the R2DBC SPI method by using the JDBC ResultSet that backs
116114
* this row to convert the specified column value into the specified {@code
117115
* type}.
118116
* </p><p>
@@ -145,7 +143,7 @@ public <T> T get(int index, Class<T> type) {
145143
/**
146144
* {@inheritDoc}
147145
* <p>
148-
* Implements the R2DBC SPI method by using the JDBC ResultSet which backs
146+
* Implements the R2DBC SPI method by using the JDBC ResultSet that backs
149147
* this row to convert the specified column value into the Oracle R2DBC
150148
* Driver's default Java type mapping for the column's SQL type.
151149
* </p><p>
@@ -181,7 +179,7 @@ public Object get(String name) {
181179
/**
182180
* {@inheritDoc}
183181
* <p>
184-
* Implements the R2DBC SPI method by using the JDBC ResultSet which backs
182+
* Implements the R2DBC SPI method by using the JDBC ResultSet that backs
185183
* this row to convert the specified column value into the specified {@code
186184
* type}.
187185
* </p><p>
@@ -234,7 +232,7 @@ private int getColumnIndex(String name) {
234232
* Converts the value of a column at a specified {@code index} to the
235233
* specified {@code type}. This method implements conversions to target
236234
* types that are not supported by JDBC drivers. The Oracle R2DBC Driver
237-
* will implement some conversions which adapt JDBC supported types into
235+
* will implement some conversions that adapt JDBC supported types into
238236
* R2DBC supported types.
239237
*
240238
* @param index 0-based index of a column
@@ -252,6 +250,8 @@ else if (type.equals(io.r2dbc.spi.Blob.class))
252250
return type.cast(getBlob(index));
253251
else if (type.equals(io.r2dbc.spi.Clob.class))
254252
return type.cast(getClob(index));
253+
else if (type.equals(LocalDateTime.class))
254+
return type.cast(getLocalDateTime(index));
255255
else
256256
return jdbcRow.getObject(index, type);
257257
}
@@ -263,7 +263,7 @@ else if (type.equals(io.r2dbc.spi.Clob.class))
263263
* </p><p>
264264
* A JDBC driver is not required to support {@code ByteBuffer} conversions
265265
* for any SQL type, so this method is necessary to implement the
266-
* conversion to {@code ByteBuffer} from a type which is supported by JDBC.
266+
* conversion to {@code ByteBuffer} from a type that is supported by JDBC.
267267
* </p><p>
268268
* This method should NOT be called when the database column type is BLOB.
269269
* The JDBC driver may require blocking network I/O in order to materialize a
@@ -285,7 +285,7 @@ private ByteBuffer getByteBuffer(int index) {
285285
* </p><p>
286286
* A JDBC driver is not required to support {@code io.r2dbc.spi.Blob}
287287
* conversions for any SQL type, so this method is necessary to implement the
288-
* conversion to {@code Blob} from a type which is supported by JDBC.
288+
* conversion to {@code Blob} from a type that is supported by JDBC.
289289
* </p>
290290
* @param index 0 based column index
291291
* @return A column value as a {@code Blob}, or null if the column
@@ -307,27 +307,27 @@ private Blob getBlob(int index) {
307307
* </p><p>
308308
* A JDBC driver is not required to support {@code io.r2dbc.spi.Clob}
309309
* conversions for any SQL type, so this method is necessary to implement the
310-
* conversion to {@code Clob} from a type which is supported by JDBC.
310+
* conversion to {@code Clob} from a type that is supported by JDBC.
311311
* </p>
312312
* @param index 0 based column index
313313
* @return A column value as a {@code Clob}, or null if the column
314314
* value is NULL.
315315
*/
316316
private Clob getClob(int index) {
317-
Integer columnTypeCode =
318-
rowMetadata.getColumnMetadata(index)
319-
.getNativeTypeMetadata()
320-
.getVendorTypeNumber();
321317

322-
boolean isNChar = columnTypeCode != null &&
323-
(columnTypeCode == Types.NCLOB
324-
|| columnTypeCode == Types.NCHAR
325-
|| columnTypeCode == Types.NVARCHAR
326-
|| columnTypeCode == Types.LONGNVARCHAR);
327-
328-
java.sql.Clob jdbcClob = isNChar
329-
? jdbcRow.getObject(index, java.sql.NClob.class)
330-
: jdbcRow.getObject(index, java.sql.Clob.class);
318+
// Convert to a JDBC NClob or Clob, depending on the column type
319+
final java.sql.Clob jdbcClob;
320+
switch (getColumnTypeNumber(index)) {
321+
case Types.NCLOB:
322+
case Types.LONGNVARCHAR:
323+
case Types.NVARCHAR:
324+
case Types.NCHAR:
325+
jdbcClob = jdbcRow.getObject(index, java.sql.NClob.class);
326+
break;
327+
default:
328+
jdbcClob = jdbcRow.getObject(index, java.sql.Clob.class);
329+
break;
330+
}
331331

332332
return jdbcClob == null
333333
? null
@@ -336,6 +336,35 @@ private Clob getClob(int index) {
336336
adapter.publishClobFree(jdbcClob));
337337
}
338338

339+
/**
340+
* <p>
341+
* Converts the value of a column at the specified {@code index} to a
342+
* {@code LocalDateTime}.
343+
* </p><p>
344+
* A JDBC driver is not required to support {@code LocalDateTime} conversions
345+
* for any SQL type. The Oracle JDBC driver is known to support {@code
346+
* LocalDateTime} conversions for DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE.
347+
* </p><p>
348+
* The 21.1 Oracle JDBC Driver does not implement a correct conversion for
349+
* TIMESTAMP WITH LOCAL TIME ZONE; The driver returns a value in the database
350+
* timezone rather than the session time zone. A correct conversion is
351+
* implemented for {@code java.sql.Timestamp}, so this method is implemented
352+
* to convert that into a {@code LocalDateTime}.
353+
* </p>
354+
* @param index 0 based column index
355+
* @return A column value as a {@code Clob}, or null if the column
356+
* value is NULL.
357+
*/
358+
private LocalDateTime getLocalDateTime(int index) {
359+
if (getColumnTypeNumber(index) == OracleTypes.TIMESTAMPLTZ) {
360+
Timestamp timestamp = jdbcRow.getObject(index, Timestamp.class);
361+
return timestamp == null ? null : timestamp.toLocalDateTime();
362+
}
363+
else {
364+
return jdbcRow.getObject(index, LocalDateTime.class);
365+
}
366+
}
367+
339368
/**
340369
* Checks if the specified zero-based {@code index} is a valid column index
341370
* for this row. This method is used to verify index value parameters
@@ -361,7 +390,7 @@ else if (index >= rowMetadata.getColumnNames().size()) {
361390
* as {@code type}.
362391
* </p><p>
363392
* This method handles cases where the JDBC driver may support a mapping
364-
* which the Oracle R2DBC Driver does not support. For instance, the JDBC
393+
* that the Oracle R2DBC Driver does not support. For instance, the JDBC
365394
* driver may support mapping CLOB columns to String, but the Oracle R2DBC
366395
* Driver does not support this as the JDBC driver may require blocking
367396
* network I/O to convert a CLOB into a String.
@@ -372,15 +401,8 @@ else if (index >= rowMetadata.getColumnNames().size()) {
372401
* @throws R2dbcException if the type mapping is not supported
373402
*/
374403
private void requireSupportedTypeMapping(int index, Class<?> type) {
375-
Integer sqlTypeCode =
376-
rowMetadata.getColumnMetadata(index)
377-
.getNativeTypeMetadata()
378-
.getVendorTypeNumber();
379-
380-
if (sqlTypeCode == null)
381-
return;
382404

383-
switch (sqlTypeCode) {
405+
switch (getColumnTypeNumber(index)) {
384406
case Types.BLOB:
385407
if (! type.equals(Blob.class))
386408
throw unsupportedTypeMapping("BLOB", index, type);
@@ -392,6 +414,23 @@ private void requireSupportedTypeMapping(int index, Class<?> type) {
392414
}
393415
}
394416

417+
/**
418+
* Returns the SQL type number of the column at a given {@code index}. The
419+
* returned number identifies either a standard type defined by
420+
* {@link Types} or a vendor specific type defined by the JDBC driver. This
421+
* method returns {@link Types#OTHER} if the JDBC driver does not provide a
422+
* type code for the column.
423+
* @param index 0 based column index
424+
* @return A SQL type number
425+
*/
426+
private int getColumnTypeNumber(int index) {
427+
Integer typeNumber = rowMetadata.getColumnMetadata(index)
428+
.getNativeTypeMetadata()
429+
.getVendorTypeNumber();
430+
431+
return typeNumber == null ? Types.OTHER : typeNumber;
432+
}
433+
395434
/**
396435
* Returns an exception indicating that the Oracle R2DBC Driver does
397436
* not support mapping the database type specified as {@code sqlTypeName}
@@ -401,7 +440,7 @@ private void requireSupportedTypeMapping(int index, Class<?> type) {
401440
* @param type Java type to which mapping is not supported
402441
* @return An exception that expresses the unsupported mapping
403442
*/
404-
private R2dbcException unsupportedTypeMapping(
443+
private static R2dbcException unsupportedTypeMapping(
405444
String sqlTypeName, int index, Class<?> type) {
406445
return OracleR2dbcExceptions.newNonTransientException(
407446
String.format("Unsupported SQL to Java type mapping. " +

src/test/java/oracle/r2dbc/impl/TypeMappingTest.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,9 @@
4242
import java.time.Month;
4343
import java.time.OffsetDateTime;
4444
import java.time.Period;
45+
import java.time.ZoneId;
4546
import java.time.ZoneOffset;
4647
import java.time.temporal.ChronoUnit;
47-
import java.util.Base64;
48-
import java.util.Optional;
4948
import java.util.function.BiConsumer;
5049
import java.util.stream.Collectors;
5150
import java.util.stream.Stream;
@@ -60,8 +59,6 @@
6059
import static oracle.r2dbc.util.Awaits.awaitOne;
6160
import static oracle.r2dbc.util.Awaits.awaitUpdate;
6261
import static org.junit.jupiter.api.Assertions.assertEquals;
63-
import static org.junit.jupiter.api.Assertions.assertNull;
64-
import static org.junit.jupiter.api.Assertions.assertTrue;
6562

6663
/**
6764
* <p>
@@ -460,7 +457,7 @@ private static <T> void verifyTypeMapping(
460457
"SELECT javaValue FROM "+table+" WHERE javaValue IS NOT NULL")
461458
.execute())
462459
.flatMap(result ->
463-
result.map((row, metadata) -> row.get("javaValue"))
460+
result.map((row, metadata) -> row.get("javaValue"))
464461
)));
465462

466463
awaitOne(true,

0 commit comments

Comments
 (0)