Oracle8i JDBC Developer's Guide and Reference Release 3 (8.1.7) Part Number A83724-01 |
Library | Service | Contents | Index

## Creating and Using Custom Object Classes for Oracle Objects

If you want to create custom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the drivers will instantiate for the corresponding Oracle objects.

You must also provide a way to create and populate instances of the custom object class from the Oracle object and its attribute data. The driver must be able to read from a custom object class and write to it. In addition, the custom object class can provide `get` and `set` methods corresponding to the Oracle object's attributes, although this is not necessary. To create and populate the custom classes and provide these read/write capabilities, you can choose between these two interfaces:

- the JDBC standard `SQLData` interface

- the `CustomDatum` and `CustomDatumFactory` interfaces provided by Oracle

The custom object class you create must implement one of these interfaces. The `CustomDatum` interface can also be used to implement the custom reference class corresponding to the custom object class. If you are using the `SQLData` interface, however, you can only use weak reference types in Java (`java.sql.Ref` or `oracle.sql.REF`). The `SQLData` interface is for mapping SQL objects only.

As an example, assume you have an Oracle object type, `EMPLOYEE`, in the database that consists of two attributes: `Name` (which is type `CHAR`) and `EmpNum` (employee number, which is type `NUMBER`). You use the type map to specify that the `EMPLOYEE` object should map to a custom object class that you call `JEmployee`. You can implement either the `SQLData` or `CustomDatum` interface in the `JEmployee` class.

You can create custom object classes yourself, but the most convenient way to create them is to employ the Oracle JPublisher utility to create them for you. As of release 8.1.6, JPublisher supports the standard `SQLData` interface as well as the Oracle-specific `CustomDatum` interface, and is able to generate classes that implement either one. See "Using JPublisher to Create Custom Object Classes" for more information.

The following section compares `CustomDatum` and `SQLData` functionality.

### Relative Advantages of CustomDatum Versus SQLData

In deciding which of these two interface implementations to use, consider the following:

Advantages of `CustomDatum`:

- It does not require an entry in the type map for the Oracle object.

- It has awareness of Oracle extensions.

- You can construct a `CustomDatum` from an `oracle.sql.STRUCT`. This is more efficient because it avoids unnecessary conversions to native Java types.

- You can obtain the corresponding `Datum` object (which is in `oracle.sql` format) from the `CustomDatum` object, using the `toDatum()` method.

- It provides better performance: `CustomDatum` works directly with `Datum` types, which is the internal format used by the driver to hold Oracle objects.

Advantages of `SQLData`:

- It is a JDBC standard, making your code more portable.

The `SQLData` interface is for mapping SQL objects only. The `CustomDatum` interface is more flexible, enabling you to map SQL objects as well as any other SQL type for which you want to customize processing. You can create a `CustomDatum` object from any datatype found in an Oracle database. This could be useful, for example, for serializing `RAW` data in Java.

### Understanding Type Maps for SQLData Implementations

If you use the `SQLData` interface in a custom object class, then you must create type map entries that specify the custom object class to use in mapping the SQL object type to Java. You can either use the default type map of the connection object, or a type map that you specify when you retrieve the data from the result set. The `ResultSet` interface `getObject()` method has a signature that lets you specify a type map:

    rs.getObject(int columnIndex);

or:

    rs.getObject(int columnIndex, Map map);

For a description of how to create these custom object classes with `SQLData`, see "Creating and Using Custom Object Classes for Oracle Objects".

When using a SQLData implementation, if you do not include a type map entry, then the object will map to the `oracle.sql.STRUCT` class by default. (`CustomDatum` implementations, by contrast, have their own mapping functionality so that a type map entry is not required. When using a `CustomDatum` implementation, use the Oracle `getCustomDatum()` method instead of the standard `getObject()` method.)

The type map relates a Java class to the SQL type name of an Oracle object. This one-to-one mapping is stored in a hash table as a keyword-value pair. When you read data from an Oracle object, the JDBC driver considers the type map to determine which Java class to use to materialize the data from the SQL object type. When you write data to an Oracle object, the JDBC driver gets the SQL type name from the Java class by calling the `getSQLTypeName()` method of the `SQLData` interface. The actual conversion between SQL and Java is performed by the driver.

The attributes of the Java class that corresponds to an Oracle object can use either Java native types or Oracle native types (instances of the `oracle.sql.*` classes) to store attributes.

