Skip to content

Commit 4c80abe

Browse files
Merge pull request #101 from oracle/array-types
Array Types
2 parents 4b68ec7 + d0694d5 commit 4c80abe

11 files changed

+2267
-121
lines changed

.github/workflows/test.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,6 @@ echo "HOST=localhost" >> src/test/resources/config.properties
8181
echo "PORT=1521" >> src/test/resources/config.properties
8282
echo "USER=test" >> src/test/resources/config.properties
8383
echo "PASSWORD=test" >> src/test/resources/config.properties
84-
echo "CONNECT_TIMEOUT=120" >> src/test/resources/config.properties
85-
echo "SQL_TIMEOUT=120" >> src/test/resources/config.properties
84+
echo "CONNECT_TIMEOUT=180" >> src/test/resources/config.properties
85+
echo "SQL_TIMEOUT=180" >> src/test/resources/config.properties
8686
mvn clean compile test

README.md

+49-1
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,55 @@ prefetched entirely, a smaller prefetch size can be configured using the
570570
option, and the LOB can be consumed as a stream. By mapping LOB columns to
571571
`Blob` or `Clob` objects, the content can be consumed as a reactive stream.
572572

573-
### REF Cursors
573+
### ARRAY
574+
Oracle Database supports `ARRAY` as a user defined type only. A `CREATE TYPE`
575+
command is used to define an `ARRAY` type:
576+
```sql
577+
CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
578+
```
579+
Oracle R2DBC defines `oracle.r2dbc.OracleR2dbcType.ArrayType` as a `Type` for
580+
representing user defined `ARRAY` types. A `Parameter` with a type of
581+
`ArrayType` must be used when binding array values to a `Statement`.
582+
```java
583+
Publisher<Result> arrayBindExample(Connection connection) {
584+
Statement statement =
585+
connection.createStatement("INSERT INTO example VALUES (:array_bind)");
586+
587+
// Use the name defined for an ARRAY type:
588+
// CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
589+
ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
590+
Integer[] arrayValues = {1, 2, 3};
591+
statement.bind("arrayBind", Parameters.in(arrayType, arrayValues));
592+
593+
return statement.execute();
594+
}
595+
```
596+
A `Parameter` with a type of `ArrayType` must also be used when binding OUT
597+
parameters of a PL/SQL call.
598+
```java
599+
Publisher<Result> arrayOutBindExample(Connection connection) {
600+
Statement statement =
601+
connection.createStatement("BEGIN; exampleCall(:array_bind); END;");
602+
603+
// Use the name defined for an ARRAY type:
604+
// CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
605+
ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
606+
statement.bind("arrayBind", Parameters.out(arrayType));
607+
608+
return statement.execute();
609+
}
610+
```
611+
`ARRAY` values may be consumed from a `Row` or `OutParameter` as a Java array.
612+
The element type of the Java array may be any Java type that is supported as
613+
a mapping for the SQL type of the `ARRAY`. For instance, if the `ARRAY` type is
614+
`NUMBER`, then a `Integer[]` mapping is supported:
615+
```java
616+
Publisher<Integer[]> arrayMapExample(Result result) {
617+
return result.map(readable -> readable.get("arrayValue", Integer[].class));
618+
}
619+
```
620+
621+
### REF Cursor
574622
Use the `oracle.r2dbc.OracleR2dbcTypes.REF_CURSOR` type to bind `SYS_REFCURSOR` out
575623
parameters:
576624
```java

src/main/java/oracle/r2dbc/OracleR2dbcTypes.java

+97-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
*/
2121
package oracle.r2dbc;
2222

23+
import io.r2dbc.spi.Parameter;
24+
import io.r2dbc.spi.R2dbcType;
2325
import io.r2dbc.spi.Result;
26+
import io.r2dbc.spi.Statement;
2427
import io.r2dbc.spi.Type;
2528
import oracle.sql.json.OracleJsonObject;
2629

@@ -29,6 +32,7 @@
2932
import java.time.Duration;
3033
import java.time.LocalDateTime;
3134
import java.time.Period;
35+
import java.util.Objects;
3236

3337
/**
3438
* SQL types supported by Oracle Database that are not defined as standard types
@@ -99,10 +103,102 @@ private OracleR2dbcTypes() {}
99103
public static final Type REF_CURSOR =
100104
new TypeImpl(Result.class, "SYS_REFCURSOR");
101105

106+
/**
107+
* <p>
108+
* Creates an {@link ArrayType} representing a user defined {@code ARRAY}
109+
* type. The {@code name} passed to this method must identify the name of a
110+
* user defined {@code ARRAY} type.
111+
* </p><p>
112+
* Typically, the name passed to this method should be UPPER CASE, unless the
113+
* {@code CREATE TYPE} command that created the type used an "enquoted" type
114+
* name.
115+
* </p><p>
116+
* The {@code ArrayType} object returned by this method may be used to create
117+
* a {@link Parameter} that binds an array value to a {@link Statement}.
118+
* </p><pre>{@code
119+
* Publisher<Result> arrayBindExample(Connection connection) {
120+
* Statement statement =
121+
* connection.createStatement("INSERT INTO example VALUES (:array_bind)");
122+
*
123+
* // Use the name defined for an ARRAY type:
124+
* // CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
125+
* ArrayType arrayType = OracleR2dbcTypes.arrayType("MY_ARRAY");
126+
* Integer[] arrayValues = {1, 2, 3};
127+
* statement.bind("arrayBind", Parameters.in(arrayType, arrayValues));
128+
*
129+
* return statement.execute();
130+
* }
131+
* }</pre>
132+
* @param name Name of a user defined ARRAY type. Not null.
133+
* @return A {@code Type} object representing the user defined ARRAY type. Not
134+
* null.
135+
*/
136+
public static ArrayType arrayType(String name) {
137+
return new ArrayTypeImpl(Objects.requireNonNull(name, "name is null"));
138+
}
139+
140+
/**
141+
* Extension of the standard {@link Type} interface used to represent user
142+
* defined ARRAY types. An instance of {@code ArrayType} must be used when
143+
* binding an array value to a {@link Statement} created by the Oracle R2DBC
144+
* Driver.
145+
* </p><p>
146+
* Oracle Database does not support an anonymous {@code ARRAY} type, which is
147+
* what the standard {@link R2dbcType#COLLECTION} type represents. Oracle
148+
* Database only supports {@code ARRAY} types which are declared as a user
149+
* defined type, as in:
150+
* <pre>{@code
151+
* CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
152+
* }</pre>
153+
* In order to bind an array, the name of a user defined ARRAY type must
154+
* be known to Oracle R2DBC. Instances of {@code ArrayType} retain the name
155+
* that is provided to the {@link #arrayType(String)} factory method.
156+
*/
157+
public interface ArrayType extends Type {
158+
159+
/**
160+
* {@inheritDoc}
161+
* Returns {@code Object[].class}, which is the standard mapping for
162+
* {@link R2dbcType#COLLECTION}. The true default type mapping is the array
163+
* variant of the default mapping for the element type of the {@code ARRAY}.
164+
* For instance, an {@code ARRAY} of {@code VARCHAR} maps to a
165+
* {@code String[]} by default.
166+
*/
167+
@Override
168+
Class<?> getJavaType();
169+
170+
/**
171+
* {@inheritDoc}
172+
* Returns the name of this user defined {@code ARRAY} type. For instance,
173+
* this method returns "MY_ARRAY" if the type is declared as:
174+
* <pre>{@code
175+
* CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
176+
* }</pre>
177+
*/
178+
@Override
179+
String getName();
180+
}
181+
182+
/** Concrete implementation of the {@code ArrayType} interface */
183+
private static final class ArrayTypeImpl
184+
extends TypeImpl implements ArrayType {
185+
186+
/**
187+
* Constructs an ARRAY type with the given {@code name}. The constructed
188+
* {@code ArrayType} as a default Java type mapping of
189+
* {@code Object[].class}. This is consistent with the standard
190+
* {@link R2dbcType#COLLECTION} type.
191+
* @param name User defined name of the type. Not null.
192+
*/
193+
ArrayTypeImpl(String name) {
194+
super(Object[].class, name);
195+
}
196+
}
197+
102198
/**
103199
* Implementation of the {@link Type} SPI.
104200
*/
105-
private static final class TypeImpl implements Type {
201+
private static class TypeImpl implements Type {
106202

107203
/**
108204
* The Java Language mapping of this SQL type.

0 commit comments

Comments
 (0)