Oracle8i SQLJ Developer's Guide and Reference
Release 8.1.5






Prev  Chap Top Next

About Custom Java Classes and the CustomDatum Interface

The key functionality of custom Java classes is to provide a way to convert data between the database and your Java application and making the data accessible, particularly in supporting objects and collections or if you want to do custom data conversions.

It is advisable to provide custom Java classes for all user-defined types (objects and collections) that you use in a SQLJ application. The Oracle JDBC driver will use instances of these classes in converting data, which is more convenient and less error-prone than using the weakly typed classes oracle.sql.STRUCT, REF, and ARRAY.

Custom Java classes are first-class types that you can use to read from and write to user-defined SQL types transparently.

CustomDatum and CustomDatumFactory Specifications

Oracle provides the interface oracle.sql.CustomDatum and the related interface oracle.sql.CustomDatumFactory as vehicles to use in mapping Oracle object types, reference types, and collection types to custom Java classes and in converting data between the database and your application. A custom Java class must implement CustomDatum to be used in SQLJ iterators and host expressions, and either the custom Java class or some other class must implement CustomDatumFactory to create instances of the custom Java class.

Data is passed to or from the database in the form of an oracle.sql.Datum object, with the underlying data being in the format of the appropriate oracle.sql.Datum subclass, such as oracle.sql.STRUCT. This data is still in its codified database format; the oracle.sql.Datum object is just a wrapper. (For information about classes in the oracle.sql package that support Oracle type extensions, see the Oracle8i JDBC Developer's Guide and Reference.)

The CustomDatum interface specifies a toDatum() method for data conversion from Java format to database format. This method takes as input your OracleConnection object (which is required by the Oracle JDBC drivers) and converts data to the appropriate oracle.sql.* representation. The OracleConnection object is necessary so that the JDBC driver can perform appropriate type checking and type conversions at runtime. Here is the CustomDatum and toDatum() specification:

interface oracle.sql.CustomDatum
   oracle.sql.Datum toDatum(OracleConnection c);

The CustomDatumFactory interface specifies a create() method that constructs instances of your custom Java class, converting from database format to Java format. This method takes as input a Datum object containing data from the database and an integer indicating the SQL type of the underlying data, such as OracleTypes.RAW. It returns an object of your custom Java class, which implements the CustomDatum interface. This object receives its data from the Datum object that was input. Here is the CustomDatumFactory and create() specification:

interface oracle.sql.CustomDatumFactory
   oracle.sql.CustomDatum create(oracle.sql.Datum d, int sqlType);

To complete the relationship between the CustomDatum and CustomDatumFactory interfaces, there is a requirement for a static getFactory() method that you must implement in any custom Java class that implements the CustomDatum interface. This method returns an object that implements the CustomDatumFactory interface, and that therefore can be used to create instances of your custom Java class. This returned object may itself be an instance of your custom Java class, and its create() method is used by the Oracle JDBC driver to produce further instances of your custom Java class, as necessary.

Custom Java classes generated by JPublisher automatically implement the CustomDatum and CustomDatumFactory interfaces and the getFactory() method.


JPublisher implements CustomDatum and its toDatum() method and CustomDatumFactory and its create() method in a single custom Java class; however, the reason toDatum() and create() are in different interfaces is to allow the option of implementing them in separate classes. You can have one custom Java class that implements CustomDatum, its toDatum() method, and the getFactory() method, and have a separate factory class that implements CustomDatumFactory and its create() method. For purposes of discussion here, though, the presumption is that both interfaces are implemented in a single class.  

For more information about the CustomDatum and CustomDatumFactory interfaces, the oracle.sql classes, and the OracleTypes class, see the Oracle8i JDBC Developer's Guide and Reference.

Custom Java Class Support for Object Methods

Methods of Oracle objects can be implemented as wrappers in custom Java classes. Whether the underlying stored procedure is written in PL/SQL or is written in Java and published to SQL is invisible to the user.

A Java wrapper method that is used to invoke a server method requires a connection to communicate with the server. The connection object can be provided as an explicit parameter or can be associated in some other way (as an attribute of your custom Java class, for example).

You can write each wrapper method as an instance method of the custom Java class, regardless of whether the server method that the wrapper method invokes is an instance method or a static method. Custom Java classes generated by JPublisher use this technique. JPublisher also provides a public no-argument constructor for each custom Java class it generates, so that an instance can be conveniently created for the purpose of calling a wrapper method that invokes a static server method. This is also a recommended technique if you are writing your own custom Java classes.

There are also issues regarding output and input-output parameters in methods of Oracle objects. In the database, if a stored procedure (Oracle object method) modifies the internal state of one of its arguments, the actual argument that was passed to the stored procedure is modified. In Java this is not possible. When a JDBC output parameter is returned from a stored procedure call, it is stored in a newly created object. The original object identity is lost.

One way to return an output or input-output parameter to the caller is to pass the parameter as an element of an array. If the parameter is input-output, the wrapper method takes the array element as input; after processing, the wrapper assigns the output to the array element. Custom Java classes generated by JPublisher use this technique--each output or input-output parameter is passed in a one-element array.

See the Oracle8i JPublisher User's Guide for more information.

Custom Java Class Requirements

All custom Java classes must satisfy certain requirements to be recognized by the SQLJ translator as valid host variable types. These requirements are primarily the same for any kind of custom Java class but vary slightly depending on whether the class is a custom object class, custom reference class, custom collection class, or some other kind of custom Java class.

These requirements are as follows:


The collection type name reflects the collection type, not the base type. For example, if you have declared a VARRAY or nested table type PERSON_ARRAY for PERSON objects, then the name of the collection type that you specify for the _SQL_NAME entry is PERSON_ARRAY, not PERSON.  

Compiling Custom Java Classes

You can include the .java files for your custom Java classes on the SQLJ command line together with your .sqlj file. For example, if ObjectDemo.sqlj uses Oracle object types ADDRESS and PERSON, and you have run JPublisher or otherwise produced custom Java classes for these objects, then you can run SQLJ as follows:

% sqlj ObjectDemo.sqlj

Otherwise, you can use your Java compiler to compile them directly. If you do this, it must be done prior to translating the .sqlj file.

(Running SQLJ is discussed in Chapter 8, "Translator Command Line and Options".)


Because custom Java classes rely on Oracle-specific features, SQLJ will report numerous portability warnings unless you use the translator option setting -warn=noportable (the default). For information about this flag, see "Translator Warnings (-warn)".  

Reading and Writing Custom Data

This section describes how data from custom Java class instances is read from the database and written to the database.

How Custom Data is Read from the Database

In reading data from the database, the conversion of codified bytes of an Oracle object and its attributes (or collection and its elements) into an instance of the corresponding custom Java class takes place in three steps:

  1. The JDBC driver reads the codified bytes from the database and creates an instance of the appropriate oracle.sql class (typically STRUCT or ARRAY) to contain the bytes.

  2. The SQLJ runtime calls the static getFactory() method of your custom Java class to obtain a CustomDatumFactory object, which is typically a static instance of your custom Java class.

  3. The JDBC driver calls the create() method of the CustomDatumFactory object obtained in step 2. This creates and populates an instance of your custom class, using the data from the oracle.sql.* instance created in step 1.

How Custom Data is Written to the Database

In writing an instance of a custom object class or custom collection class to the database, the toDatum() method is called, returning an instance of oracle.sql.STRUCT (or oracle.sql.ARRAY) that is then written to the database.

Additional Uses for Custom Java Classes

Most discussion of custom Java classes involves their use as one of the following:

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

This last use is further discussed in "Serializing Java Objects through Custom Java Classes".

In "General Use of", there is a sample class BetterDate that can be used instead of java.sql.Date to represent dates.



Copyright © 1999 Oracle Corporation.

All Rights Reserved.