### Creating a Type Map Object and Defining Mappings for a SQLData Implementation

When using a `SQLData` implementation, the JDBC applications programmer is responsible for providing a type map, which must be an instance of a class as follows:

- under JDK 1.2.x, an instance of a class that implements the standard `java.util.Map` interface

or:

- under JDK 1.1.x, an instance of a class that extends the standard `java.util.Dictionary` class (or an instance of the `Dictionary` class itself)

You have the option of creating your own class to accomplish this, but under either JDK 1.2.x or JDK 1.1.x, the standard class `java.util.Hashtable` meets the requirement.

Note: If you are migrating from JDK 1.1.x to JDK 1.2.x, you must ensure that your code uses a class that implements the `Map` interface. If you were using the `java.util.Hashtable` class under 1.1.x, then no change is necessary.

`Hashtable` and other classes used for type maps implement a `put()` method that takes keyword-value pairs as input, where each key is a fully qualified SQL type name and the corresponding value is an instance of a specified Java class.

A type map is associated with a connection instance. The standard `java.sql.Connection` interface and the Oracle-specific `oracle.jdbc.driver.OracleConnection` class include a `getTypeMap()` method. Under JDK 1.2.x, both return a `Map` object; under JDK 1.1.x, both return a `Dictionary` object.

The remainder of this section covers the following topics:

- Adding Entries to an Existing Type Map

- Creating a New Type Map

#### Adding Entries to an Existing Type Map

When a connection instance is first established, the default type map is empty. You must populate it to use any SQL-Java mapping functionality.

Follow these general steps to add entries to an existing type map.

1. Use the `getTypeMap()` method of your `OracleConnection` object to return the connection's type map object. The `getTypeMap()` method returns a `java.util.Map` object (or `java.util.Dictionary` under JDK 1.1.x). For example, presuming an `OracleConnection` instance `oraconn`:

       java.util.Map myMap = oraconn.getTypeMap();

   Note: If the type map in the `OracleConnection` instance has not been initialized, then the first call to `getTypeMap()` returns an empty map.

2. Use the type map's `put()` method to add map entries. The `put()` method takes two arguments: a SQL type name string and an instance of a specified Java class that you want to map to.

       myMap.put(sqlTypeName, classObject);

   The `sqlTypeName` is a string that represents the fully qualified name of the SQL type in the database. The `classObject` is the Java class object to which you want to map the SQL type. Get the class object with the `Class.forName()` method, as follows:

       myMap.put(sqlTypeName, Class.forName(className));

   For example, if you have a `PERSON` SQL datatype defined in the `CORPORATE` database schema, then map it to a `Person` Java class defined as `Person` with this statement:

       myMap.put("CORPORATE.PERSON", Class.forName("Person"));

   The map has an entry that maps the `PERSON` SQL datatype in the `CORPORATE` database to the `Person` Java class.

   Note: SQL type names in the type map must be all uppercase, because that is how the Oracle database stores SQL names.

#### Creating a New Type Map

Follow these general steps to create a new type map. This example uses an instance of `java.util.Hashtable`, which extends `java.util.Dictionary` and, under JDK 1.2.x, also implements `java.util.Map`.

1. Create a new type map object.

       Hashtable newMap = new Hashtable();

2. Use the `put()` method of the type map object to add entries to the map. For more information on the `put()` method, see Step 2 under "Adding Entries to an Existing Type Map". For example, if you have an `EMPLOYEE` SQL type defined in the `CORPORATE` database, then you can map it to an `Employee` class object defined by `Employee.java`, with this statement:

       newMap.put("CORPORATE.EMPLOYEE", class.forName("Employee"));

3. When you finish adding entries to the map, use the `OracleConnection` object's `setTypeMap()` method to overwrite the connection's existing type map. For example:

       oraconn.setTypeMap(newMap);

   In this example, `setTypeMap()` overwrites the `oraconn` connection's original map with `newMap`.

   Note: The default type map of a connection instance is used when mapping is required but no map name is specified, such as for a result set `getObject()` call that does not specify the map as input.

#### Materializing Object Types not Specified in the Type File

If you do not provide a type map with an appropriate entry when using a `getObject()` call, then the JDBC driver will materialize an Oracle object as an instance of the `oracle.sql.STRUCT` class. If the Oracle object type contains embedded objects, and they are not present in the type map, the driver will materialize the embedded objects as instances of `oracle.sql.STRUCT` as well. If the embedded objects are present in the type map, a call to the `getAttributes()` method will return embedded objects as instances of the specified Java classes from the type map.

