Skip Headers

Oracle9i JPublisher User's Guide
Release 2 (9.2)

Part Number A96658-01
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback

Go to previous page Go to next page

2
JPublisher Concepts

This chapter provides a detailed discussion of JPublisher's underlying concepts and of its operation. The following topics are covered:

Details of Datatype Mapping

As described previously, you can specify one of the following settings for datatype mappings when you use the type mapping options (-builtintypes, -lobtypes, -numbertypes, and -usertypes):

These mappings, described in "Overview of Datatype Mappings", affect the argument and result types JPublisher uses in the methods it generates.

The class that JPublisher generates for an object type will have getXXX() and setXXX() methods for the object attributes. The class that JPublisher generates for a VARRAY or nested table type will have getXXX() and setXXX() methods that access the elements of the array or nested table. When you use the option -methods=true, the class that JPublisher generates for an object type or PL/SQL package will have wrapper methods that invoke server methods of the object type or package. The mapping options control the argument and result types these methods will use.

The JDBC and Object JDBC mappings use familiar Java types that can be manipulated using standard Java operations. If your JDBC program is manipulating Java objects stored as object types, you might prefer the JDBC or Object JDBC mapping.

The Oracle mapping is the most efficient mapping. The oracle.sql types match the Oracle internal datatypes as closely as possible so that little or no data conversion is required between the Java and the SQL formats. You do not lose any information and have greater flexibility in how you process and unpack the data. The Oracle mappings for standard SQL types are the most convenient representations if you are manipulating data within the database or moving data (for example, performing SELECT and INSERT operations from one existing table to another). When data format conversion is necessary, you can use methods in the oracle.sql.* classes to convert to Java native types.

When you decide which mapping to use, you should remember that data format conversion is only a part of the cost of transferring data between your program and the server.

SQL and PL/SQL Mappings to Oracle and JDBC Types

Table 2-1 lists the mappings from SQL and PL/SQL datatypes to Java types using the Oracle and JDBC mappings. You can use all the supported datatypes listed in this table as argument or result types for PL/SQL methods. You can use a subset of the datatypes as object attribute types, as listed in "Allowed Object Attribute Types".

The SQL and PL/SQL Datatype column contains all possible datatypes.

The Oracle Mapping column lists the corresponding Java types JPublisher uses when all the type mapping options are set to oracle. These types are found in the oracle.sql package supplied by Oracle and are designed to minimize the overhead incurred when converting Oracle datatypes to Java types. Refer to the Oracle9i JDBC Developer's Guide and Reference for more information on the oracle.sql package.

The JDBC Mapping column lists the corresponding Java types JPublisher uses when all the type mapping options are set to jdbc. For standard SQL datatypes, JPublisher uses Java types specified in the JDBC specification. For SQL datatypes that are Oracle extensions, JPublisher uses the oracle.sql.* types. When you set the type mapping option to objectjdbc, the corresponding types will be the same as in the JDBC Mapping column except that primitive Java types, such as int, are replaced with their object counterparts, such as java.lang.Integer. Type correspondences that are explicitly defined in the JPublisher type map, such as PL/SQL BOOLEAN to SQL NUMBER to Java boolean, are not affected by the mapping option settings.

A few datatypes are not directly supported by JPublisher, in particular those types that pertain to PL/SQL only. You can overcome these limitations by providing equivalent SQL and Java types, as well as PL/SQL conversion functions between PL/SQL and SQL representations. The annotations and subsequent sections explain these conversions further.

Table 2-1 SQL and PL/SQL Datatype to Oracle and JDBC Mapping Classes  
SQL and PL/SQL Datatype Oracle Mapping JDBC Mapping

CHAR, CHARACTER, LONG, STRING, VARCHAR, VARCHAR2

oracle.sql.CHAR

java.lang.String

NCHAR, NVARCHAR2

oracle.sql.NCHAR (note 1)

oracle.sql.NString (note 1)

RAW, LONG RAW

oracle.sql.RAW

byte[]

BINARY_INTEGER, NATURAL, NATURALN, PLS_INTEGER, POSITIVE, POSITIVEN, SIGNTYPE, INT, INTEGER

oracle.sql.NUMBER

int

DEC, DECIMAL, NUMBER, NUMERIC

oracle.sql.NUMBER

java.math.BigDecimal

DOUBLE PRECISION, FLOAT

oracle.sql.NUMBER

double

SMALLINT

oracle.sql.NUMBER

short

REAL

oracle.sql.NUMBER

float

DATE

oracle.sql.DATE

java.sql.Timestamp

TIMESTAMP,
TIMESTAMP WITH TZ,
TIMESTAMP WITH LOCAL TZ

oracle.sql.TIMESTAMP,
oracle.sql.TIMESTAMPTZ,
oracle.sql.TIMESTAMPLTZ

java.sql.Timestamp

INTERVAL YEAR TO MONTH
INTERVAL DAY TO SECOND

String (note 2)

String (note 2)

ROWID, UROWID

oracle.sql.ROWID

oracle.sql.ROWID

BOOLEAN

boolean (note 3)

boolean (note 3)

CLOB

oracle.sql.CLOB

java.sql.Clob

BLOB

oracle.sql.BLOB

java.sql.Blob

BFILE

oracle.sql.BFILE

oracle.sql.BFILE

NCLOB

oracle.sql.NCLOB (note 1)

oracle.sql.NCLOB (note 1)

object types

generated class

generated class

SQLJ object types

Java class defined at type creation

Java class defined at type creation

OPAQUE types

generated or predefined class (note 4)

generated or predefined class (note 4)

RECORD types

through mapping to SQL object type (note 5)

through mapping to SQL object type (note 5)

nested table, VARRAY

generated class implemented using oracle.sql.ARRAY

java.sql.Array

reference to object type

generated class implemented using oracle.sql.REF

java.sql.Ref

REF CURSOR

java.sql.ResultSet

java.sql.ResultSet

scalar (numeric or character)
indexed-by tables

through mapping to Java array (note 6)

through mapping to Java array (note 6)

indexed-by tables

through mapping to SQL collection (note 7)

through mapping to SQL collection (note 7)

user-defined subtypes

same as for base type

same as for base type

Datatype Mapping Notes

The following notes correspond to marked entries in the preceding table.

  1. The Java classes oracle.sql.NCHAR, oracle.sql.NCLOB, and oracle.sql.NString are not part of JDBC but are distributed with the SQLJ runtime. SQLJ uses these classes to represent the NCHAR form of use of the corresponding classes oracle.sql.CHAR, oracle.sql.CLOB, and java.lang.String.
  2. Mapping of SQL INTERVAL types to VARCHAR2 and Java String is defined in a default JPublisher type map. It uses conversion functions from the SYS.SQLJUTL package. See also "JPublisher Default Type Map and User Type Map".
  3. Mapping of PL/SQL BOOLEAN to SQL NUMBER and Java boolean is defined in the default JPublisher type map. It uses conversion functions from the SYS.SQLJUTL package.
  4. Mapping of the SQL OPAQUE type SYS.XMLTYPE to the Java class oracle.xdb.XMLType is defined in the default JPublisher type map. For other OPAQUE types, the vendor will typically provide a corresponding Java class. In this case you just have to specify a JPublisher type map entry that defines the correspondence between the SQL OPAQUE type and the corresponding Java wrapper class. If JPublisher encounters an OPAQUE type that does not have a type map entry, it will generate a Java wrapper class for that OPAQUE type. See also "Type Mapping Support for OPAQUE Types".
  5. In order to support a PL/SQL RECORD type you must define a corresponding SQL object type and two PL/SQL conversion functions that map between SQL and PL/SQL types (one function to convert in each direction). Additionally, you must publish a Java wrapper class for the SQL type with JPublisher. At this point you can provide a type map entry for JPublisher that defines the correspondences between PL/SQL, SQL, and Java types and the PL/SQL conversion functions. This allows JPublisher to automatically publish PL/SQL or method signatures that use the PL/SQL RECORD type. See also "Type Mapping Support for PL/SQL RECORD Types".
  6. If you are using the JDBC OCI driver to call PL/SQL stored procedures or object methods, you have direct support for scalar indexed-by tables, also known as PL/SQL TABLE types. In this case, specify a type map entry for JPublisher that contains the PL/SQL scalar indexed-by table type and the corresponding Java array type. JPublisher can then automatically publish PL/SQL or object method signatures that use this scalar indexed-by type. See also "Type Mapping Support for Scalar Indexed-by Tables Using JDBC OCI".
  7. In order to support a PL/SQL indexed-by table type, you must define a corresponding SQL collection type and two PL/SQL conversion functions that map between SQL and PL/SQL types. Additionally, you must publish a Java wrapper class for the SQL collection type with JPublisher. (If the elements of the indexed-by table are PL/SQL records, you also must provide full JPublisher mapping support between these records and corresponding SQL and Java types.) At this point you can provide a type map entry for JPublisher that defines the correspondences between PL/SQL, SQL, and Java types and the PL/SQL conversion functions. Now JPublisher can automatically publish PL/SQL or method signatures that use this PL/SQL indexed-by-table type. See also "Type Mapping Support for PL/SQL Indexed-by Table Types".


    Note:

    The Object JDBC and BigDecimal mappings, which affect numeric types only, are fully described in "Mappings For Numeric Types (-numbertypes)".


