Skip to content

Commit 9d37887

Browse files
Merge pull request #104 from oracle/83-struct-types
OBJECT Types
2 parents 5e190f4 + 755f15c commit 9d37887

17 files changed

+2284
-200
lines changed

.github/workflows/test.sh

Lines changed: 2 additions & 2 deletions
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=180" >> src/test/resources/config.properties
85-
echo "SQL_TIMEOUT=180" >> src/test/resources/config.properties
84+
echo "CONNECT_TIMEOUT=240" >> src/test/resources/config.properties
85+
echo "SQL_TIMEOUT=240" >> src/test/resources/config.properties
8686
mvn clean compile test

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,120 @@ Publisher<Integer[]> arrayMapExample(Result result) {
618618
}
619619
```
620620

621+
### OBJECT
622+
Oracle Database supports `OBJECT` as a user defined type. A `CREATE TYPE`
623+
command is used to define an `OBJECT` type:
624+
```sql
625+
CREATE TYPE PET AS OBJECT(
626+
name VARCHAR(128),
627+
species VARCHAR(128),
628+
weight NUMBER,
629+
birthday DATE)
630+
```
631+
Oracle R2DBC defines `oracle.r2dbc.OracleR2dbcType.ObjectType` as a `Type` for
632+
representing user defined `OBJECT` types. A `Parameter` with a type of
633+
`ObjectType` may be used to bind `OBJECT` values to a `Statement`.
634+
635+
Use an `Object[]` to bind the attribute values of an `OBJECT` by index:
636+
```java
637+
Publisher<Result> objectArrayBindExample(Connection connection) {
638+
Statement statement =
639+
connection.createStatement("INSERT INTO petTable VALUES (:petObject)");
640+
641+
// Bind the attributes of the PET OBJECT defined above
642+
ObjectType objectType = OracleR2dbcTypes.objectType("PET");
643+
Object[] attributeValues = {
644+
"Derby",
645+
"Dog",
646+
22.8,
647+
LocalDate.of(2015, 11, 07)
648+
};
649+
statement.bind("petObject", Parameters.in(objectType, attributeValues));
650+
651+
return statement.execute();
652+
}
653+
```
654+
655+
Use a `Map<String,Object>` to bind the attribute values of an `OBJECT` by name:
656+
```java
657+
Publisher<Result> objectMapBindExample(Connection connection) {
658+
Statement statement =
659+
connection.createStatement("INSERT INTO petTable VALUES (:petObject)");
660+
661+
// Bind the attributes of the PET OBJECT defined above
662+
ObjectType objectType = OracleR2dbcTypes.objectType("PET");
663+
Map<String,Object> attributeValues = Map.of(
664+
"name", "Derby",
665+
"species", "Dog",
666+
"weight", 22.8,
667+
"birthday", LocalDate.of(2015, 11, 07));
668+
statement.bind("petObject", Parameters.in(objectType, attributeValues));
669+
670+
return statement.execute();
671+
}
672+
```
673+
A `Parameter` with a type of `ObjectType` must be used when binding OUT
674+
parameters of `OBJECT` types for a PL/SQL call:
675+
```java
676+
Publisher<Result> objectOutBindExample(Connection connection) {
677+
Statement statement =
678+
connection.createStatement("BEGIN; getPet(:petObject); END;");
679+
680+
ObjectType objectType = OracleR2dbcTypes.objectType("PET");
681+
statement.bind("petObject", Parameters.out(objectType));
682+
683+
return statement.execute();
684+
}
685+
```
686+
`OBJECT` values may be consumed from a `Row` or `OutParameter` as an
687+
`oracle.r2dbc.OracleR2dbcObject`. The `OracleR2dbcObject` interface is a subtype
688+
of `io.r2dbc.spi.Readable`. Attribute values may be accessed using the standard
689+
`get` methods of `Readable`. The `get` methods of `OracleR2dbcObject` support
690+
alll SQL to Java type mappings defined by the
691+
[R2DBC Specification](https://r2dbc.io/spec/1.0.0.RELEASE/spec/html/#datatypes.mapping):
692+
```java
693+
Publisher<Pet> objectMapExample(Result result) {
694+
return result.map(row -> {
695+
OracleR2dbcObject oracleObject = row.get(0, OracleR2dbcObject.class);
696+
return new Pet(
697+
oracleObject.get("name", String.class),
698+
oracleObject.get("species", String.class),
699+
oracleObject.get("weight", Float.class),
700+
oracleObject.get("birthday", LocalDate.class));
701+
});
702+
}
703+
```
704+
705+
Instances of `OracleR2dbcObject` may be passed directly to `Statement` bind
706+
methods:
707+
```java
708+
Publisher<Result> objectBindExample(
709+
OracleR2dbcObject oracleObject, Connection connection) {
710+
Statement statement =
711+
connection.createStatement("INSERT INTO petTable VALUES (:petObject)");
712+
713+
statement.bind("petObject", oracleObject);
714+
715+
return statement.execute();
716+
}
717+
```
718+
Attribute metadata is exposed by the `getMetadata` method of
719+
`OracleR2dbcObject`:
720+
```java
721+
void printObjectMetadata(OracleR2dbcObject oracleObject) {
722+
OracleR2dbcObjectMetadata metadata = oracleObject.getMetadata();
723+
OracleR2dbcTypes.ObjectType objectType = metadata.getObjectType();
724+
725+
System.out.println("Object Type: " + objectType);
726+
metadata.getAttributeMetadatas()
727+
.stream()
728+
.forEach(attributeMetadata -> {
729+
System.out.println("\tAttribute Name: " + attributeMetadata.getName()));
730+
System.out.println("\tAttribute Type: " + attributeMetadata.getType()));
731+
});
732+
}
733+
```
734+
621735
### REF Cursor
622736
Use the `oracle.r2dbc.OracleR2dbcTypes.REF_CURSOR` type to bind `SYS_REFCURSOR` out
623737
parameters:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package oracle.r2dbc;
2+
3+
public interface OracleR2dbcObject extends io.r2dbc.spi.Readable {
4+
5+
OracleR2dbcObjectMetadata getMetadata();
6+
7+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package oracle.r2dbc;
2+
3+
import io.r2dbc.spi.ReadableMetadata;
4+
5+
import java.util.List;
6+
import java.util.NoSuchElementException;
7+
8+
/**
9+
* Represents the metadata for attributes of an OBJECT. Metadata for attributes
10+
* can either be retrieved by index or by name. Attribute indexes are
11+
* {@code 0}-based. Retrieval by attribute name is case-insensitive.
12+
*/
13+
public interface OracleR2dbcObjectMetadata {
14+
15+
/**
16+
* Returns the type of the OBJECT which metadata is provided for.
17+
* @return The type of the OBJECT. Not null.
18+
*/
19+
OracleR2dbcTypes.ObjectType getObjectType();
20+
21+
/**
22+
* Returns the {@link ReadableMetadata} for one attribute.
23+
*
24+
* @param index the attribute index starting at 0
25+
* @return the {@link ReadableMetadata} for one attribute. Not null.
26+
* @throws IndexOutOfBoundsException if {@code index} is out of range
27+
* (negative or equals/exceeds {@code getParameterMetadatas().size()})
28+
*/
29+
ReadableMetadata getAttributeMetadata(int index);
30+
31+
/**
32+
* Returns the {@link ReadableMetadata} for one attribute.
33+
*
34+
* @param name the name of the attribute. Not null. Parameter names are
35+
* case-insensitive.
36+
* @return the {@link ReadableMetadata} for one attribute. Not null.
37+
* @throws IllegalArgumentException if {@code name} is {@code null}
38+
* @throws NoSuchElementException if there is no attribute with the
39+
* {@code name}
40+
*/
41+
ReadableMetadata getAttributeMetadata(String name);
42+
43+
/**
44+
* Returns the {@link ReadableMetadata} for all attributes.
45+
*
46+
* @return the {@link ReadableMetadata} for all attributes. Not null.
47+
*/
48+
List<? extends ReadableMetadata> getAttributeMetadatas();
49+
}

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ public static ArrayType arrayType(String name) {
137137
return new ArrayTypeImpl(Objects.requireNonNull(name, "name is null"));
138138
}
139139

140+
public static ObjectType objectType(String name) {
141+
return new ObjectTypeImpl(Objects.requireNonNull(name, "name is null"));
142+
}
143+
140144
/**
141145
* Extension of the standard {@link Type} interface used to represent user
142146
* defined ARRAY types. An instance of {@code ArrayType} must be used when
@@ -179,22 +183,60 @@ public interface ArrayType extends Type {
179183
String getName();
180184
}
181185

186+
public interface ObjectType extends Type {
187+
188+
/**
189+
* {@inheritDoc}
190+
* Returns {@code Object[].class}, which is the standard mapping for
191+
* {@link R2dbcType#COLLECTION}. The true default type mapping is the array
192+
* variant of the default mapping for the element type of the {@code ARRAY}.
193+
* For instance, an {@code ARRAY} of {@code VARCHAR} maps to a
194+
* {@code String[]} by default.
195+
*/
196+
@Override
197+
Class<?> getJavaType();
198+
199+
/**
200+
* {@inheritDoc}
201+
* Returns the name of this user defined {@code ARRAY} type. For instance,
202+
* this method returns "MY_ARRAY" if the type is declared as:
203+
* <pre>{@code
204+
* CREATE TYPE MY_ARRAY AS ARRAY(8) OF NUMBER
205+
* }</pre>
206+
*/
207+
@Override
208+
String getName();
209+
}
210+
182211
/** Concrete implementation of the {@code ArrayType} interface */
183212
private static final class ArrayTypeImpl
184213
extends TypeImpl implements ArrayType {
185214

186215
/**
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.
216+
* Constructs an ARRAY type with the given {@code name}. {@code Object[]} is
217+
* the default mapping of the constructed type. This is consistent with the
218+
* standard {@link R2dbcType#COLLECTION} type.
191219
* @param name User defined name of the type. Not null.
192220
*/
193221
ArrayTypeImpl(String name) {
194222
super(Object[].class, name);
195223
}
196224
}
197225

226+
/** Concrete implementation of the {@code ObjectType} interface */
227+
private static final class ObjectTypeImpl
228+
extends TypeImpl implements ObjectType {
229+
230+
/**
231+
* Constructs an OBJECT type with the given {@code name}.
232+
* {@code OracleR2dbcObject} is the default mapping of the constructed type.
233+
* @param name User defined name of the type. Not null.
234+
*/
235+
ObjectTypeImpl(String name) {
236+
super(OracleR2dbcObject.class, name);
237+
}
238+
}
239+
198240
/**
199241
* Implementation of the {@link Type} SPI.
200242
*/
@@ -259,6 +301,19 @@ public String getName() {
259301
public String toString() {
260302
return getName();
261303
}
304+
305+
@Override
306+
public boolean equals(Object other) {
307+
if (! (other instanceof Type))
308+
return false;
309+
310+
return sqlName.equals(((Type)other).getName());
311+
}
312+
313+
@Override
314+
public int hashCode() {
315+
return sqlName.hashCode();
316+
}
262317
}
263318

264319
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ static <T> T requireNonNull(T obj, String message) {
8181
return obj;
8282
}
8383

84+
8485
/**
8586
* Checks if a {@code jdbcConnection} is open, and throws an exception if the
8687
* check fails.

0 commit comments

Comments
 (0)