### Understanding the SQLData Interface

One of the choices in making an Oracle object and its attribute data available to Java applications is to create a custom object class that implements the `SQLData` interface. Note that if you use this interface, you must supply a type map that specifies the Oracle object types in the database and the names of the corresponding custom object classes that you will create for them.

The `SQLData` interface defines methods that translate between SQL and Java for Oracle database objects. Standard JDBC provides a `SQLData` interface and companion `SQLInput` and `SQLOutput` interfaces in the `java.sql` package (`oracle.jdbc2` package under JDK 1.1.x).

If you create a custom object class that implements `SQLData`, then you must provide a `readSQL()` method and a `writeSQL()` method, as specified by the `SQLData` interface.

The JDBC driver calls your `readSQL()` method to read a stream of data values from the database and populate an instance of your custom object class. Typically, the driver would use this method as part of an `OracleResultSet` object `getObject()` call.

Similarly, the JDBC driver calls your `writeSQL()` method to write a sequence of data values from an instance of your custom object class to a stream that can be written to the database. Typically, the driver would use this method as part of an `OraclePreparedStatement` object `setObject()` call.

#### Understanding the SQLInput and SQLOutput Interfaces

The JDBC driver includes classes that implement the `SQLInput` and `SQLOutput` interfaces. It is not necessary to implement the `SQLOutput` or `SQLInput` objects--the JDBC drivers will do this for you.

The `SQLInput` implementation is an input stream class, an instance of which must be passed in to the `readSQL()` method. `SQLInput` includes a `readXXX()` method for every possible Java type that attributes of an Oracle object might be converted to, such as `readObject()`, `readInt()`, `readLong()`, `readFloat()`, `readBlob()`, and so on. Each `readXXX()` method converts SQL data to Java data and returns it into an output parameter of the corresponding Java type. For example, `readInt()` returns an integer.

The `SQLOutput` implementation is an output stream class, an instance of which must be passed in to the `writeSQL()` method. `SQLOutput` includes a `writeXXX()` method for each of these Java types. Each `writeXXX()` method converts Java data to SQL data, taking as input a parameter of the relevant Java type. For example, `writeString()` would take as input a string attribute from your Java class.

#### Implementing readSQL() and writeSQL() Methods

When you create a custom object class that implements `SQLData`, you must implement the `readSQL()` and `writeSQL()` methods, as described here.

You must implement `readSQL()` as follows:

    public void readSQL(SQLInput stream, String sql_type_name) throws SQLException

- The `readSQL()` method takes as input a `SQLInput` stream and a string that indicates the SQL type name of the data (in other words, the name of the Oracle object type, such as `EMPLOYEE`).

  When your Java application calls `getObject()`, the JDBC driver creates a `SQLInput` stream object and populates it with data from the database. The driver can also determine the SQL type name of the data when it reads it from the database. When the driver calls `readSQL()`, it passes in these parameters.

- For each Java datatype that maps to an attribute of the Oracle object, `readSQL()` must call the appropriate `readXXX()` method of the `SQLInput` stream that is passed in.

  For example, if you are reading `EMPLOYEE` objects that have an employee name as a `CHAR` variable and an employee number as a `NUMBER` variable, you must have a `readString()` call and a `readInt()` call in your `readSQL()` method. JDBC calls these methods according to the order in which the attributes appear in the SQL definition of the Oracle object type.

- The `readSQL()` method takes the data that the `readXXX()` methods read and convert, and assigns them to the appropriate fields or elements of a custom object class instance.

You must implement `writeSQL()` as follows:

    public void writeSQL(SQLOutput stream) throws SQLException

- The `writeSQL()` method takes as input a `SQLOutput` stream.

  When your Java application calls `setObject()`, the JDBC driver creates a `SQLOutput` stream object and populates it with data from a custom object class instance. When the driver calls `writeSQL()`, it passes in this stream parameter.

- For each Java datatype that maps to an attribute of the Oracle object, `writeSQL()` must call the appropriate `writeXXX()` method of the `SQLOutput` stream that is passed in.

  For example, if you are writing to `EMPLOYEE` objects that have an employee name as a `CHAR` variable and an employee number as a `NUMBER` variable, then you must have a `writeString()` call and a `writeInt()` call in your `writeSQL()` method. These methods must be called according to the order in which attributes appear in the SQL definition of the Oracle object type.

