Oracle9i JPublisher User's Guide Release 2 (9.2) Part Number A96658-01 |
|
This chapter provides a detailed discussion of JPublisher's underlying concepts and of its operation. The following topics are covered:
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 get
XXX
()
and set
XXX
()
methods for the object attributes. The class that JPublisher generates for a VARRAY or nested table type will have get
XXX
()
and set
XXX
()
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.
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.
The following notes correspond to marked entries in the preceding table.
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
.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".BOOLEAN
to SQL NUMBER
and Java boolean
is defined in the default JPublisher type map. It uses conversion functions from the SYS.SQLJUTL
package.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".
Note: The Object JDBC and |
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:
CHAR
, VARCHAR
, VARCHAR2
, CHARACTER
NCHAR
, NVARCHAR2
DATE
DECIMAL
, DEC
, NUMBER
, NUMERIC
DOUBLE PRECISION
, FLOAT
INTEGER
, SMALLINT
, INT
REAL
RAW
, LONG RAW
CLOB
BLOB
BFILE
NCLOB
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.
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:
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".
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:
OUT
or IN OUT
parameter position, you must specify the maximum number of elements. (This is optional otherwise.) This is defined using the customary syntax for Java array allocation. For example, you could specify int[100]
to denote a type that can accommodate up to 100 elements, or oracle.sql.CHAR[20]
for up to 20 elements.IN
arguments, you could specify String[](30)
. Or specify oracle.sql.CHAR[20](255)
for an indexed-by table of maximum length 20, each of whose elements will not exceed 255 bytes.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".
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.
SCHEMA.PACKAGE.TYPE.
You must be able to directly map this type to the Java wrapper class. For example, if the SQL type is NUMBER
, then the corresponding Java class could be types such as int
, double
, Integer
, Double
, java.math.BigDecimal
, or oracle.sql.NUMBER
. Or, if the SQL type is an object type, then the corresponding Java class would be a corresponding object wrapper class--typically generated by JPublisher--that implements the ORAData
or SQLData
interface.
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:
BOOLEAN
boolean
INTEGER
JDBC considers boolean
values as special numeric values.
INT2BOOL
, that maps from SQL to PL/SQL (from NUMBER
to BOOLEAN
)
Here is the code for that function:
function int2bool(i INTEGER) return BOOLEAN is begin if i is null then return null; else return i<>0; end if; end int2bool;
BOOL2INT
, that maps from PL/SQL to SQL (from BOOLEAN
to NUMBER
):
Here is the code for that function:
function bool2int(b BOOLEAN) return INTEGER is begin if b is null then return null; elsif b then return 1; else return 0; end if; end bool2int;
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 |
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:
-plsqlfile=
filename
This specifies the name of the file into which JPublisher generates PL/SQL code. If this file already exists, it will be overwritten. If no file name is specified, JPublisher will write to a file named plsql_wrapper.sql
. Remember that you will have to run this SQL script in order to install the PL/SQL wrappers in the database.
-plsqlpackage=
plsql_package
This specifies the name of the PL/SQL package into which JPublisher generates PL/SQL code. If no package name is provided, JPublisher will use JPUB_PLSQL_WRAPPER
.
-plsqlmap=
flag
This specifies how JPublisher generates PL/SQL wrapper procedures and functions. The flag
setting can be any of the following:
true
(default)--JPublisher will generate PL/SQL wrapper procedures and functions as needed and use conversion functions only when that is sufficient.false
--JPublisher will not generate PL/SQL wrapper procedures or functions. If it encounters a PL/SQL type in a signature that cannot be supported by conversion functions alone, then it will skip generation of Java code for the particular procedure or function.always
--JPublisher will generate a PL/SQL wrapper procedure or function for every stored procedure or function that uses a PL/SQL type. This is useful for generating a "proxy" PL/SQL package that complements an original PL/SQL package, providing Java-accessible signatures for those functions or procedures that were not accessible from JDBC or SQLJ in the original package.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.
PLSQL_RECORD
can be mapped to. For example:
create TYPE sql_record as object ( sql_number NUMBER, sql_name VARCHAR2(60));
SqlRecord
for SQL_RECORD
as follows:
jpub -u scott/tiger -s SQL_RECORD:SqlRecord
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;
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
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
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.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.
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
.
SqlIndexby
for SQL_INDEXBY
, you can run JPublisher as follows:
jpub -u scott/tiger -s SQL_INDEXBY:SqlIndexby
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;
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
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.
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 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.
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:
For more information on this technique, see "Example: Using Datatypes Unsupported by JDBC".
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:
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.
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.
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:
this
is returned as the result of the wrapper method.
As an example, assume the SQL object type MYTYPE
has the following member procedure:
MEMBER PROCEDURE f1(y IN OUT INTEGER);
Also assume that JPublisher generates a corresponding Java class MyJavaType
. This class would define the following method:
public MyJavaType f1(int[] y)
The f1
method returns the modified this
object value as a MyJavaType
instance.
this
is returned in a single-element array, passed as an extra argument (the last argument) to the wrapper method.
Assume the SQL object type MYTYPE
has the following member function:
MEMBER FUNCTION f2(x IN INTEGER) RETURNS VARCHAR2;
Then the corresponding Java class MyJavaType
would define the following method:
public String f2(int x, MyJavaType[] newValue)
The f2
method returns the VARCHAR2
function-return as a Java string, and returns the modified this
object value as an array element in the MyJavaType
array.
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.
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.
Be aware of the following for JPublisher-generated SQLJ classes:
release()
method. In creating and using an instance of a JPublisher-generated wrapper class, if you do not use the constructor with the DefaultContext
argument, and you do not subsequently call the setConnectionContext()
method with a connection context argument, and you then invoke a wrapper method, then the wrapper object will implicitly construct a DefaultContext
instance. In this case, you should use the release()
method to release the connection context instance when it is no longer needed.
In other words, one of the following is recommended:
or:
setConnectionContext()
method.or:
See "Use of Connection Contexts and Instances in SQLJ Code Generated by JPublisher" for more information.
DefaultContext
instance or user-specified-class instance, there is a constructor that simply takes a ConnectionContext
instance (an instance of any class that implements the standard sqlj.runtime.ConnectionContext
interface).Take the following steps to use a class that JPublisher generates for a PL/SQL package:
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.
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:
OUT
argument or as the function call return of a JPublisher-generated wrapper method, or by retrieving the SQL object through #sql
statements you write, or by retrieving the SQL object through JDBC calls you write.or:
set
XXX
()
methods, or construct the Java object with the constructor that accepts values for all of the object attributes. Typically, you would subsequently use the setConnection()
or setConnectionContext()
method to associate the object with a database connection before invoking any of its wrapper methods. If you do not explicitly associate the object with a JDBC or SQLJ connection and invoke a method on it, it will become implicitly associated with the default (static) SQLJ connection context.
Other constructors for the class associate a connection with the class instance. One constructor takes a DefaultContext
instance (or an instance of a class specified through the -context
option when you ran JPublisher), and one constructor takes a Connection
instance. The constructor that takes a Connection
instance is provided 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". |
Once you have initialized your Java object, you can:
#sql
statements.There is a Java attribute for each attribute of the corresponding SQL object type, with get
XXX
()
and set
XXX
()
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.
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:
-context=DefaultContext
(the default) results in JPublisher using instances of the standard sqlj.runtime.ref.DefaultContext
class.sqlj.runtime.ConnectionContext
interface) results in JPublisher using instances of that class.-context=generated
results in the following declaration in the JPublisher-generated class.
#sql static context _Ctx;
In this case, JPublisher uses instances of the _Ctx
class for connection context instances.
Note: It is no longer routine (as it was in Oracle8i JPublisher) for JPublisher to declare a connection context instance Unless you have legacy code that depends on |
See "SQLJ Connection Context Classes (-context)" for more information about the -context
option.
Consider the following points in using SQLJ connection context instances or JDBC connection instances in instances of JPublisher-generated wrapper classes:
setConnectionContext()
method you can use to explicitly specify a SQLJ connection context instance. (This will not be necessary if you have already specified a connection context instance through the constructor.)
This method is defined as follows:
public void setConnectionContext(conn_ctxt_instance);
This installs the passed connection context instance as the SQLJ connection context in the object wrapper instance. The connection context instance must be an instance of the class specified through the -context
option for JPublisher connection contexts (typically DefaultContext
).
Be aware that the underlying JDBC connection must be compatible with the connection used to materialize the database object in the first place. Specifically, some objects may have attributes, such as object reference types or BLOBs, that are only valid for a particular connection.
The getConnectionContext()
method returns an instance of the connection context class specified through the JPublisher -context
option (typically DefaultContext
).
The returned connection context instance might either be an instance that was set explicitly through the setConnectionContext()
method, or an instance that was created implicitly by JPublisher.
Note: These methods are available only in generated |
getConnectionContext()
method is called.
In this circumstance, you must be careful to use the release()
method to free resources in the SQLJ runtime that would otherwise result in a memory leak.
See "Releasing Connection Context Resources" (below) and "SQLJ Connection Context Classes (-context)" for related information.
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:
runtime
library (as opposed to runtime12
, runtime11
, and so on) when you execute the generated class or classes.and:
DefaultContext
(or some other connection context class you specified through the -context
option when you ran JPublisher).and:
and:
setConnectionContext()
method of the wrapper instance to explicitly set a connection context instance.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.)
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:
OUT
argument or as the function call return of a JPublisher-generated wrapper method in some other class, or by retrieving the SQL object through #sql
statements you write, or by retrieving the SQL object through JDBC calls you write.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 set
XXX
()
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:
#sql
statements.get
XXX
()
and set
XXX
()
accessor methods.getArray()
, setArray()
, getElement()
, and setElement()
methods.
The getArray()
and setArray()
methods return or modify an array as a whole. The getElement()
and setElement()
methods return or modify individual elements of the array. Then re-insert the Java array into the database if you want to update the data there.
getValue()
and setValue()
methods.
The getValue()
method returns a copy of the SQL object to which the reference refers. The setValue()
method updates a SQL object type instance in the database, taking as input an instance of the Java class that represents the object type. Unlike the get
XXX
()
and set
XXX
()
accessor methods of a class generated for an object type, the getValue()
and setValue()
methods read and write SQL objects.
Note that both, getValue()
and setValue()
will result in a database round trip for reading and, respectively, writing the value of the underlying database object that the reference points to.
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.
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.
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:
MyAddressRef
rather than JAddressRef
.MyAddress
class instead of the JAddress
class to represent attributes whose SQL type is ADDRESS
, or to represent VARRAY and nested table elements whose SQL type is ADDRESS
.MyAddress
factory instead of the JAddress
factory when the ORADataFactory
interface is used to construct Java objects whose SQL type is ADDRESS
.JAddress
class. In addition, it also generates an initial version of the code for the MyAddress
class, which you can then modify to insert your own additional functionality. If the source file for the MyAddress
class already exists, however, it will be left untouched by JPublisher.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".
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:
ORAData
interface or the SQLData
interface. This happens implicitly by inheriting the necessary methods from the superclass.ORAData
class, the subclass will also implement the ORADataFactory
interface.
An implementation of the ORADataFactory
create()
method might look as follows.
public ORAData create(Datum d, int sqlType) throws SQLException { return create(new UserClass(),d,sqlType); }
When the class is part of an inheritance hierarchy, however, the generated method changes to protected ORAData createExact()
with the same signature and body as create()
above.
The following code shows a more efficient implementation, where an initialized UserClass
instance is created through the UserClass(boolean)
constructor. This constructor is provided in JPublisher-generated code, including the superclass that UserClass
extends. Using this constructor ensures that a UserClass
instance is not needlessly created if the data object is null, or needlessly re-initialized if the data object is non-null.
protected UserClass(boolean init) { super(boolean); } public ORAData create(Datum d, int sqlType) throws SQLException { return (d==null) ? null : create(new UserClass(false),d,sqlType); }
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 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:
_ctx
connection context field with use of the provided getConnectionContext()
method. The _ctx
field is no longer supported under Oracle9i.create()
method with a call to a superclass create()
method.
Assume that in the example below, UserClass
extends BaseClass
. Instead of writing the following method in UserClass
:
public CustomDatum create(Datum d, int sqlType) throws SQLException { if (d == null) return null; UserClass o = new UserClass(); o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory); o._ctx = new _Ctx(((STRUCT) d).getConnection()); return o; }
supply the following:
public CustomDatum create(Datum d, int sqlType) throws SQLException { return create(new UserClass(),d,sqlType); }
or, if the class is part of an inheritance hierarchy, write the following:
protected CustomDatum createExact(Datum d, int sqlType) throws SQLException { return create(new UserClass(),d,sqlType); }
In addition, in .sqlj
files, JPublisher now generates a protected
constructor with a boolean argument that specifies whether the object must be initialized:
protected BaseClass(boolean init) { ... }
You can use this to optimize the UserClass
code as described in "Format of the Class that Extends the Generated Class".
getConnectionContext()
method, Oracle9i JPublisher provides a getConnection()
method that can be used to obtain the JDBC connection associated with the object.Oracle9i JPublisher provides the following utility methods in generated .sqlj
files:
setFrom(
anotherObject
)
This initializes the calling object from another object of the same base type, including connection and connection context information. An existing, implicitly created, connection context object on the calling object is freed.
setValueFrom(
anotherObject
)
This initializes the underlying field values of the calling object from another object of the same base type. This method does not transfer connection or connection context information.
setContextFrom(
anotherObject
)
This initializes the connection and connection context information on the calling object from the connection setting of another object of the same base type. An existing, implicitly created, connection context object on the calling object is freed. This method does not transfer any information related to the object value.
Note that there is semantic equivalence between the following:
x.setFrom(y);
and the following:
x.setValueFrom(y); x.setContextFrom(y);
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.
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.
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.
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.
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.
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.
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.
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:
getValue()
method in StudentRef
to return a Student
object when the method it would override in the PersonRef
class returns a Person
object, even though this is arguably a sensible thing to do.setValue()
method in StudentRef
would not override the setValue()
method in PersonRef
, because the two methods have different signatures.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.
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.
xref
from its strong XxxxRef
type to the weak oracle.sql.REF
type:
oracle.sql.REF ref = (oracle.sql.REF) xref.toDatum(conn);
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. |
The following example, including SQL definitions and Java code, illustrates the points of the preceding discussion.
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));
Assume the following mappings when you run JPublisher:
Person_t:Person,instructor_t:Instructor,instructorPartTime_t:InstructorPartTime, graduate_t:Graduate,studentPartTime_t:StudentPartTime
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(); } }
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.
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.
This section discusses issues of backward compatibility, compatibility between JDK versions, and migration between Oracle8i and Oracle9i releases of JPublisher.
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.
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.
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. |
Be aware of the following changes in JPublisher behavior in Oracle9i:
_Ctx
for every object type. Instead, it uses the connection context class sqlj.runtime.ref.DefaultContext
throughout.
Also, user-written code must call the getConnectionContext()
method to have a connection context handle, instead of using the _ctx
connection context field that was declared under Oracle8i code generation. See "Considerations in Using Connection Contexts and Connection Instances" for more information about the getConnectionContext()
method.
-methods=true
, .java
files are generated instead of .sqlj
files if the underlying SQL object type or PL/SQL package does not define any methods. (But a setting of -methods=always
will always result in .sqlj
files being produced.)oracle.sql.ORAData
interface instead of the deprecated oracle.sql.CustomDatum
interface.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.
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.
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.
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:
runtime12
or runtime12ee
library, or will execute in the Oracle9i release of the server-side Oracle JVM.or:
JPublisher has the following functionality in Oracle8i compatibility mode:
CustomDatum
and CustomDatumFactory
interfaces instead of the ORAData
interface (as with the -compatible=customdatum
setting). In addition, if you choose the setting -compatible=both8i
, the generated code will also implement the ORAData
interface, though not ORADataFactory
.-methods=true
setting, it will always generate SQLJ source code for a SQL object type, even if the object type does not define any methods (as with -methods=always
).-context=generated
):
#sql static context _Ctx; protected _Ctx _ctx;
ConnectionContext
instance (an instance of any class implementing the standard sqlj.runtime.ConnectionContext
interface) as input. In Oracle9i, the constructor accepts only a DefaultContext
instance or an instance of the class specified through the -context
option when JPublisher was run.By contrast, Oracle9i JPublisher provides both a setConnectionContext()
method for explicitly setting the connection context instance for an object, and a release()
method for releasing an implicitly created connection context instance of an object.
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.Oracle
Xxxx
interfaces. CustomDatum
implementations do not support such wrapped connections.
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: and you use the following SQLJ translator setting: as well as any of the following JPublisher settings: then there may be significant memory leakage caused by implicit connection context instances that are not closed. Avoid the |
This section summarizes limitations in the Oracle9i release 2 version of JPublisher.
Note that JPublisher has predefined support for mapping PL/SQL BOOLEAN
to Java boolean
using conversion functions in the SYS.SQLJUTL
package. In general, if JPublisher encounters wrapper methods that use one or more unrecognized datatypes, it will not generate a corresponding Java method and will display one or more error messages instead.
For more information about datatype support, see "SQL and PL/SQL Mappings to Oracle and JDBC Types".
INPUT
file error reporting is sometimes incomplete.
JPublisher reports most, but not all, errors in the INPUT
file. The few errors in the INPUT
file that are not reported by JPublisher are described in "INPUT File Precautions".
-omit_schema_names
option behaves as a boolean option, you cannot set it =true
or =false
(unlike other boolean options). Simply specify "-omit_schema_names
" to enable it. The default is disabled. See "Omission of Schema Name from Generated Names (-omit_schema_names)" for information about this option.
|
Copyright © 1999, 2002 Oracle Corporation. All Rights Reserved. |
|