Allowed Object Attribute Types

You can use a subset of the PL/SQL datatypes listed in Table 2-1 as object attribute types. These datatypes are listed here and have the same Oracle mappings and JDBC mappings as described in the table:

The TIMESTAMP types TIMESTAMP, TIMESTAMP WITH TIMEZONE, and TIMESTAMP WITH LOCAL TIMEZONE are supported by JPublisher as object attributes. However, in Oracle9i release 2 (9.2.0), JDBC does not support these types as object attributes.

Using Datatypes Unsupported by JDBC

Generally, if JPublisher encounters a PL/SQL stored procedure or function or an object type method with an unsupported PL/SQL type, it will issue an error message and skip the generation of a corresponding method in the wrapper class. However, if you provide appropriate type mapping information, such methods can still be automatically published by JPublisher. In addition, a JPublisher type map entry can be used to associate types, such as SQL OPAQUE types or certain scalar PL/SQL indexed-by table types, with corresponding Java classes. The following sections discuss various aspects of the type mapping support provided by JPublisher:

Type Mapping Support for OPAQUE Types

Oracle JDBC and Oracle SQLJ provide support for SQL OPAQUE types that are published as Java classes implementing the oracle.sql.ORAData interface. Such classes must also contain the following public static fields and methods:

public static String _SQL_NAME = "SQL_name_of_OPAQUE_type";
public static int _SQL_TYPECODE = OracleTypes.OPAQUE;
public static ORADataFactory getORADataFactory() { ... }

As of Oracle 9i release 2, the SQL OPAQUE type SYS.XMLTYPE is supported with the corresponding Java wrapper class oracle.xdb.XMLType.

If you have a Java wrapper class for a SQL OPAQUE type that follows the rules outlined here, you can specify this association to JPublisher with the following command line option:

-addtypemap=sql_opaque_type:java_wrapper_class

In this way the predefined type correspondence for XMLTYPE could have been supplied explicitly to JPublisher as follows:

-addtypemap=SYS.XMLTYPE:oracle.xdb.XMLType

Whenever JPublisher encounters a SQL OPAQUE type for which no type correspondence has been provided, it will actually publish a Java wrapper class. Consider the following SQL type defined in the SCOTT schema:

CREATE TYPE X_TYP AS OBJECT (xml SYS.XMLTYPE);

Notice that the attribute xml is published as an oracle.xdb.XMLType, which corresponds to the predefined type mapping for SYS.XMLTYPE. The following publishes X_TYP as a Java class XTyp.

jpub -u scott/tiger -s X_TYP:XTyp

If you clear the JPublisher default type map, then an additional wrapper class Xmltype would be automatically generated for the SYS.XMLTYPE attribute. You can verify this by invoking JPublisher as follows:

jpub -u scott/tiger -s X_TYP:XTyp -defaulttypemap=

The option -defaulttypemap is for setting the JPublisher default type map. If you give it no value, as in the preceding example, then the default type map is set to the empty string, effectively clearing it. For more information on the default type map refer to "JPublisher Default Type Map and User Type Map".

Type Mapping Support for Scalar Indexed-by Tables Using JDBC OCI

The Oracle JDBC OCI driver directly supports PL/SQL scalar indexed-by tables with numeric or character elements. (If you are not using the JDBC OCI driver, see "Type Mapping Support for PL/SQL Indexed-by Table Types".) An indexed-by table with numeric elements can be mapped to the following Java array types:

An indexed-by table with character elements can be mapped to the following Java array types:

In certain circumstances, as described, you must convey the following information for an indexed-by table type:

Use the JPublisher option -addtypemap to add instructions to the user type map to specify correspondences between PL/SQL types that are scalar indexed-by tables, and corresponding Java array types. The size hints that are given using the syntax outlined above will be embedded into the generated SQLJ statements and thus conveyed to JDBC at runtime.

As an example, consider the following code fragment from the definition of a PL/SQL package INDEXBY in the schema SCOTT. Assume this is available in a file indexby.sql.

create or replace package indexby as

--  jpub.addtypemap=SCOTT.INDEXBY.VARCHAR_ARY:String[1000](4000)
--  jpub.addtypemap=SCOTT.INDEXBY.INTEGER_ARY:int[1000]
--  jpub.addtypemap=SCOTT.INDEXBY.FLOAT_ARY:double[1000]

 type varchar_ary IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER;
 type integer_ary IS TABLE OF INTEGER        INDEX BY BINARY_INTEGER;
 type float_ary   IS TABLE OF NUMBER         INDEX BY BINARY_INTEGER;

 function get_float_ary RETURN float_ary;
 procedure pow_integer_ary(x integer_ary, y OUT integer_ary);
 procedure xform_varchar_ary(x IN OUT varchar_ary);

end indexby;
/
create or replace package body indexby is ...
/

The following are the required -addtypemap directives for mapping the three indexed-by table types:

-addtypemap=SCOTT.INDEXBY.VARCHAR_ARY:String[1000](4000)
-addtypemap=SCOTT.INDEXBY.INTEGER_ARY:int[1000]
-addtypemap=SCOTT.INDEXBY.FLOAT_ARY:double[1000]

Note that depending on the operating system shell you are using, you might have to quote options that contain square brackets [...] or parentheses (...). Or you can avoid this by placing such options into a JPublisher properties file, as follows:

jpub.addtypemap=SCOTT.INDEXBY.VARCHAR_ARY:String[1000](4000)
jpub.addtypemap=SCOTT.INDEXBY.INTEGER_ARY:int[1000]
jpub.addtypemap=SCOTT.INDEXBY.FLOAT_ARY:double[1000]

See "Properties File Structure and Syntax" for information about properties files.

Also, as a convenience feature, JPublisher directives in a properties file are recognized when placed behind a "--" prefix (two dashes), whereas any entry that does not start with "jpub." or with "-- jpub." is simply ignored. This means you can place JPublisher directives into SQL scripts and reuse the same SQL scripts as JPublisher properties files. Thus, after invoking the indexby.sql script in order to define the INDEXBY package, you can now run JPublisher to publish this package as a Java class IndexBy as follows:

jpub -u scott/tiger -s INDEXBY:IndexBy -props=indexby.sql

As mentioned previously, this mapping of scalar indexed-by tables can only be used in conjunction with the JDBC OCI driver. If you are using another driver or if you want to create driver-independent code, you will have to define SQL types that correspond to the indexed-by table types as well as conversion functions that map between the two. Please refer to the section "Type Mapping Support for PL/SQL Indexed-by Table Types".

Type Mapping Support Through PL/SQL Conversion Functions