- The `writeSQL()` method then writes the data converted by the `writeXXX()` methods to the `SQLOutput` stream so that it can be written to the database once you execute the prepared statement.

"SQLData Implementation--SQLDataExample.java" contains a sample implementation of the `SQLData` interface for a given SQL object definition.

### Reading and Writing Data with a SQLData Implementation

This section describes how to read data from an Oracle object or write data to an Oracle object if your corresponding Java class implements `SQLData`.

#### Reading SQLData Objects from a Result Set

This section summarizes the steps to read data from an Oracle object into your Java application when you choose the `SQLData` implementation for your custom object class.

These steps assume you have already defined the Oracle object type, created the corresponding custom object class, updated the type map to define the mapping between the Oracle object and the Java class, and defined a statement object `stmt`.

1. Query the database to read the Oracle object into a JDBC result set.

       ResultSet rs = stmt.executeQuery("SELECT emp_col FROM personnel");

   The `PERSONNEL` table contains one column, `EMP_COL`, of SQL type `EMP_OBJECT`. This SQL type is defined in the type map to map to the Java class `Employee`.

2. Use the `getObject()` method of your result set to populate an instance of your custom object class with data from one row of the result set. The `getObject()` method returns the user-defined `SQLData` object because the type map contains an entry for `Employee`.

       if (rs.next())
          Employee emp = (Employee)rs.getObject(1);

   Note that if the type map did not have an entry for the object, then `getObject()` would return an `oracle.sql.STRUCT` object. Cast the output to type `STRUCT`, because the `getObject()` method signature returns the generic `java.lang.Object` type.

       if (rs.next())
          STRUCT empstruct = (STRUCT)rs.getObject(1);

   The `getObject()` call triggers `readSQL()` and `readXXX()` calls from the `SQLData` interface, as described above.

   Note: If you want to avoid using a type map, then use the `getSTRUCT()` method. This method always returns a `STRUCT` object, even if there is a mapping entry in the type map.

3. If you have `get` methods in your custom object class, then use them to read data from your object attributes. For example, if `EMPLOYEE` has an `EmpName` (employee name) of type `CHAR`, and an `EmpNum` (employee number) of type `NUMBER`, then provide a `getEmpName()` method that returns a Java `String` and a `getEmpNum()` method that returns an integer (`int`). Then invoke them in your Java application, as follows:

       String empname = emp.getEmpName();
       int empnumber = emp.getEmpNum();

   Note: Alternatively, fetch data by using a callable statement object, which also has a `getObject()` method.

#### Retrieving SQLData Objects from a Callable Statement OUT Parameter

Suppose you have an `OracleCallableStatement` `ocs` that calls a PL/SQL function `GETEMPLOYEE()`. The program passes an employee number (`empnumber`) to the function; the function returns the corresponding `Employee` object.

1. Prepare an `OracleCallableStatement` to call the `GETEMPLOYEE()` function.

       OracleCallableStatement ocs =
         (OracleCallableStatement)conn.prepareCall("{ ? = call GETEMPLOYEE(?) }");

2. Declare the `empnumber` as the input parameter to `GETEMPLOYEE()`. Register the `SQLData` object as the `OUT` parameter, with typecode `OracleTypes.STRUCT`. Then, execute the statement.

       ocs.setInt(2, empnumber);
       ocs.registerOutParameter(1, OracleTypes.STRUCT, "EMP_OBJECT");
       ocs.execute();

3. Use the `getObject()` method to retrieve the employee object. The following code assumes that there is a type map entry to map the Oracle object to Java type `Employee`:

       Employee emp = (Employee)ocs.getObject(1);

   If there is no type map entry, then `getObject()` would return an `oracle.sql.STRUCT` object. Cast the output to type `STRUCT`, because the `getObject()` method signature returns the generic `java.lang.Object` type:

       STRUCT emp = (STRUCT)ocs.getObject(1);

#### Passing SQLData Objects to a Callable Statement as an IN Parameter

Suppose you have a PL/SQL function `addEmployee(?)` that takes an `Employee` object as an `IN` parameter and adds it to the `PERSONNEL` table. In this example, `emp` is a valid `Employee` object.

1. Prepare an `OracleCallableStatement` to call the `addEmployee(?)` function.

       OracleCallableStatement ocs =
         (OracleCallableStatement) conn.prepareCall("{ call addEmployee(?) }");

