Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 42 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ Oracle R2DBC implements the R2DBC Service Provider Interface (SPI) as specified
### Learn More About R2DBC:
[R2DBC Project Home Page](https://r2dbc.io)

[R2DBC Javadocs v0.9.0.M1](https://r2dbc.io/spec/0.9.0.M1/api/)
[R2DBC Javadocs v0.9.0.M2](https://r2dbc.io/spec/0.9.0.M2/api/)

[R2DBC Specification v0.9.0.M1](https://r2dbc.io/spec/0.9.0.M1/spec/html/)
[R2DBC Specification v0.9.0.M2](https://r2dbc.io/spec/0.9.0.M2/spec/html/)

### Learn More About Reactive Streams:
[Reactive Streams Project Home Page](http://www.reactive-streams.org)
Expand All @@ -19,14 +19,26 @@ Oracle R2DBC implements the R2DBC Service Provider Interface (SPI) as specified

[Reactive Streams Specification v1.0.3](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md)
# About This Version
Oracle R2DBC 0.2.0 updates the implemented SPI version to 0.9.0.M1. With the
0.9.0.M1 SPI update, Oracle R2DBC 0.2.0 introduces support for procedural
calls (PL/SQL), the ```Statement.bind(...)``` methods are enhanced to accept
```io.r2dbc.spi.Parameter``` objects, and the
```Connection.beginTransaction(TransactionDefintion)``` method is
implemented to support named and read-only/read-write transactions.

# Performance Goals
The 0.3.0 release Oracle R2DBC implements version 0.9.0.M2 of the R2DBC SPI.
The 0.9.0.M2 SPI update introduces support for consuming a `Result` as a
stream of `Segment` objects, configuring statement execution timeouts, and
managing pooled `Connection`s with the `LifeCycle` interface.

The 0.3.0 release updates the Oracle JDBC dependency to 21.3. The 21.3 release
introduces several improvements for the Reactive Extensions:
- Substantial reduction in object allocation costs.
- Row mapping functions will no longer contend with other threads for
access to the JDBC connection.
- Blocking database calls are no longer required for PreparedStatements
returning values generated by DML.

### Spring Integration
Use the 0.1.0 version of Oracle R2DBC if you are programming with Spring.
The later versions of Oracle R2DBC implement the 0.9.x versions of the R2DBC
SPI. Currently, Spring only supports drivers that implement the 0.8.x versions
of the SPI.

### Performance Goals
The primary goal of these early releases of Oracle R2DBC is to support the R2DBC
SPI on Oracle Database. The only performance goal is to enable concurrent
database calls to be executed by a single thread.
Expand All @@ -48,18 +60,20 @@ Artifacts can also be found on Maven Central.
<dependency>
<groupId>com.oracle.database.r2dbc</groupId>
<artifactId>oracle-r2dbc</artifactId>
<version>${version}</version>
<version>0.3.0</version>
</dependency>
```

Oracle R2DBC is compatible with JDK 11 (or newer), and has the following runtime dependencies:
- R2DBC SPI 0.9.0.M1
- R2DBC SPI 0.9.0.M2
- Reactive Streams 1.0.3
- Project Reactor 3.0.0
- Oracle JDBC 21.1.0.0 for JDK 11 (ojdbc11.jar)
- Oracle R2DBC relies on the Oracle JDBC Driver's [Reactive Extensions](https://docs.oracle.com/en/database/oracle/oracle-database/21/jjdbc/jdbc-reactive-extensions.html#GUID-1C40C43B-3823-4848-8B5A-D2F97A82F79B) APIs. These APIs were introduced in the 21.1 release of Oracle JDBC, and are only available with the JDK 11 build (ojdbc11).
- Project Reactor 3.3.0.RELEASE
- Oracle JDBC 21.3.0.0 for JDK 11 (ojdbc11.jar)
- Oracle R2DBC relies on the Oracle JDBC Driver's [Reactive Extensions
](https://docs.oracle.com/en/database/oracle/oracle-database/21/jjdbc/jdbc-reactive-extensions.html#GUID-1C40C43B-3823-4848-8B5A-D2F97A82F79B) APIs.

The Oracle R2DBC Driver has been verified with Oracle Database versions 19c and 21c.
The Oracle R2DBC Driver has been verified with Oracle Database versions 18, 19,
and 21.

# Code Examples

Expand All @@ -74,7 +88,7 @@ Mono.from(connectionFactory.create())
"SELECT 'Hello, Oracle' FROM sys.dual")
.execute())
.flatMap(result ->
result.map((row, metadata) -> row.get(0, String.class)))
result.map(row -> row.get(0, String.class)))
.doOnNext(System.out::println)
.thenMany(connection.close()))
.subscribe();
Expand All @@ -90,7 +104,7 @@ Mono.from(connectionFactory.create())
.bind("locale_name", "France")
.execute())
.flatMap(result ->
result.map((row, metadata) ->
result.map(row ->
String.format("%s, Oracle", row.get("greeting", String.class))))
.doOnNext(System.out::println)
.thenMany(connection.close()))
Expand Down Expand Up @@ -128,8 +142,8 @@ This document specifies the behavior of the R2DBC SPI implemented for the
Oracle Database. This SPI implementation is referred to as the "Oracle R2DBC
Driver" or "Oracle R2DBC" throughout the remainder of this document.

The Oracle R2DBC Driver implements behavior specified by the R2DBC 0.9.0.M1
[Specification](https://r2dbc.io/spec/0.9.0.M1/spec/html/)
The Oracle R2DBC Driver implements behavior specified by the R2DBC 0.9.0.M2
[Specification](https://r2dbc.io/spec/0.9.0.M2/spec/html/)
and [Javadoc](https://r2dbc.io/spec/0.8.3.RELEASE/api/)

Publisher objects created by Oracle R2DBC implement behavior specified by
Expand All @@ -146,8 +160,8 @@ implements a ConnectionFactoryProvider located by an R2DBC URL identifing
"oracle" as a driver, or by a DRIVER ConnectionFactoryOption with the value
of "oracle".
- The following well-known ConnectionFactory Options are supported:
DRIVER, USER, PASSWORD, HOST, PORT, DATABASE, SSL, and
CONNECT_TIMEOUT.
DRIVER, USER, PASSWORD, HOST, PORT, DATABASE, SSL,
CONNECT_TIMEOUT, STATEMENT_TIMEOUT.
- The DATABASE ConnectionFactoryOption is interpreted as the
[service name](https://docs.oracle.com/en/database/oracle/oracle-database/21/netag/identifying-and-accessing-database.html#GUID-153861C1-16AD-41EC-A179-074146B722E6) of an Oracle Database instance.
System Identifiers (SID) are not recognized.
Expand Down Expand Up @@ -175,7 +189,7 @@ Options. For Options having any of the following names, a CharSequence value may
- [oracle.jdbc.implicitStatementCacheSize](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html?is-external=true#CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE)
- [oracle.jdbc.defaultLobPrefetchSize](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html?is-external=true#CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE)
- [oracle.net.disableOob](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html?is-external=true#CONNECTION_PROPERTY_THIN_NET_DISABLE_OUT_OF_BAND_BREAK)
- Out of band (oob) breaks effect statement timeouts. Set this to "true"
- Out of band (OOB) breaks effect statement timeouts. Set this to "true"
if statement timeouts are not working correctly.
- Oracle Net Descriptors of the form ```(DESCRIPTION=...)``` may be specified as an io.r2dbc.spi.Option having the name `oracleNetDescriptor`.
- If `oracleNetDescriptor` is specified, then it is invalid to specify any other options that might conflict with information in the descriptor, such as: `HOST`, `PORT`, `DATABASE`, and `SSL`.
Expand Down Expand Up @@ -210,8 +224,9 @@ or Oracle JDBC Driver error message](https://docs.oracle.com/en/database/oracle/
- READ COMMITTED is the default transaction isolation level, and is the
only level supported in this release.
- Transaction savepoints are not supported in this release.
- TransactionDefinition.LOCK_WAIT_TIMEOUT is not supported in this release.
- Oracle Database does not support a lock wait timeout that applies to all statements within a transaction.
- Oracle Database does not support a lock wait timeout that is configurable
within the scope of a transaction or session. SPI methods that configure a
lock wait timeout throw ```UnsupportedOperationException```

### Statements
- Batch execution is only supported for DML type SQL commands (INSERT/UPDATE/DELETE).
Expand All @@ -229,12 +244,6 @@ of each row affected by an INSERT or UPDATE.
- The ROWID of a row may change.
- After a row is deleted, its ROWID may be reassigned to a new row.
- Further Reading: https://asktom.oracle.com/pls/apex/asktom.search?tag=is-it-safe-to-use-rowid-to-locate-a-row
- A **blocking database call** is executed by a Statement returning generated
values for a non-empty set of column names.
- The **blocking database call** is a known limitation that will be resolved
with a non-blocking implementation of
java.sql.Connection.prepareStatement(String, String[]) in the Oracle JDBC Driver.
The Oracle JDBC Team is aware of this problem and is working on a fix.
- Returning generated values is only supported for INSERT and UPDATE commands when a RETURNING INTO clause can be appended to the end of that command. (This limitation may be resolved in a later release)
- Example: `INSERT INTO my_table(val) VALUES (:val)` is supported because a RETURNING INTO clause may be appended to this command.
- Example: `INSERT INTO my_table(val) SELECT 1 FROM sys.dual` is not supported because a RETURNING INTO clause may not be appended to this command.
Expand All @@ -260,9 +269,9 @@ values for a non-empty set of column names.
```io.r2dbc.spi.Parameter.Out``` and
```io.r2dbc.spi.Parameter.In``` marker interfaces.
- Consume out parameters by invoking
```Result.map(BiFunction<Row, RowMetadata>)```:
```Result.map(Function)```:
```java
result.map((row,metadata) -> row.get("greeting_out", String.class))
result.map(outParameters -> outParameters.get("greeting_out", String.class))
```
- ```Statement.execute()``` returns a ```Publisher<Result>``` that emits one
```Result``` for each cursor returned by ```DBMS_SQL.RETURN_RESULT```
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

<groupId>com.oracle.database.r2dbc</groupId>
<artifactId>oracle-r2dbc</artifactId>
<version>0.2.0</version>
<version>0.3.0</version>
<name>oracle-r2dbc</name>
<description>
Oracle R2DBC Driver implementing version 0.9.0.M2 of the R2DBC SPI for Oracle Database.
Expand Down Expand Up @@ -65,7 +65,7 @@

<properties>
<java.version>11</java.version>
<ojdbc.version>21.1.0.0</ojdbc.version>
<ojdbc.version>21.3.0.0</ojdbc.version>
<r2dbc.version>0.9.0.M2</r2dbc.version>
<reactor.version>3.3.0.RELEASE</reactor.version>
<reactive-streams.version>1.0.3</reactive-streams.version>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
with oracle.r2dbc.impl.OracleConnectionFactoryProviderImpl;

requires java.sql;
requires ojdbc11;
requires com.oracle.database.jdbc;
requires reactor.core;
requires transitive org.reactivestreams;
requires transitive r2dbc.spi;
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/oracle/r2dbc/impl/OracleConnectionFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,23 @@ final class OracleConnectionFactoryImpl implements ConnectionFactory {
OracleR2dbcExceptions.requireNonNull(options, "options is null.");
adapter = ReactiveJdbcAdapter.getOracleAdapter();
dataSource = adapter.createDataSource(options);

// Handle any Options that Oracle JDBC doesn't
if (options.hasOption(ConnectionFactoryOptions.LOCK_WAIT_TIMEOUT)) {
throw new UnsupportedOperationException(
"Unsupported Option: "
+ ConnectionFactoryOptions.LOCK_WAIT_TIMEOUT.name()
+ ". Oracle Database does not support a lock wait timeout session " +
"parameter.");
}

statementTimeout = Optional.ofNullable(
options.getValue(ConnectionFactoryOptions.STATEMENT_TIMEOUT))
.map(timeout -> (timeout instanceof Duration)
? (Duration)timeout
: Duration.parse(timeout.toString()))
.orElse(Duration.ZERO);

}

/**
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/oracle/r2dbc/impl/OracleResultImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,16 @@ static OracleResultImpl createUpdateCountResult(long updateCount) {
return new UpdateCountResult(updateCount);
}

/**
* Creates a {@code Result} that publishes a batch of {@code updateCounts}
* as {@link UpdateCount} segments
* @return A {@code Result} for a batch DML update
* @param updateCounts Update counts to publish
*/
static OracleResultImpl createBatchUpdateResult(long[] updateCounts) {
return new BatchUpdateResult(updateCounts);
}

/**
* Creates a {@code Result} that publishes update counts of a
* {@code batchUpdateException} as {@link UpdateCount} segments, followed a
Expand Down Expand Up @@ -444,18 +454,17 @@ <T> Publisher<T> publishSegments(Function<Segment, T> mappingFunction) {
private static final class ResultSetResult extends OracleResultImpl {

private final ResultSet resultSet;
private final RowMetadataImpl metadata;
private final ReactiveJdbcAdapter adapter;

private ResultSetResult(ResultSet resultSet, ReactiveJdbcAdapter adapter) {
this.resultSet = resultSet;
this.metadata = createRowMetadata(fromJdbc(resultSet::getMetaData));
this.adapter = adapter;
}

@Override
<T> Publisher<T> publishSegments(Function<Segment, T> mappingFunction) {
RowMetadataImpl metadata =
createRowMetadata(fromJdbc(resultSet::getMetaData));

return adapter.publishRows(resultSet, jdbcReadable ->
mappingFunction.apply(
new RowSegmentImpl(createRow(jdbcReadable, metadata, adapter))));
Expand Down Expand Up @@ -595,7 +604,9 @@ <T> Publisher<T> publishSegments(Function<Segment, T> mappingFunction) {
// Invoke publishSegments(Class, Function) rather than
// publishSegments(Function) to update the state of the result; Namely,
// the state that has the onConsumed Publisher emit a terminal signal.
.concatWith(result.publishSegments(Segment.class, mappingFunction));
.concatWith(result != null
? result.publishSegments(Segment.class,mappingFunction)
: Mono.empty());
}
}

Expand Down
Loading