This section discusses the general mechanism used by JPublisher for supporting PL/SQL types in Java code, through PL/SQL functions that convert to corresponding SQL types. The sections that follow this are concerned with mapping issues that are specific to PL/SQL RECORD types and PL/SQL indexed-by table types, respectively.

In general, Java programs do not support the binding of PL/SQL-specific types. (Although one exception is scalar indexed-by tables. See "Type Mapping Support for Scalar Indexed-by Tables Using JDBC OCI".) The only way such types can be used from Java is by using PL/SQL code to map them to SQL types and then accessing these SQL types from Java.

JPublisher makes this task more convenient. For a particular PL/SQL type, specify the following information in a JPublisher type map entry.

The -addtypemap specification for this has the following form:

-addtypemap=plsql_type:java_type:sql_type:sql_to_plsql_fun:plsql_to_sql_fun

As an example, consider a type map entry for supporting the PL/SQL type BOOLEAN. It consists of the following specifications:

Put all this together in the following type map entry:

-addtypemap=BOOLEAN:boolean:INTEGER:INT2BOOL:BOOL2INT

Such a type map entry assumes that the SQL type, the Java type, and both conversion functions have been defined in SQL, Java, and PL/SQL, respectively. Note that there already is an entry for PL/SQL BOOLEAN in the JPublisher default type map--see "JPublisher Default Type Map and User Type Map". If you want to try the above type map entry, you would therefore have to override the default type map. You can use the JPublisher -defaulttypemap option to accomplish this, as follows:

jpub -u scott/tiger -s SYS.SQLJUTL:SQLJUtl 
-defaulttypemap=BOOLEAN:boolean:INTEGER:INT2BOOL:BOOL2INT


Note:

While this manual has described conversions in terms of mapping between SQL and PL/SQL types, there is no intrinsic limitation in this approach that would restrict us to PL/SQL. You could also map between different SQL types. In fact, this is done in the JPublisher default type map to support SQL INTERVAL types, which are mapped to VARCHAR2 values and back. (See "JPublisher Default Type Map and User Type Map".)


If the PL/SQL type that we are trying to convert occurs either as an IN parameter or as a function return value, then no further effort is necessary. The two conversion functions, from SQL to PL/SQL and vice versa, are entirely sufficient for all such conversion requirements. A problem arises, however, if the PL/SQL type occurs in an OUT or IN OUT parameter position. In this case, conversions between PL/SQL and SQL representations may be required before or after calling the original procedure or function that is using this type. This means that we may have to generate and load additional PL/SQL code, on a method-by-method basis, for performing this additional conversion task. Fortunately, JPublisher creates this code automatically for you. It remains your responsibility, however, to install this additional PL/SQL code in the database.

The following JPublisher options permit you to control how JPublisher creates this PL/SQL code:

Type Mapping Support for PL/SQL RECORD Types

Publishing PL/SQL RECORD types is just a special case of using conversion functions as described in the previous section. The required steps are most easily illustrated by a concrete example.

Assume that you have method signatures that use the following PL/SQL RECORD type, defined in a PL/SQL package SCHEM.PACK:

TYPE plsql_record IS RECORD (
     pls_number   NUMBER,
     pls_name     VARCHAR2(60));

Also assume that the conversions are to take place in the schema SCOTT.