2. Use `setObject()` to pass the `emp` object as an `IN` parameter to the callable statement. Then, execute the statement.

       ocs.setObject(1, emp);
       ocs.execute();

#### Writing Data to an Oracle Object Using a SQLData Implementation

This section describes the steps in writing data to an Oracle object from your Java application when you choose the `SQLData` implementation for your custom object class.

This description assumes you have already defined the Oracle object type, created the corresponding Java class, and updated the type map to define the mapping between the Oracle object and the Java class.

1. If you have `set` methods in your custom object class, then use them to write data from Java variables in your application to attributes of your Java datatype object.

       emp.setEmpName(empname);
       emp.setEmpNum(empnumber);

   This statement uses the `emp` object and the `empname` and `empnumber` variables assigned in "Reading SQLData Objects from a Result Set".

2. Prepare a statement that updates an Oracle object in a row of a database table, as appropriate, using the data provided in your Java datatype object.

       PreparedStatement pstmt = conn.prepareStatement
                                 ("INSERT INTO PERSONNEL VALUES (?)");

   This assumes `conn` is your connection object.

3. Use the `setObject()` method of the prepared statement to bind your Java datatype object to the prepared statement.

       pstmt.setObject(1, emp);

4. Execute the statement, which updates the database.

       pstmt.executeUpdate();

### Understanding the CustomDatum Interface

One of the choices in making an Oracle object and its attribute data available to Java applications is to create a custom object class that implements the `oracle.sql.CustomDatum` and `oracle.sql.CustomDatumFactory` interfaces (or you can implement `CustomDatumFactory` in a separate class). The `CustomDatum` and `CustomDatumFactory` interfaces are supplied by Oracle and are not a part of the JDBC standard.

Note: The JPublisher utility supports the generation of classes that implement the `CustomDatum` and `CustomDatumFactory` interfaces. See "Using JPublisher to Create Custom Object Classes".

#### Understanding CustomDatum Features

The `CustomDatum` interface has these advantages:

- It recognizes Oracle extensions to the JDBC; `CustomDatum` uses `oracle.sql.Datum` types directly.

- It does not require a type map to specify the names of the Java custom classes you want to create.

- It provides better performance: `CustomDatum` works directly with `Datum` types, the internal format the driver uses to hold Oracle objects.

The `CustomDatum` and `CustomDatumFactory` interfaces do the following:

- The `toDatum()` method of the `CustomDatum` class transforms the data into an `oracle.sql.*` representation.

- `CustomDatumFactory` specifies a `create()` method equivalent to a constructor for your custom object class. It creates and returns a `CustomDatum` instance. The JDBC driver uses the `create()` method to return an instance of the custom object class to your Java application or applet. It takes as input an `oracle.sql.Datum` object and an integer indicating the corresponding SQL typecode as specified in the `OracleTypes` class.

`CustomDatum` and `CustomDatumFactory` have the following definitions:

    public interface CustomDatum
    {
        Datum toDatum (OracleConnection conn) throws SQLException;
    }

    public interface CustomDatumFactory
    {
        CustomDatum create (Datum d, int sql_Type_Code) throws SQLException;
    }

Where `conn` represents the Connection object, `d` represents an object of type `oracle.sql.Datum`, and `sql_Type_Code` represents the SQL typecode (from the standard `Types` or `OracleTypes` class) of the `Datum` object.

#### Retrieving and Inserting Object Data

The JDBC drivers provide the following methods to retrieve and insert object data as instances of `CustomDatum`.

To retrieve object data:

- Use the Oracle-specific `OracleResultSet` class `getCustomDatum()` method (assume an `OracleResultSet` object `ors`):

      ors.getCustomDatum (int col_index, CustomDatumFactory factory);

  This method takes as input the column index of the data in your result set, and a `CustomDatumFactory` instance. For example, you can implement a `getFactory()` method in your custom object class to produce the `CustomDatumFactory` instance to input to `getCustomDatum()`. The type map is not required when using Java classes that implement `CustomDatum`.

or:

- Use the standard `getObject(``index``,` `map``)` method specified by the `ResultSet` interface to retrieve data as instances of `CustomDatum`. In this case, you must have an entry in the type map that identifies the factory class to be used for the given object type, and its corresponding SQL type name.

To insert object data:

- Use the Oracle-specific `OraclePreparedStatement` class `setCustomDatum()` method (assume an `OraclePreparedStatement` object `ops`):

      ops.setCustomDatum (int bind_index, CustomDatum custom_obj);

  This method takes as input the parameter index of the bind variable and the name of the object containing the variable.

or:

- Use the standard `setObject()` method specified by the `PreparedStatement` interface. You can also use this method, in its different forms, to insert `CustomDatum` instances without requiring a type map.

The following sections describe the `getCustomDatum()` and `setCustomDatum()` methods.

To continue the example of an Oracle object `EMPLOYEE`, you might have something like the following in your Java application:

    CustomDatum datum = ors.getCustomDatum(1, Employee.getFactory());

In this example, `ors` is an Oracle result set, `getCustomDatum()` is a method in the `OracleResultSet` class used to retrieve a `CustomDatum` object, and the `EMPLOYEE` is in column 1 of the result set. The static `Employee.getFactory()` method will return a `CustomDatumFactory` to the JDBC driver. The JDBC driver will call `create(`) from this object, returning to your Java application an instance of the `Employee` class populated with data from the result set.

Notes: `CustomDatum` and `CustomDatumFactory` are defined as separate interfaces so that different Java classes can implement them if you wish (such as an `Employee` class and an `EmployeeFactory` class). To use the `CustomDatum` interface, your custom object classes must import `oracle.sql.*` (or at least `CustomDatum`, `CustomDatumFactory`, and `Datum`).

"CustomDatum Implementation--CustomDatumExample.java" contains an example implementation of the `CustomDatum` interface for a given SQL object definition.

### Reading and Writing Data with a CustomDatum Implementation

This section describes how to read data from an Oracle object or write data to an Oracle object if your corresponding Java class implements `CustomDatum`.

#### Reading Data from an Oracle Object Using a CustomDatum Implementation

This section summarizes the steps in reading data from an Oracle object into your Java application. These steps apply whether you implement `CustomDatum` manually or use JPublisher to produce your custom object classes.

These steps assume you have already defined the Oracle object type, created the corresponding custom object class or had JPublisher create it for you, and defined a statement object `stmt`.

1. Query the database to read the Oracle object into a result set, casting to an Oracle result set.

       OracleResultSet ors = (OracleResultSet)stmt.executeQuery
                             ("SELECT Emp_col FROM PERSONNEL");

   Where `PERSONNEL` is a one-column table. The column name is `Emp_col` of type `Employee_object`.

2. Use the `getCustomDatum()` method of your Oracle result set to populate an instance of your custom object class with data from one row of the result set. The `getCustomDatum()` method returns an `oracle.sql.CustomDatum` object, which you can cast to your specific custom object class.

       if (ors.next())
          Employee emp = (Employee)ors.getCustomDatum(1, Employee.getFactory());

   or:

       if (ors.next())
          CustomDatum datum = ors.getCustomDatum(1, Employee.getFactory());

   This example assumes that `Employee` is the name of your custom object class and `ors` is the name of your `OracleResultSet` object.

   In case you do not want to use `getCustomDatum()`, the JDBC drivers let you use the `getObject()` method of a standard JDBC `ResultSet` to retrieve `CustomDatum` data. However, you must have an entry in the type map that identifies the factory class to be used for the given object type, and its corresponding SQL type name.

   For example, if the SQL type name for your object is `EMPLOYEE`, then the corresponding Java class is `Employee`, which will implement `CustomDatum`. The corresponding Factory class is `EmployeeFactory`, which will implement `CustomDatumFactory`.

   Use this statement to declare the `EmployeeFactory` entry for your type map:

       map.put ("EMPLOYEE", Class.forName ("EmployeeFactory"));

   Then use the form of `getObject()` where you specify the map object:

       Employee emp = (Employee) rs.getObject (1, map);

   If the connection's default type map already has an entry that identifies the factory class to be used for the given object type, and its corresponding SQL type name, then you can use this form of `getObject()`:

       Employee emp = (Employee) rs.getObject (1);

3. If you have `get` methods in your custom object class, use them to read data from your object attributes into Java variables in your application. For example, if `EMPLOYEE` has `EmpName` of type `CHAR` and `EmpNum` (employee number) of type `NUMBER`, provide a `getEmpName()` method that returns a Java string and a `getEmpNum()` method that returns an integer. Then invoke them in your Java application as follows:

       String empname = emp.getEmpName();
       int empnumber = emp.getEmpNum();

   Note: Alternatively, you can fetch data into a callable statement object. The `OracleCallableStatement` class also has a `getCustomDatum()` method.

#### Writing Data to an Oracle Object Using a CustomDatum Implementation

This section summarizes the steps in writing data to an Oracle object from your Java application. These steps apply whether you implement `CustomDatum` manually or use JPublisher to produce your custom object classes.

These steps assume you have already defined the Oracle object type and created the corresponding custom object class (or had JPublisher create it for you).

Note: The type map is not used when you are performing database `INSERT` and `UPDATE` operations.

1. If you have `set` methods in your custom object class, then use them to write data from Java variables in your application to attributes of your Java datatype object.

       emp.setEmpName(empname);
       emp.setEmpNum(empnumber);

   This statement uses the `emp` object and the `empname` and `empnumber` variables defined in "Reading Data from an Oracle Object Using a CustomDatum Implementation".

2. Write an Oracle prepared statement that updates an Oracle object in a row of a database table, as appropriate, using the data provided in your Java datatype object.

       OraclePreparedStatement opstmt = conn.prepareStatement
          ("UPDATE PERSONNEL SET Employee = ? WHERE Employee.EmpNum = 28959);

   This assumes `conn` is your `Connection` object.

3. Use the `setCustomDatum()` method of the Oracle prepared statement to bind your Java datatype object to the prepared statement.

       opstmt.setCustomDatum(1, emp);

   The `setCustomDatum()` method calls the `toDatum()` method of the custom object class instance to retrieve an `oracle.sql.STRUCT` object that can be written to the database.

   In this step you could also use the `setObject()` method to bind the Java datatype. For example:

       opstmt.setObject(1,emp);

   Note: You can use your Java datatype objects as either `IN` or `OUT` bind variables.

### Additional Uses for CustomDatum

The `CustomDatum` interface offers far more flexibility than the `SQLData` interface. The `SQLData` interface is designed to let you customize the mapping of only SQL object types (that is, Oracle8 object types) to Java types of your choice. Implementing the `SQLData` interface lets the JDBC driver populate fields of a custom Java class instance from the original SQL object data, and the reverse, after performing the appropriate conversions between Java and SQL types.

The `CustomDatum` interface goes beyond supporting the customization of SQL object types to Java types. It lets you provide a mapping between Java object types and any SQL type supported by the `oracle.sql` package.

It might be useful to provide custom Java classes to wrap `oracle.sql.*` types and perhaps implement customized conversions or functionality as well. The following are some possible scenarios:

- to perform encryption and decryption or validation of data

- to perform logging of values that have been read or are being written

- to parse character columns (such as character fields containing URL information) into smaller components

- to map character strings into numeric constants

- to map data into more desirable Java formats (such as mapping a `DATE` field to `java.util.Date` format)

- to customize data representation (for example, data in a table column is in feet but you want it represented in meters after it is selected)

- to serialize and deserialize Java objects--into or out of `RAW` fields, for example

For example, use `CustomDatum` to store instances of Java objects that do not correspond to a particular SQL Oracle8 object type in the database in columns of SQL type `RAW`. The `create()` method in `CustomDatumFactory` would have to implement a conversion from an object of type `oracle.sql.RAW` to the desired Java object. The `toDatum()` method in `CustomDatum` would have to implement a conversion from the Java object to an `oracle.sql.RAW` object. This can be done, for example, by using Java serialization.

Upon retrieval, the JDBC driver transparently retrieves the raw bytes of data in the form of an `oracle.sql.RAW` and calls the `CustomDatumFactory`'s `create()` method to convert the `oracle.sql.RAW` object to the desired Java class.

When you insert the Java object into the database, you can simply bind it to a column of type `RAW` to store it. The driver transparently calls the `CustomDatum`.`toDatum()` method to convert the Java object to an `oracle.sql.RAW` object. This object is then stored in a column of type `RAW` in the database.

Support for the `CustomDatum` interfaces is also highly efficient because the conversions are designed to work using `oracle.sql.*` formats, which happen to be the internal formats used by the JDBC drivers. Moreover, the type map, which is necessary for the `SQLData` interface, is not required when using Java classes that implement `CustomDatum`. For more information on why classes that implement `CustomDatum` do not need a type map, see "Understanding the CustomDatum Interface".

| Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
Library | Service | Contents | Index