The following list describes the steps to take.

  1. Define a SQL type that PLSQL_RECORD can be mapped to. For example:
     create TYPE sql_record as object (
             sql_number   NUMBER,
             sql_name     VARCHAR2(60));
    
    
  2. Use JPublisher to publish the SQL type to Java. For example, you can create a Java wrapper class SqlRecord for SQL_RECORD as follows:
    jpub -u scott/tiger -s SQL_RECORD:SqlRecord
    
    
  3. Define PL/SQL stored functions that map from PLSQL_RECORD to SQL_RECORD and vice versa:
    function plsql_record2sql(r SCHEM.PACK.PLSQL_RECORD)
             return sql_record is
    begin
       return sql_record(r.inst_number, r.inst_name);
    end plsql_record2sql;
    
    function sql_record2plsql(r sql_record)
             return SCHEM.PACK.PLSQL_RECORD is
     res SCHEM.PACK.PLSQL_RECORD;
    begin
     if r IS NOT NULL
     then
       res.plsql_number := r.sql_number;
       res.plsql_name   := r.sql_name;
     end if;
     return res;
    end sql_record2plsql;
    
    
  4. Set up a type map entry for JPublisher that tells it how to publish the PLSQL_RECORD type by mapping it to the SQL_RECORD type. You could create the following JPublisher properties file, record.properties, for example (with backslash characters, "\", indicating that continuation lines follow):
    # Type map entries have the format:
    # jpub.sql=PLSQL_type:Java_type:SQL_type:sql_to_plsql_fun:plsql_to_sql_fun
    #
    # Note the use of line continuation in the entry below.
    jpub.addtypemap=SCHEM.PACK.PLSQL_RECORD:\
                    SqlRecord:\
                    SQL_RECORD:\
                    SQL_RECORD2PLSQL:\
                    PLSQL_RECORD2SQL
    
    
  5. Use this type map entry whenever you publish a package or type that refers to PLSQL_RECORD. For example, in the following JPublisher invocation we are including record.properties with this type map entry (using the -u shorthand for -users and -p for -props):
    jpub -u schema/pw_for_schem -p record.properties -s SCHEM.PACK:Pack
    
    
  6. If PLSQL_RECORD is used as an OUT or IN OUT parameter in SCHEM.PACK, then JPublisher will also alert you that it has generated a file plsql_wrapper.sql containing PL/SQL wrapper definitions. Make sure to run this script before using the generated Java class Pack. Also note that you can use the -plsqlfile, -plsqlpackage, and -plsqlmap options to customize the PL/SQL script that JPublisher creates.

Type Mapping Support for PL/SQL Indexed-by Table Types

If you are using the JDBC OCI driver and require only the publishing of scalar indexed-by tables, you can use the direct mapping between Java and these types outlined in "Type Mapping Support for Scalar Indexed-by Tables Using JDBC OCI". In all other cases you must define a SQL collection type that permits conversion to and from the PL/SQL indexed-by table type.

This section continues the example in the preceding section and adds an indexed-by table type PLSQL_INDEXBY with elements of type PLSQL_RECORD. The steps to follow are the same as those outlined previously. We assume once more that the type declarations are defined in the package SCHEM.PACK. In addition to the previous declaration of PLSQL_RECORD, there is also the following definition for PLSQL_INDEXBY:

TYPE plsql_indexby IS TABLE OF plsql_record INDEX BY BINARY_INTEGER;

Again, assume that the conversions are taking place in the schema SCOTT.

The following list describes the steps to take.

  1. Define a SQL type that PLSQL_INDEXBY can be mapped to. For example:
    create TYPE sql_indexby as table of sql_record;
    
    

    Note that the elements of this type must be mappable to the elements of PLSQL_INDEXBY. We accomplished this previously by creating the type SQL_RECORD and mapping it to PLSQL_RECORD.

  2. Use JPublisher to publish the SQL type to Java. For example, to create a Java wrapper class SqlIndexby for SQL_INDEXBY, you can run JPublisher as follows:
    jpub -u scott/tiger -s SQL_INDEXBY:SqlIndexby
    
    
  3. Define PL/SQL stored functions that map from PLSQL_INDEXBY to SQL_INDEXBY and vice versa. The following functions work in conjunction with the previously defined conversion functions PLSQL_RECORD2SQL and SQL_RECORD2PLSQL:
    function plsql_indexby2sql (r SCHEM.PACK.PLSQL_INDEXBY)
             return sql_indexby is
      tab sql_indexby := sql_indexby();
    begin
      FOR i IN 1..r.LAST LOOP
         tab(i) := plsql_record2sql(r(i));
      END LOOP;
      return tab;
    end plsql_indexby2sql;
    
    function sql_indexby2plsql (r sql_indexby)
             return SCHEM.PACK.PLSQL_INDEXBY is
      res SCHEM.PACK.PLSQL_INDEXBY;
    begin
      FOR i IN 1..r.LAST LOOP
        res(i) := sql_record2plsql(r(i));
      END LOOP;
      return res;
    end sql_indexby2plsql;
    
    
  4. Set up a type map entry for JPublisher that tells it how to publish the PLSQL_INDEXBY type by mapping it to the SQL_INDEXBY type. For example, you could create the following JPublisher properties file, indexby.properties:
    # Type map entries have the format:
    # jpub.sql=PLSQL_type:Java_type:SQL_type:sql_to_plsql_fun:plsql_to_sql_fun
    #
    # Note the use of line continuation in the entry below.
    jpub.addtypemap=SCHEM.PACK.PLSQL_INDEXBY:\
                    SqlIndexby:\
                    SQL_INDEXBY:\
                    SQL_INDEXBY2PLSQL:\
                    PLSQL_INDEXBY2SQL
    
    
  5. Use this type map entry whenever you publish a package or type that refers to PLSQL_INDEXBY. For example, in the following JPublisher invocation (a single wraparound command line), the indexby.properties file is included with this type map entry:
    jpub -u schem/pw_for_schem -p indexby.properties -p record.properties
         -s SCHEM.PACK:Pack
    
    

    Note that we also included the record.properties file that tells JPublisher how to map PLSQL_RECORD entities. This allows JPublisher to map signatures that contain either PLSQL_RECORD or PLSQL_INDEXBY types or both. Of course you can also combine all the type map entries into a single properties file.

  6. If PLSQL_INDEXBY or PLSQL_RECORD is used as an OUT or IN OUT parameter in SCHEM.PACK, then JPublisher will also alert you that it has generated a file plsql_wrapper.sql containing PL/SQL wrapper definitions. Be sure to run this script before using the generated Java class Pack. Also note that you can use the -plsqlfile, -plsqlpackage, and -plsqlmap options to customize the PL/SQL script that JPublisher creates.

JPublisher Default Type Map and User Type Map

JPublisher has a user type map, which is controlled by the -typemap and -addtypemap options and starts out empty, and a default type map, which is controlled by the -defaulttypemap and -adddefaulttypemap options and starts with the following entries:

jpub.defaulttypemap=SYS.XMLTYPE:oracle.xdb.XMLType
jpub.adddefaulttypemap=BOOLEAN:boolean:INTEGER:\
SYS.SQLJUTL.INT2BOOL:SYS.SQLJUTL.BOOL2INT
jpub.adddefaulttypemap=INTERVAL DAY TO SECOND:String:CHAR:\
SYS.SQLJUTL.CHAR2IDS:SYS.SQLJUTL.IDS2CHAR
jpub.adddefaulttypemap=INTERVAL YEAR TO MONTH:String:CHAR:\
SYS.SQLJUTL.CHAR2IYM:SYS.SQLJUTL.IYM2CHAR

JPublisher reads the default type map first. If you attempt in the user type map to redefine a mapping that is in the default type map, JPublisher will generate a warning message and ignore the redefinition. Similarly, attempts to add mappings through -adddefaulttypemap or -addtypemap settings that conflict with previous mappings are ignored and generate warnings.

To use custom mappings, it is recommended that you clear the default type map, as follows:

-defaulttypemap=

and then use the -addtypemap option to put any required mappings into the user type map.

The predefined default type map defines a correspondence between the OPAQUE type SYS.XMLTYPE and the Java wrapper class oracle.xdb.XMLType. In addition, it maps the PL/SQL BOOLEAN type to Java boolean and to SQL INTEGER through two conversion functions defined in the SYS.SQLJUTL package. Finally, the default type map provides mappings between SQL INTERVAL types and the Java String type.

However, you may (for example) prefer mapping the PL/SQL BOOLEAN type to the Java object type Boolean in order to capture SQL NULL values in addition to true and false values. This can be accomplished by resetting the default type map, as shown by the following (single wraparound line):

-defaulttypemap=BOOLEAN:Boolean:INTEGER:SYS.SQLJUTL.INT2BOOL:
SYS.SQLJUTL.BOOL2INT

This changes the designated Java type from boolean to Boolean. The rest of the conversion remains valid.

Other Alternatives for Datatypes Unsupported by JDBC

The preceding sections describe the mechanisms used by JPublisher to access types that are not supported in JDBC. As an alternative to using JPublisher in this way, you can try one of these alternatives:

Concepts of JPublisher-Generated Classes

This section covers basic concepts about the code that JPublisher produces, including the following:

For more information, see the following sections later in this chapter:

Passing OUT Parameters

Stored procedures called through SQLJ do not have the same parameter-passing behavior as ordinary Java methods. This affects the code you write when you call a wrapper method that JPublisher generates.

When you call an ordinary Java method, parameters that are Java objects are passed as object references. The method can modify the object.

In contrast, when you call a stored procedure through SQLJ, a copy of each parameter is passed to the stored procedure. If the procedure modifies any parameters, copies of the modified parameters are returned to the caller. Therefore, the "before" and "after" values of a parameter that has been modified appear in separate objects.

A wrapper method JPublisher generates contains SQLJ code to call a stored procedure. The parameters to the stored procedure, as declared in your CREATE TYPE or CREATE PACKAGE declaration, have three possible parameter modes: IN, OUT, and IN OUT. The IN OUT and OUT parameters of the stored procedure are returned to the wrapper method in newly created objects. These new values must be returned to the caller somehow, but assignment to the formal parameter within the wrapper method does not affect the actual parameter visible to the caller.

Passing Parameters Other Than the "this" Parameter

The simplest way to solve the problem described above is to pass an OUT or IN OUT parameter to the wrapper method in a single-element array. The array is a sort of container that holds the parameter.

In the following example, you have an initialized variable p of class Person, and x is an object belonging to a JPublisher-generated class that has a wrapper method f taking an IN OUT Person argument. You create the array and pass the parameter as follows:

Person [] pa = {p}; 
x.f(pa); 
p = pa[0]; 

Unfortunately, this technique for passing OUT or IN OUT parameters requires you to add a few extra lines of code to your program for each parameter. If your stored program has many OUT or IN OUT parameters, you might prefer to call it directly using SQLJ code, rather than a wrapper method.

Passing the "this" Parameter

Problems similar to what is described above arise when the this object of an instance method is modified.

The this object is an additional parameter that is passed in a different way. Its mode, as declared in the CREATE TYPE statement, may be IN or IN OUT. If you do not explicitly declare the mode of this, its mode is IN OUT if the stored procedure does not return a result, or IN if it does.

If the mode of the this object is IN OUT, the wrapper method must return the new value of this. The code generated by JPublisher processes this in different ways, depending on the situation:

Translating Overloaded Methods

PL/SQL, as with Java, lets you create overloaded methods--two or more methods with the same name, but different signatures. If you use JPublisher to generate wrapper methods for PL/SQL methods, it is possible that two overloaded methods with different signatures in PL/SQL might have identical signatures in Java. If this occurs, JPublisher changes the names of the methods to avoid generating two or more methods with the identical signature. For example, consider a PL/SQL package or object type that includes these functions:

FUNCTION f(x INTEGER, y INTEGER) RETURN INTEGER 

and

FUNCTION f(xx FLOAT, yy FLOAT) RETURN INTEGER 

In PL/SQL, these functions have different argument types. However, once they are translated to Java with Oracle mapping, this difference disappears (both INTEGER and FLOAT map to oracle.sql.NUMBER).

Suppose that JPublisher generates a class for the package or object type with the command-line setting -methods=true and Oracle mapping. JPublisher responds by generating code similar to this:

  public oracle.sql.NUMBER f_1 ( 
    oracle.sql.NUMBER x, 
    oracle.sql.NUMBER y) 
  throws SQLException 
  { 
    /* body omitted */  
  } 
 
  public oracle.sql.NUMBER f_4 ( 
    oracle.sql.NUMBER xx, 
    oracle.sql.NUMBER yy) 
  throws SQLException 
  { 
    /* body omitted */ 
  } 
 

Note that in this example, JPublisher names the first function f_1 and the second function f_4. Each function name ends with _<nn>, where <nn> is a number assigned by JPublisher. The number has no significance of its own, but JPublisher uses it to guarantee that the names of functions with identical parameter types will be unique.

JPublisher Generation of SQLJ Classes (.sqlj)

When -methods=all (the default) or -methods=true, JPublisher generates .sqlj files for PL/SQL packages and for object types--both ORAData implementations and SQLData implementations (unless an object type does not define any methods, in which case a .java file is generated). The classes includes wrapper methods that invoke the server methods of the object types and packages. Run SQLJ to translate the .sqlj file.

This section describes how to use these generated classes in your SQLJ code.

Important Notes About Generation of SQLJ Classes

Be aware of the following for JPublisher-generated SQLJ classes:

Use of SQLJ Classes JPublisher Generates for PL/SQL Packages

Take the following steps to use a class that JPublisher generates for a PL/SQL package:

  1. Construct an instance of the class.
  2. Call the wrapper methods of the class.

The constructors for the class associate a database connection with an instance of the class. One constructor takes a SQLJ DefaultContext instance (or an instance of a class specified through the -context option when you ran JPublisher), one constructor takes a JDBC Connection instance, and one constructor has no arguments. Calling the no-argument constructor is equivalent to passing the SQLJ default context to the constructor that takes a DefaultContext instance. Oracle JDBC provides the constructor that takes a Connection instance for the convenience of the JDBC programmer who knows how to compile a SQLJ program, but is unfamiliar with SQLJ concepts such as DefaultContext.


Important:

See "Important Notes About Generation of SQLJ Classes".


The wrapper methods are all instance methods, because the connection context in the this object is used in #sql statements in the wrapper methods.

Because a class generated for a PL/SQL package has no instance data other than the connection context, you will typically construct one class instance for each connection context you use. If the default context is the only one you use then you can call the no-argument constructor once. However, the Oracle9i SQLJ Developer's Guide and Reference discusses reasons for using explicit connection context instances instead.

An instance of a class generated for a PL/SQL package does not contain copies of PL/SQL package variables. It is not an ORAData class or a SQLData class, and you cannot use it as a host variable.

"Example: Using Classes Generated for Packages" shows how to use a class generated for a PL/SQL package.

Use of Classes JPublisher Generates for Object Types

To use an instance of a Java class that JPublisher generates for a SQL object type or a SQL OPAQUE type, you must first initialize the Java object. You can accomplish this in one of the following ways:

or:

or:

Once you have initialized your Java object, you can:

There is a Java attribute for each attribute of the corresponding SQL object type, with getXXX() and setXXX() accessor methods for each attribute. The accessor method names are of the form getFoo() and setFoo() for attribute foo. JPublisher does not generate fields for the attributes.

By default, the class includes wrapper methods that invoke the associated Oracle object methods executing in the server. The wrapper methods are all instance methods, regardless of whether the server methods are. The DefaultContext in the this object is used in #sql statements in the wrapper methods.

With Oracle mapping, JPublisher generates the following methods for the Oracle JDBC driver to use. These methods are specified in the ORAData and ORADataFactory interfaces:

These methods are not generally intended for your direct use. In addition, JPublisher generates methods setFrom(otherObject), setValueFrom(otherObject), and setContextFrom(otherObject) that can be used to copy value or connection information from one object instance to another.

The sample in "Example: Using Classes Generated for Object Types" shows how to use a class that was generated for an object type and has wrapper methods.

Use of Connection Contexts and Instances in SQLJ Code Generated by JPublisher

The class that JPublisher uses in creating SQLJ connection context instances depends on how you set the -context option when you run JPublisher, as follows:

See "SQLJ Connection Context Classes (-context)" for more information about the -context option.

Considerations in Using Connection Contexts and Connection Instances

Consider the following points in using SQLJ connection context instances or JDBC connection instances in instances of JPublisher-generated wrapper classes:

See "Releasing Connection Context Resources" (below) and "SQLJ Connection Context Classes (-context)" for related information.

Releasing Connection Context Resources

In some situations, you must use the release() method of an instance of a JPublisher-generated wrapper class in order to free SQLJ runtime connection context resources. This is true in the following set of circumstances:

and:

and:

and:

and:

In this set of circumstances, a connection context instance would have been created implicitly on the object and must explicitly be freed through the release() method before the object goes out of scope.

(When there is an explicit connection context instance, such as through an explicit constructor or use of the setConnectionContext() method, using release() is not necessary.)

JPublisher Generation of Java Classes (.java)

When -methods=false, or when SQL object types do not define any methods, JPublisher does not generate wrapper methods for object types. In this regard, the behavior is the same for ORAdata and SQLData implementations. Furthermore, when -methods=false, JPublisher does not generate code for PL/SQL packages at all, because they are not useful without wrapper methods. (Note that when -methods=false, JPublisher exclusively generates .java files.)

JPublisher generates the same Java code for reference, VARRAY, and nested table types regardless of whether -methods is false or true.

To use an instance of a class JPublisher generates for an object type when -methods=false, or for a reference, VARRAY, or nested table type, you must first initialize the object.

Similarly to the case with JPublisher-generated SQLJ classes, you can initialize your object in one of the following ways:

or:

or:

Unlike the constructors generated in .sqlj source files, the constructors generated in .java source files do not take a connection argument. Instead, when your object is passed to or returned from a Statement, CallableStatement, or PreparedStatement object, JPublisher applies the connection it uses to construct the Statement, CallableStatement, or PreparedStatement object.

This does not mean you can use the same object with different connections at different times. On the contrary, this is not always possible. An object might have a subcomponent, such as a reference or a BLOB, that is valid only for a particular connection.

To initialize the object data, use the setXXX() methods if your class represents an object type, or the setArray() or setElement() method if your class represents a VARRAY or nested table type. If your class represents a reference type, you can only construct a null reference. All non-null references come from the database.

Once you have initialized your object, you can accomplish the following:

A few methods have not been mentioned yet. You can use the getORADataFactory() method in JDBC code to return an ORADataFactory object. You can pass this ORADataFactory to the Oracle getORAData() methods in the classes ArrayDataResultSet, OracleCallableStatement, and OracleResultSet in the oracle.jdbc package. The Oracle JDBC driver uses the ORADataFactory object to create objects of your JPublisher-generated class.

In addition, classes representing VARRAYs and nested tables have a few methods that implement features of the oracle.sql.ARRAY class:

JPublisher-generated classes for VARRAYs and nested tables do not, however, extend oracle.sql.ARRAY.

With Oracle mapping, JPublisher generates the following methods for the Oracle JDBC driver to use. These methods are specified in the ORAData and ORADataFactory interfaces:

These methods are not generally intended for your direct use; however, you may want to use them if converting from one object reference wrapper type to another.

The sample in "Example: Using Classes Generated for Packages" includes a class that was generated for an object type that does not have wrapper methods.

User-Written Subclasses of JPublisher-Generated Classes

You might want to enhance the functionality of a custom Java class generated by JPublisher by adding methods and transient fields.

One way to accomplish this is to add methods directly to the JPublisher-generated class. However, this is not advisable if you anticipate running JPublisher at some future time to regenerate the class. If you regenerate a class that you have modified in this way, your changes (that is, the methods you have added) will be overwritten. Even if you direct JPublisher output to a separate file, you will still need to merge your changes into the file.

The preferred way to enhance the functionality of a generated class is to extend the class--that is, treat the JPublisher-generated class as a superclass, write a subclass to extend its functionality, then map the object type to the subclass. (This is referred to as the "Generation Gap" pattern in object-oriented terminology.)

This section discusses how to accomplish this.

Extending JPublisher-Generated Classes

Suppose you want JPublisher to generate the class JAddress from the SQL object type ADDRESS. You also want to write a class MyAddress to represent ADDRESS objects, where MyAddress extends the functionality JAddress provides.

Under this scenario, you can use JPublisher to generate a custom Java class JAddress, as well as an initial version of a subclass, MyAddress, into which you then add the desired functionality. You then use JPublisher to map ADDRESS objects to the MyAddress class instead of the JAddress class.

To do this, JPublisher must alter the code it generates in the following ways:

Syntax for Mapping to Alternative Classes

JPublisher has functionality to streamline the process of mapping to alternative classes. Use the following syntax in your -sql command-line option setting:

-sql=object_type:generated_class:map_class

For the above scenario, this would be:

-sql=ADDRESS:JAddress:MyAddress

See "Declaration of Object Types and Packages to Translate (-sql)" for information about the -sql option.

If you were to enter the line in the INPUT file instead of on the command line, it would look like this:

SQL ADDRESS GENERATE JAddress AS MyAddress

See "INPUT File Structure and Syntax" for information about the INPUT file.

In this syntax, JAddress indicates the name of the class that JPublisher will generate (typically as JAddress.sqlj), but MyAddress specifies the name of the class that actually maps to ADDRESS. You are ultimately responsible for the code in MyAddress. Update this as necessary to add your custom functionality. If you retrieve an object that has an ADDRESS attribute, this attribute will be created as an instance of MyAddress in Java. Or if you retrieve an ADDRESS object directly, you will retrieve it into an instance of MyAddress.

For an example of how you would use JPublisher to generate the JAddress class, see "Example: Generating a SQLData Class".

Format of the Class that Extends the Generated Class

For convenience, an initial version of the source file into which you place your custom code--for example, MyAddress.sqlj--is automatically generated by JPublisher, unless it already exists.

The generated code has the following features:

Changes in User-Written Subclasses of Oracle9i JPublisher-Generated Classes

If you have been providing user-written subclasses for JPublisher-generated classes under Oracle8i JPublisher, you should be aware that there are a number of relevant changes in how Oracle9i JPublisher generates code. You would have to make changes in any applications written against the Oracle8i functionality if you want to use it under Oracle9i.


Note:

If you use the -compatible=both8i or 8i setting, you will not see the changes discussed here and your application will continue to work as before. See "Backward-Compatible Oracle Mapping for User-Defined Types (-compatible)".

In general, however, it is advisable to make the transformation to Oracle9i JPublisher functionality, because this will help insulate your user code from implementation details of JPublisher-generated classes.


Following are the changes:

The setFrom(), setValueFrom(), and setContextFrom() Methods

Oracle9i JPublisher provides the following utility methods in generated .sqlj files:

Note that there is semantic equivalence between the following:

x.setFrom(y);

and the following:

x.setValueFrom(y); 
x.setContextFrom(y);

JPublisher Support for Inheritance

This section primarily discusses inheritance support for ORAData types, explaining the following related topics:

This information is followed by a brief overview of standard inheritance support for SQLData types, with reference to appropriate documentation for further information.

ORAData Object Types and Inheritance

Consider the following SQL object types:

CREATE TYPE PERSON AS OBJECT (
...
) NOT FINAL;

CREATE TYPE STUDENT UNDER PERSON (
...
);

CREATE TYPE INSTRUCTOR UNDER PERSON (
...
);

And consider the following JPublisher command line to create corresponding Java classes (a single wraparound command):

jpub -user=scott/tiger -sql=PERSON:Person,STUDENT:Student,INSTRUCTOR:Instructor 
-usertypes=oracle

In this example, JPublisher generates a Person class, a Student class, and an Instructor class. The Student and Instructor classes extend the Person class, because STUDENT and INSTRUCTOR are subtypes of PERSON.

The class at the root of the inheritance hierarchy--Person in this example--contains the full information for the entire inheritance hierarchy and automatically initializes its type map with the required information. As long as you use JPublisher to generate all the required classes of a class hierarchy together, no additional action is required in order to appropriately populate the type map of the class hierarchy.

Precautions when Combining Partially Generated Type Hierarchies

If you run JPublisher several times on a SQL type hierarchy, each time generating only part of the corresponding Java wrapper classes, then you must take precautions in the user application in order to ensure that the type map at the root of the class hierarchy is properly initialized.

In our previous example you might have run the following JPublisher command lines:

jpub -user=scott/tiger -sql=PERSON:Person,STUDENT:Student -usertypes=oracle
jpub -user=scott/tiger -sql=PERSON:Person,INSTRUCTOR:Instructor 
-usertypes=oracle

In this case you should create instances of the generated classes--at a minimum, the leaf classes--before using these mapped types in your code. For example:

new Instructor(); // required
new Student();    // required
new Person();     // optional

The reason for this requirement is explained next.

Mapping of Type Hierarchies in JPublisher-Generated Code

The Person class includes the following method:

Person create(oracle.sql.Datum d, int sqlType)

This method, which converts a Datum instance to its representation as a custom Java object, is called by the Oracle JDBC driver whenever a SQL object declared to be a PERSON is retrieved into a Person variable. The SQL object, however, might actually be a STUDENT object. In this case, the create() method must create a Student instance rather than a Person instance.

In general, to handle this kind of situation, the create() method of a custom Java class (regardless of whether the class was created by JPublisher) must be able to create instances of any subclass that represents a subtype of the SQL object type corresponding to the oracle.sql.Datum argument. This ensures that the actual type of the created Java object will match the actual type of the SQL object.

You might think that the code for the create() method in the root class of a custom Java class hierarchy must mention all its subclasses. But if this were the case, you would have to modify the code for a base class when writing or generating a new subclass. While this would happen automatically if you always use JPublisher to regenerate entire class hierarchies, this might not always be possible. For example, you might not have access to the source code for the Java classes being extended.

Code generated by JPublisher permits incremental extension of a class hierarchy by creating a static initialization block in each subclass of the custom Java class hierarchy. This static initialization block initializes a data structure (equivalent to a type map) declared in the root-level Java class, giving the root class the information it needs about the subclass. When an instance of a subclass is created at runtime, the type is registered in the data structure. Because of this implicit mapping mechanism, no explicit type map, such as those required in SQLData scenarios, is required.


Important:

This implementation makes it possible to extend existing classes without having to modify them, but it also carries a penalty--the static initialization blocks of the subclasses must be executed before the class hierarchy can be used to read objects from the database. This occurs if you instantiate an object of each subclass by calling new(). It is sufficient to instantiate just the leaf classes, because the constructor for a subclass will invoke the constructor for its immediate superclass.

As an alternative, you can always generate (or regenerate) the entire class hierarchy. In this case, there is no need for concern about instantiating the type map through creation of instances of all the leaf classes.


To better understand how code generated by JPublisher supports inheritance, try an example similar to the one at the beginning of this section, and look at the generated code.

ORAData Reference Types and Inheritance

This section shows how to convert from one custom reference class to another, and also generally explains why a custom reference class generated for a subtype by JPublisher does not extend the reference classes of the base type.

Casting a Reference Type Instance into Another Reference Type

Revisiting the example in "ORAData Object Types and Inheritance", we also obtain PersonRef, StudentRef, and InstructorRef, for strongly typed references, in addition to the underlying object type wrappers.

There may be situations where you have a StudentRef instance but you want to use it in a context that requires a PersonRef instance. In this case, use the static cast() method that is generated on strongly typed reference classes:

StudentRef s_ref = ...;  PersonRef p_ref = PersonRef.cast(s_ref);

Conversely, you might have a PersonRef instance and know that you can narrow it to an InstructorRef instance:

PersonRef pr = ...; InstructorRef ir = InstructorRef.cast(pr);

Next we outline why we need to use a cast() function rather than just being able to establish a reference type hierarchy that mirrors the object type hierarchy.

Why Reference Type Inheritance Does Not Follow Object Type Inheritance

The example here helps explain why it is not desirable for reference types to follow the hierarchy of their related object types.

Consider again a subset of the example given in the previous section, repeated here for convenience:

CREATE TYPE PERSON AS OBJECT (
...
) NOT FINAL;

CREATE TYPE STUDENT UNDER PERSON (
...
);

jpub -user=scott/tiger -sql=PERSON:Person,STUDENT:Student -usertypes=oracle

In addition to generating Person.sqlj (or .java) and Student.sqlj (or .java), JPublisher will generate PersonRef.java and StudentRef.java.

Because the Student class extends the Person class, you might expect StudentRef to extend PersonRef. This is not the case, however, because the StudentRef class can provide more compile-time type safety as an independent class than as a subtype of PersonRef. Additionally, a PersonRef can do something that a StudentRef cannot do: modify a Person object in the database.

The most important methods of the PersonRef class would be the following:

The corresponding methods of the StudentRef class would be as follows:

If the StudentRef class extended the PersonRef class, two problems would occur:

It would not be sensible to remedy these problems by giving the StudentRef methods the same signatures and result types as the PersonRef methods, because the additional type safety provided by declaring an object as a StudentRef, rather than as a PersonRef, would be lost.

Manually Converting Between Reference Types

Because reference types do not follow the hierarchy of their related object types, there is a JPublisher limitation that you cannot convert directly from one reference type to another. For background information, this section explains how the generated cast() methods work to convert from one reference type to another.

It is not recommended that you follow these manual steps--they are presented here for illustration only. Simply use the cast() method instead.

The following code, for example, could be used to convert from the reference type XxxxRef to the reference type YyyyRef.

java.sql.Connection conn = ...;  // get underlying JDBC connection
XxxxRef xref = ...;
YyyyRef yref = (YyyyRef) YyyyRef.getORADataFactory().
                create(xref.toDatum(conn),oracle.jdbc.OracleTypes.REF);

This conversion consists of two steps, each of which can be useful in its own right.

  1. Convert xref from its strong XxxxRef type to the weak oracle.sql.REF type:
    oracle.sql.REF ref  = (oracle.sql.REF) xref.toDatum(conn);
    
    
  2. Convert from the oracle.sql.REF type to the target YyyyRef type:
    YyyyRef yref = (YyyyRef) YyyyRef.getORADataFactory().
                              create(ref,oracle.jdbc.OracleTypes.REF);
    
    

"Example: Manually Converting Between Reference Types" below provides sample code for such a conversion.


Note:

This conversion does not involve any type-checking. Whether this conversion is actually permitted depends on your application and on the SQL schema you are using.


Example: Manually Converting Between Reference Types

The following example, including SQL definitions and Java code, illustrates the points of the preceding discussion.

SQL Definitions

Consider the following SQL definitions:

create type person_t as object (ssn number, name varchar2 (30), dob date) not 
final;
/
show errors

create type instructor_t under person_t (title varchar2(20)) not final;
/
show errors

create type instructorPartTime_t under instructor_t (num_hours number);
/
show errors

create type student_t under person_t (deptid number, major varchar2(30)) not 
final;
/
show errors

create type graduate_t under student_t (advisor instructor_t);
/
show errors

create type studentPartTime_t under student_t (num_hours number);
/
show errors

create table person_tab  of person_t;

insert into person_tab values (1001, 'Larry', TO_DATE('11-SEP-60'));
insert into person_tab values (instructor_t(1101, 'Smith', TO_DATE 
('09-OCT-1940'), 'Professor'));
insert into person_tab values (instructorPartTime_t(1111, 'Myers', 
TO_DATE('10-OCT-65'), 'Adjunct Professor', 20));
insert into person_tab values (student_t(1201, 'John', To_DATE('01-OCT-78'), 11, 
'EE'));
insert into person_tab values (graduate_t(1211, 'Lisa', TO_DATE('10-OCT-75'), 
12, 'ICS', instructor_t(1101, 'Smith', TO_DATE ('09-OCT-40'), 'Professor')));
insert into person_tab values (studentPartTime_t(1221, 'Dave', 
TO_DATE('11-OCT-70'), 13, 'MATH', 20));

JPublisher Mappings

Assume the following mappings when you run JPublisher:

Person_t:Person,instructor_t:Instructor,instructorPartTime_t:InstructorPartTime,
graduate_t:Graduate,studentPartTime_t:StudentPartTime

Java Class

Here is a Java class with an example of reference type conversion as discussed above, in "Manually Converting Between Reference Types":

import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sqlj.runtime.Oracle;
import sqlj.runtime.ref.DefaultContext;
import sqlj.runtime.ResultSetIterator;

public class Inheritance
{
  public static void main(String[] args) throws SQLException
  {
    System.out.println("Connecting.");
    Oracle.connect("jdbc:oracle:oci:@","scott","tiger");

    // The following is only required in 9.0.1
    // or if the Java class hierarchy was created piecemeal
    System.out.println("Initializing type system.");
    new Person();
        new Instructor();
            new InstructorPartTime();
        new StudentT();
            new StudentPartTime();
            new Graduate();
    
    PersonRef p_ref;
    InstructorRef i_ref;
    InstructorPartTimeRef ipt_ref;
    StudentTRef s_ref;
    StudentPartTimeRef spt_ref;
    GraduateRef g_ref;

    System.out.println("Selecting a person.");
    #sql { select ref(p) INTO :p_ref FROM PERSON_TAB p WHERE p.NAME='Larry' };

    System.out.println("Selecting an instructor.");
    #sql { select ref(p) INTO :i_ref FROM PERSON_TAB p WHERE p.NAME='Smith' };

    System.out.println("Selecting a part time instructor.");
    #sql { select ref(p) INTO :ipt_ref FROM PERSON_TAB p WHERE p.NAME='Myers' };

    System.out.println("Selecting a student.");
    #sql { select ref(p) INTO :s_ref FROM PERSON_TAB p WHERE p.NAME='John' };

    System.out.println("Selecting a part time student.");
    #sql { select ref(p) INTO :spt_ref FROM PERSON_TAB p WHERE p.NAME='Dave' };

    System.out.println("Selecting a graduate student.");
    #sql { select ref(p) INTO :g_ref FROM PERSON_TAB p WHERE p.NAME='Lisa' };


    // Connection object for conversions
    Connection conn = DefaultContext.getDefaultContext().getConnection();

    // Assigning a part-time instructor ref to a person ref
    System.out.println("Assigning a part-time instructor ref to a person ref");
    oracle.sql.Datum ref = ipt_ref.toDatum(conn);
    PersonRef pref = (PersonRef) PersonRef.getORADataFactory().
                                           create(ref,OracleTypes.REF);
    // or just use:  PersonRef pref = PersonRef.cast(ipt_ref);

    // Assigning a person ref to an instructor ref
    System.out.println("Assigning a person ref to an instructor ref");
    InstructorRef iref = (InstructorRef) InstructorRef.getORADataFactory().
                           create(pref.toDatum(conn), OracleTypes.REF);
    // or just use:  InstructorRef iref = InstructorRef.cast(pref);

    // Assigning a graduate ref to an part time instructor ref
    // ==> this should actually bomb at runtime!
    System.out.println
               ("Assigning a graduate ref to a part time instructor ref");
    InstructorPartTimeRef iptref =
       (InstructorPartTimeRef) InstructorPartTimeRef.getORADataFactory()
                               .create(g_ref.toDatum(conn), OracleTypes.REF);
    // or just use:  InstructorPartTimeRef iptref = 
InstructorPartTimeRef.cast(g_ref);

    Oracle.close();
  }
}

SQLData Object Types and Inheritance

As described earlier, if you use the JPublisher -usertypes=jdbc setting instead of -usertypes=oracle, the custom Java class that JPublisher generates will implement the standard SQLData interface instead of the Oracle ORAData interface. The SQLData standard readSQL() and writeSQL() methods provide equivalent functionality to the ORAData/ORADataFactory create() and toDatum() methods for reading and writing data.

As is the case when JPublisher generates ORAData classes corresponding to a hierarchy of SQL object types, when JPublisher generates SQLData classes corresponding to a SQL hierarchy, the Java types will follow the same hierarchy as the SQL types.

SQLData implementations do not, however, offer the implicit mapping intelligence that JPublisher automatically generates into ORAData classes (as described in "ORAData Object Types and Inheritance").

In a SQLData scenario, you must manually provide a type map to ensure the proper mapping between SQL object types and Java types. In a JDBC application, you can properly initialize the default type map for your connection, or you can explicitly provide a type map as a getObject() input parameter. (See the Oracle9i JDBC Developer's Guide and Reference for information.) In a SQLJ application, use a type map resource that is similar in nature to a properties file. (See the Oracle9i SQLJ Developer's Guide and Reference for information.)

In addition, be aware that there is no support for strongly typed object references in a SQLData implementation. All object references are weakly typed java.sql.Ref instances.

Effect of Using SQL FINAL, NOT FINAL, INSTANTIABLE, NOT INSTANTIABLE

This section discusses the effect on JPublisher-generated wrapper classes of using the SQL modifiers FINAL, NOT FINAL, INSTANTIABLE, or NOT INSTANTIABLE.

Using the SQL modifier FINAL or NOT FINAL on a SQL type or on a method of a SQL type has no effect on the generated Java wrapper code. This is so JPublisher users are able in all cases to customize the generated Java wrapper class through subclassing and overriding the generated behavior.

Using the SQL modifier NOT INSTANTIABLE on a method of a SQL type results in no code being generated for that method in the Java wrapper class. Therefore, you must cast to some wrapper class that corresponds to an instantiable SQL subtype in order to call such a method.

Using NOT INSTANTIABLE on a SQL type results in the corresponding wrapper class being generated with protected constructors. This will remind you that instances of that class can only be created through subclasses that correspond to instantiable SQL types.

Backward Compatibility and Migration

This section discusses issues of backward compatibility, compatibility between JDK versions, and migration between Oracle8i and Oracle9i releases of JPublisher.

JPublisher Backward Compatibility

The JPublisher runtime is packaged with Oracle JDBC in the classes111, classes12, or ojdbc14 library. Code generated by an earlier version of JPublisher will:

If you use an earlier release of the JPublisher runtime and Oracle JDBC in generating code, the code will be compilable against that version of the JPublisher runtime. Specifically, when you use an Oracle8i JDBC driver, JPublisher will generate code for the now-deprecated CustomDatum interface, not the ORAData interface that replaced it.

JPublisher Compatibility Between JDK Versions

Generally speaking, .sqlj files generated by JPublisher can be translated under either JDK 1.1.x (assuming you are not using JDBC 2.0-specific types), or JDK 1.2.x or higher. However, if you intend to translate and compile in separate steps (setting -compile=false in SQLJ so that only .java files, not .class files, are produced), then you must use the same JDK version for compilation as for translation unless you use a special JPublisher option setting.

In this situation (translating and compiling in separate steps), the JPublisher default setting -context=DefaultContext results in generation of .sqlj files that are completely compatible between JDK 1.1.x and JDK 1.2.x or higher. (With this setting, for example, you could translate against JDK 1.1.x but still compile against JDK 1.2.x successfully.)

In this situation, all generated .sqlj files use the sqlj.runtime.ref.DefaultContext class for all connection contexts. This is as opposed to the setting -context=generated, which results in each generated .sqlj file declaring its own connection context inner class. This was the Oracle8i JPublisher default behavior, and is what makes translated .java code incompatible between JDK 1.1.x and 1.2.x or higher.

See "SQLJ Connection Context Classes (-context)" for more information about the -context option.


Important:

With some JPublisher option settings under JDK 1.1.x there is risk of memory leakage caused by SQLJ connection context instances that are not closed. See "Releasing Connection Context Resources" for information.


See the Oracle9i SQLJ Developer's Guide and Reference for general information about connection contexts.

Migration Between Oracle8i JPublisher and Oracle9i JPublisher

In Oracle9i JPublisher, default option settings and some features of the generated code have changed. If you wrote an application using JPublisher release 8.1.7 or earlier, it is unlikely that you will be able to simply re-run JPublisher in Oracle9i and have the generated classes still work within your application. This section describes how to modify your JPublisher option settings or your application code appropriately.


Note:

Also see "Changes in User-Written Subclasses of Oracle9i JPublisher-Generated Classes" for differences between Oracle8i functionality and Oracle9i functionality for classes that extend JPublisher-generated classes.


Changes in Behavior in Oracle9i JPublisher

Be aware of the following changes in JPublisher behavior in Oracle9i:

See the following sections, "Individual Settings to Force JPublisher Behavior as in Previous Releases" below and "Oracle8i Compatibility Mode", for information about how to revert to Oracle8i behavior instead.

Individual Settings to Force JPublisher Behavior as in Previous Releases

In Oracle9i, if you want JPublisher to behave as it did in release 8.1.7 and prior, there are a number of individual backward-compatibility options you can set. This is detailed in Table 2-2. See descriptions of these options under "Detailed Descriptions of General JPublisher Options" for more information.

See "Oracle8i Compatibility Mode" for a single setting that results in the same behavior as for Oracle8i JPublisher--backward-compatible code generation plus behavior that is equivalent to what would happen with the combination of these individual option settings.

Table 2-2 JPublisher Backward-Compatibility Options  
Option Setting Behavior

-context=generated

This results in the declaration of an inner class, _Ctx, for SQLJ connection contexts. This is used instead of the default DefaultContext class or user-specified connection context classes.

-methods=always

This forces generation of .sqlj (as opposed to .java) source files for all JPublisher-generated classes, regardless of whether the underlying SQL object or package actually defines any methods.

-compatible=customdatum

For Oracle-specific object wrappers, this results in JPublisher implementing the deprecated (but still supported) oracle.sql.CustomDatum and CustomDatumFactory interfaces instead of the oracle.sql.ORAData and ORADataFactory interfaces.

-dir=.

Setting this option to "." (a period or "dot") results in generation of output files into a hierarchy under the current directory, as was the default behavior in Oracle8i.

Unless you have a compelling reason to use the backward-compatibility settings, however, it is recommended that you accept the current default (or other) settings.

Oracle8i Compatibility Mode

Either of the JPublisher option settings -compatible=both8i and -compatible=8i results in what is called Oracle8i compatibility mode.

See "Backward-Compatible Oracle Mapping for User-Defined Types (-compatible)" for more information about this option.

For use of this mode to be permissible, however, at least one of the following circumstances must hold:

or:

or:

JPublisher has the following functionality in Oracle8i compatibility mode:

In general, if you must choose Oracle8i compatibility mode, it is strongly recommended that you use the setting -compatible=both8i. This will permit your application to work in a middle-tier environment such as the Oracle9i Application Server, where JDBC connections are obtained through data sources and likely will be wrapped using oracle.jdbc.OracleXxxx interfaces. CustomDatum implementations do not support such wrapped connections.


Note:

The setting -compatible=both8i requires Oracle JDBC 9.0.1 or higher.


Oracle8i compatibility mode is now the only way for a connection context instance _ctx to be declared in JPublisher-generated code--there is no other option setting to accomplish this particular Oracle8i behavior. The _ctx instance might be useful if you have legacy code that depends on it, but otherwise you should obtain connection context instances through the getConnectionContext() method.


Important:

There are circumstances where you should not use Oracle8i compatibility mode. If your environment uses any of the following:

  • JDK 1.1.x, the SQLJ generic runtime library, or the SQLJ runtime11 library

and you use the following SQLJ translator setting:

  • -codegen=iso

as well as any of the following JPublisher settings:

  • -methods=named (or some), -methods=true (or all), or -methods=always

then there may be significant memory leakage caused by implicit connection context instances that are not closed.

Avoid the -compatible=8i and -compatible=both8i settings in these circumstances, and use the setConnectionContext() and release() methods in manipulating connection contexts. For more information, see "Use of Connection Contexts and Instances in SQLJ Code Generated by JPublisher".


JPublisher Limitations

This section summarizes limitations in the Oracle9i release 2 version of JPublisher.


Go to previous page Go to next page
Oracle
Copyright © 1999, 2002 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback