Oracle9i JPublisher User's Guide Release 1 (9.0.1) Part Number A90214-01 |
|
This chapter provides an overview of JPublisher followed by more detailed discussion of its operation. The following topics are covered:
This section provides a basic understanding of what JPublisher is for and what it accomplishes, covering the following topics:
JPublisher is a utility, written entirely in Java, that generates Java classes to represent the following user-defined database entities in your Java program:
JPublisher enables you to specify and customize the mapping of SQL object types, object reference types, and collection types (VARRAYs or nested tables) to Java classes in a strongly typed paradigm.
JPublisher generates getXXX()
and setXXX()
accessor methods for each attribute of an object type. If your object types have stored procedures, JPublisher can generate wrapper methods to invoke the stored procedures. A wrapper method is a method that invokes a stored procedure that executes in Oracle9i.
JPublisher can also generate classes for PL/SQL packages. These classes have wrapper methods to invoke the stored procedures in the PL/SQL packages.
The wrapper methods JPublisher generates contain SQLJ code, so when JPublisher generates wrapper methods, it generally produces .sqlj
source files. This is true for classes representing PL/SQL packages or object types that define methods, unless you specify (through the -methods
option) that JPublisher should not generate wrapper methods.
If no wrapper methods are generated, JPublisher produces .java
source files. This is true for classes representing object types without methods, object reference types, or collection types, or for classes where the -methods
option is off.
Instead of using JPublisher-generated classes directly, you can:
oracle.sql
package contains simple generic classes that represent object, object reference, and collection types. If these classes meet your requirements, you do not need JPublisher.
JPublisher allows your Java language applications to employ user-defined object types in Oracle9i. If you intend to have your Java-language application access object data, then it must represent the data in a Java format. JPublisher helps you do this by creating the mapping between object types and Java classes, and between object attribute types and their corresponding Java types.
Classes generated by JPublisher implement either the oracle.sql.ORAData
interface or the java.sql.SQLData
interface, depending on how you set the JPublisher options. Either interface makes it possible to transfer object type instances between the database (or middle-tier database cache) and your Java program. For more information about the ORAData
and SQLData
interfaces, see the Oracle9i JDBC Developer's Guide and Reference.
You might want to call stored procedures in a PL/SQL package from your Java application. The stored procedure can be a PL/SQL subprogram or a Java method that has been published to SQL. Java arguments and functions are passed to and returned from the stored procedure.
To help you do this, you can direct JPublisher to create a class containing a wrapper method for each subprogram in the package. The wrapper methods generated by JPublisher provide a convenient way to invoke PL/SQL stored procedures from Java code or to invoke a Java stored procedure from a client Java program.
If you call PL/SQL code that includes top-level subprograms (subprograms not in any PL/SQL package), JPublisher generates a single class containing wrapper methods for the top-level subprograms you request.
JPublisher connects to a database and retrieves descriptions of the SQL object types or PL/SQL packages that you specify on the command line or from an input file. By default, JPublisher connects to the database by using the JDBC OCI driver, which requires an Oracle client installation, including Oracle Net and required support files. If you do not have an Oracle client installation, JPublisher can use the Oracle JDBC Thin driver.
JPublisher generates a Java class for each SQL object type it translates. The Java class includes code required to read objects from and write objects to the database. When you deploy the generated JPublisher classes, your JDBC driver installation includes all the necessary runtime files. If you create wrapper methods (Java methods to wrap stored procedures or functions of the SQL object type), JPublisher generates SQLJ source code so you must additionally have the SQLJ runtime libraries.
When you call a wrapper method, the SQL value for the object is sent to the server, along with any IN
our IN OUT
arguments. Then the method (stored procedure or function) is invoked, and the new object value is returned to the client, along with any OUT
or IN OUT
arguments.
JPublisher also generates a class for each PL/SQL package it translates. The class includes code to invoke the package methods on the server. IN
arguments for the methods are transmitted from the client to the server, and OUT
arguments and results are returned from the server to the client.
The next section furnishes a general description of the source files that JPublisher creates for object types and PL/SQL packages.
The number of files JPublisher produces depends on whether you request ORAData
classes (classes that implement the oracle.sql.ORAData
interface) or SQLData
classes (classes that implement the standard java.sql.SQLData
interface).
The ORAData
interface supports SQL object, object reference, and collection types in a strongly typed way. That is, for each specific object, object reference, or collection type in the database, there is a corresponding Java type. The SQLData
interface, on the other hand, supports only SQL object types in a strongly typed way. All object reference types are represented generically as java.sql.Ref
instances, and all collection types are represented generically as java.sql.Array
instances. Therefore, JPublisher generates classes for object reference and collection types only if it is generating ORAData
classes.
When you run JPublisher for a user-defined object type and you request ORAData
classes, JPublisher automatically creates the following:
For
Advantages of using strongly typed instead of weakly typed references are described in "Strongly Typed Object References for ORAData Implementations".
Note:
ORAData
implementations, a strongly typed reference class is always generated, regardless of whether the SQL object type uses references.
If, instead, you request SQLData
classes, JPublisher does not generate the object reference class and does not generate classes for nested collection attributes.
When you run JPublisher for a user-defined collection type, you must request ORAData
classes. JPublisher automatically creates the following:
When you run JPublisher for a PL/SQL package, it automatically creates the following:
JPublisher requires that Oracle SQLJ and Oracle JDBC also be installed on your system and in your classpath appropriately. You will need the following libraries (all available as either .zip
or .jar
):
translator
)
runtime12
, runtime12ee
, runtime11
, runtime
, or runtime-nonoracle
)
classes12
or classes111
)
"12" refers to versions for JDK 1.2.x; "11" and "111" refer to versions for JDK 1.1.x. See the Oracle9i SQLJ Developer's Guide and Reference for more information about these files.
When you use Oracle9i JPublisher, you should also use the equivalent version of SQLJ, because these two products are always installed together. To use all features of JPublisher, you also need:
If you are using only some features of JPublisher, your requirements might be less stringent:
SQLData
classes, and you never use the java.sql.Blob
and java.sql.Clob
classes, you can use JDK version 1.1.x instead of JDK 1.2.x.
ORAData
interface (or the deprecated CustomDatum
interface), you should be able to use a non-Oracle JDBC driver or a non-Oracle SQLJ implementation. When running code generated by JPublisher, you should even be able to connect to a non-Oracle database; however, JPublisher itself must connect to an Oracle database. Oracle does not test or support configurations that use non-Oracle components.
SQLData
interface (that is, you use JPublisher to generate only classes that implement the ORAData
interface or the deprecated CustomDatum
interface), you can use Oracle database version 8.1.5 with JDBC version 8.1.5 and JDK version 1.1.x or higher. (Be aware that the ORAData
interface requires the Oracle9i or higher JDBC implementation.)
-methods=false
), or if your object types define no methods, then JPublisher will not generate wrapper methods or produce any .sqlj
files. In this case, you would not need the SQLJ translator. See "Generate Classes for Packages and Wrapper Methods for Methods (-methods)" for information about the -methods
option.
You can specify input options on the command line and in the properties file. In addition to producing .sqlj
and .java
files for the translated objects, JPublisher writes the names of the translated objects and packages to standard output.
You can specify JPublisher options on the command line or in a properties file. "JPublisher Options" describes all the JPublisher options.
In addition, you can use a file known as the INPUT
file to specify the object types and PL/SQL packages JPublisher should translate. It also controls the naming of the generated packages and classes. "INPUT File Structure and Syntax" describes INPUT
file syntax.
A properties file is an optional text file that you can use to specify frequently-used options. You name the properties file on the command line. JPublisher processes the properties file as if its contents were inserted, in sequence, on the command line at that point. For more information about this file, see "Properties File Structure and Syntax".
JPublisher generates a Java class for each object type that it translates. For each object type, JPublisher generates a <
type
>.sqlj
file (or a <
type
>.java
file if wrapper methods were suppressed or do not exist) for the class code and a <
type
>Ref.java
file for the code for the REF class of the Java type. For example, if you define an EMPLOYEE
SQL object type, JPublisher generates an employee.sqlj
file (or an employee.java
file) and an employeeRef.java
file. Note that the case of Java class names produced by JPublisher is determined by the -case
option. See "Case of Java Identifiers (-case)".
For each collection type (nested table or VARRAY) it translates, JPublisher generates a <
type
>.java
file. For nested tables, the generated class has methods to get and set the nested table as an entire array and to get and set individual elements of the table. JPublisher translates collection types when generating ORAData
classes, but not when generating SQLData
classes.
For PL/SQL packages, JPublisher generates classes containing wrapper methods as .sqlj
files.
When JPublisher generates the class files and wrappers, it also writes the names of the translated types and packages to standard output.
JPublisher offers different categories of datatype mappings from SQL to Java. JPublisher options to specify these mappings are described below, under "Detailed Descriptions--Options That Affect Datatype Mappings".
Each type mapping option has at least two possible values: jdbc
and oracle
. The -numbertypes
option has two additional alternatives: objectjdbc
and bigdecimal
.
The following sections describe these categories of mappings.
For more information about datatype mappings, see "Details of Datatype Mapping".
The JDBC mapping maps most numeric datatypes to Java primitive types such as int
and float
, and maps DECIMAL
and NUMBER
to java.math.BigDecimal
. LOB types and other non-numeric built-in types map to standard JDBC Java types such as java.sql.Blob
and java.sql.Timestamp
. For object types, JPublisher generates SQLData
classes. Predefined datatypes that are Oracle extensions (such as BFILE
and ROWID
) do not have JDBC mappings, so only the oracle.sql.*
mapping is supported for these types.
The Java primitive types used in the JDBC mapping do not support null values and do not guard against integer overflow or floating-point loss of precision. If you are using the JDBC mapping and you attempt to call an accessor or method to get an attribute of a primitive type (short
, int
, float,
or double
) whose value is null
, an exception is thrown. If the primitive type is short
or int
, then an exception is thrown if the value is too large to fit in a short
or int
variable.
The Object JDBC mapping maps most numeric datatypes to Java wrapper classes such as java.lang.Integer
and java.lang.Float
, and maps DECIMAL
and NUMBER
to java.math.BigDecimal
. It differs from the JDBC mapping only in that it does not use primitive types.
When you use the Object JDBC mapping, all your returned values are objects. If you attempt to get an attribute whose value is null
, a null object is returned.
The Java wrapper classes used in the Object JDBC mapping do not guard against integer overflow or floating-point loss of precision. If you call an accessor method to get an attribute that maps to java.lang.Integer
, an exception is thrown if the value is too large to fit.
This is the default mapping for numeric types.
BigDecimal mapping, as the name implies, maps all numeric datatypes to java.math.BigDecimal
. It supports null values and very large values.
In the Oracle mapping, JPublisher maps any numeric, LOB, or other built-in type to a class in the oracle.sql
package. For example, the DATE
type is mapped to oracle.sql.DATE
, and all numeric types are mapped to oracle.sql.NUMBER
. For object, collection, and object reference types, JPublisher generates ORAData
classes.
Because the Oracle mapping uses no primitive types, it can represent a null value as a Java null
in all cases. Because it uses the oracle.sql.NUMBER
class for all numeric types, it can represent the largest numeric values that can be stored in the database.
Before you run JPublisher, you must create any new datatypes that you will require in the database. You must also ensure that any PL/SQL packages, methods, and subprograms that you want to invoke from Java are also installed in Oracle9i.
Use the SQL CREATE TYPE
statement to create object, VARRAY, and nested table types in the database. JPublisher supports the mapping of these datatypes to Java classes. JPublisher also generates classes for references to object types. REF types are not explicitly declared in SQL. For more information on creating object types, see the Oracle9i SQL Reference.
Use the CREATE PACKAGE
and CREATE PACKAGE BODY
statements to create PL/SQL packages and store them in the database. PL/SQL furnishes all the capabilities necessary to implement the methods associated with object types. These methods (functions and procedures) reside on the server as part of a user's schema. You can implement the methods in PL/SQL or Java.
Packages are often implemented to provide the following advantages:
For more information on PL/SQL and creating PL/SQL packages, see the PL/SQL User's Guide and Reference.
This section discusses the basic steps in using JPublisher, describes the command-line syntax, and concludes with a sample translation. The following topics are covered:
Here are the basic steps for translating and using code for user-defined types and PL/SQL packages. (User-defined types include Oracle objects and Oracle collections--VARRAYs and nested tables.)
.java
files for object reference, VARRAY, and nested table classes. If you instruct JPublisher to generate wrapper methods, it will generate .sqlj
files for packages and object types (assuming the object types have methods). If you instruct JPublisher to not generate wrapper methods, it will generate .java
files without wrapper methods for object types and will not generate classes for packages (because they contain only wrapper methods). For object types without methods, JPublisher generates .java
files in any case.
.sqlj
files, and the Java compiler compiles the .java
files.
Figure 1-1 illustrates the preceding steps.
Here are the three ways to represent user-defined object, collection, and object reference types in your Java program:
ORAData
interface.
JPublisher generates classes that implement the ORAData
interface. (You can also write them by hand, but this is not generally recommended.)
SQLData
interface, as described in the JDBC 2.0 API.
JPublisher generates classes for SQL object types that implement the SQLData
interface. (You can also write them by hand, but this is not generally recommended. Be aware that if you write them by hand, or if you generate classes for an inheritance hierarchy of object types, your classes must be registered using a type map.)
When you use the SQLData
interface, all object reference types are represented generically as java.sql.Ref
instances, and all collection types are represented generically as java.sql.Array
instances.
oracle.sql.*
classes.
You can use the oracle.sql.*
classes to represent user-defined types generically. The class oracle.sql.STRUCT
represents all object types, the class oracle.sql.ARRAY
represents all VARRAY and nested table types, and the class oracle.sql.REF
represents all REF types. These classes are immutable in the same way that java.lang.String
is.
Compared to classes that implement SQLData
, classes that implement ORAData
are fundamentally more efficient, because ORAData
classes avoid unnecessary conversions to native Java types. For a comparison of the SQLData
and ORAData
interfaces, see the Oracle9i JDBC Developer's Guide and Reference.
Compared to oracle.sql.*
classes, classes that implement ORAData
or SQLData
are strongly typed. Your connected SQLJ translator will detect an error at translation time if, for example, you mistakenly select a PERSON
object into an ORAData
object that represents an ADDRESS
.
JPublisher-generated classes that implement ORAData
or SQLData
have additional advantages:
getXXX()
and setXXX()
methods named after the particular attributes of the object. Note that you have to explicitly update the object in the database if there are any changes to its data.
ORAData
classes representing object reference types are not mutable, because an object reference does not have any subcomponents that could be sensibly modified. You can, however, use the setValue()
method of a reference object to change the database value that the reference points to.
For Oracle ORAData
implementations, JPublisher always generates strongly typed object reference classes as opposed to using the weakly typed oracle.sql.REF
class. This is to provide greater type safety and to mirror the behavior in SQL, where object references are strongly typed. The strongly typed classes (with names such as PersonRef
for references to PERSON
objects) are essentially wrappers for the REF
class.
In these strongly typed REF
wrappers, there is a getValue()
method that produces an instance of the SQL object that is referenced, in the form of an instance of the corresponding Java class. (Or, in the case of inheritance, perhaps as an instance of a subclass of the corresponding Java class.) For example, if there is a PERSON
object type in the database, with a corresponding Person
Java class, there will also be a PersonRef
Java class. The getValue()
method of the PersonRef
class would return a Person
instance containing the data for a PERSON
object in the database.
Whenever a SQL object type has an attribute that is an object reference, the Java class corresponding to the object type would have an attribute that is an instance of a Java class corresponding to the appropriate reference type. For example, if there is a PERSON
object with a MANAGER REF
attribute, then the corresponding Person
Java class will have a ManagerRef
attribute.
For standard SQLData
implementations, strongly typed object references are not supported--they are not part of the standard. JPublisher does not create a custom reference class; you must use java.sql.Ref
or oracle.sql.REF
as the reference type.
On most operating systems, you invoke JPublisher on the command line, typing jpub
followed by a series of options settings as follows:
jpub -option1=value1 -option2=value2 ...
JPublisher responds by connecting to the database and obtaining the declarations of the types or packages you specify, then generating one or more custom Java files and writing the names of the translated object types or PL/SQL packages to standard output.
Here is an example of a command that invokes JPublisher (this is a single wrap-around command line):
jpub -user=scott/tiger -input=demoin -numbertypes=oracle -usertypes=oracle -dir=demo -package=corp
You enter the command on one line, allowing it to wrap as necessary. For clarity, this chapter refers to the input file (the file specified by the -input
option) as the INPUT
file (to distinguish it from any other kinds of input files).
This command directs JPublisher to connect to the database with username SCOTT
and password TIGER
and translate datatypes to Java classes, based on instructions in the INPUT
file demoin
. The -numbertypes=oracle
option directs JPublisher to map object attribute types to Java classes supplied by Oracle, and the -usertypes=oracle
option directs JPublisher to generate Oracle-specific ORAData
classes. JPublisher places the classes that it generates in the package corp
in the directory demo
.
"JPublisher Options" describes each of these options in more detail.
This section provides a sample JPublisher translation of an object type. At this point, do not worry about the details of the code JPublisher generates. You can find more information about JPublisher input and output files, options, datatype mappings, and translation later in this manual.
Create the object type EMPLOYEE
:
CREATE TYPE employee AS OBJECT ( name VARCHAR2(30), empno INTEGER, deptno NUMBER, hiredate DATE, salary REAL );
The INTEGER
, NUMBER
, and REAL
types are all stored in the database as NUMBER
types, but after translation they have different representations in the Java program, based on your choice for the value of the -numbertypes
option.
JPublisher translates the types according to the following command line:
jpub -user=scott/tiger -dir=demo -numbertypes=objectjdbc -builtintypes=jdbc -package=corp -case=mixed -sql=Employee
(This is a single wrap-around command line.)
"JPublisher Options" describes each of these options in detail.
Note that because the EMPLOYEE
object type does not define any methods, JPublisher will generate a .java
file, not a .sqlj
file.
Because -dir=demo
and -package=corp
were specified on the JPublisher command line, the translated class Employee
is written to Employee.java
in the following location:
./demo/corp/Employee.java (UNIX) .\demo\corp\Employee.java (Windows NT)
The Employee.java
class file would contain the code below.
package corp; import java.sql.SQLException; import java.sql.Connection; import oracle.jdbc.OracleTypes; import oracle.sql.ORAData; import oracle.sql.ORADataFactory; import oracle.sql.Datum; import oracle.sql.STRUCT; import oracle.jpub.runtime.MutableStruct; public class Employee implements ORAData, ORADataFactory { public static final String _SQL_NAME = "SCOTT.EMPLOYEE"; public static final int _SQL_TYPECODE = OracleTypes.STRUCT; protected MutableStruct _struct; static int[] _sqlType = { 12, 4, 2, 91, 7 }; static ORADataFactory[] _factory = new ORADataFactory[5]; static final Employee _EmployeeFactory = new Employee(); public static ORADataFactory getORADataFactory() { return _EmployeeFactory; } /* constructor */ protected Employee(boolean init) { if(init) _struct = new MutableStruct(new Object[5], _sqlType, _factory); } public Employee() { this(true); } /* ORAData interface */ public Datum toDatum(Connection c) throws SQLException { return _struct.toDatum(c, _SQL_NAME); } /* ORADataFactory interface */ public ORAData create(Datum d, int sqlType) throws SQLException { return create(null, d, sqlType); } protected ORAData create(Employee o, Datum d, int sqlType) throws SQLException { if (d == null) return null; if (o == null) o = new Employee(false); o._struct = new MutableStruct((STRUCT) d, _sqlType, _factory); return o; } /* accessor methods */ public String getName() throws SQLException { return (String) _struct.getAttribute(0); } public void setName(String name) throws SQLException { _struct.setAttribute(0, name); } public Integer getEmpno() throws SQLException { return (Integer) _struct.getAttribute(1); } public void setEmpno(Integer empno) throws SQLException { _struct.setAttribute(1, empno); } public java.math.BigDecimal getDeptno() throws SQLException { return (java.math.BigDecimal) _struct.getAttribute(2); } public void setDeptno(java.math.BigDecimal deptno) throws SQLException { _struct.setAttribute(2, deptno); } public java.sql.Timestamp getHiredate() throws SQLException { return (java.sql.Timestamp) _struct.getAttribute(3); } public void setHiredate(java.sql.Timestamp hiredate) throws SQLException { _struct.setAttribute(3, hiredate); } public Float getSalary() throws SQLException { return (Float) _struct.getAttribute(4); } public void setSalary(Float salary) throws SQLException { _struct.setAttribute(4, salary); } }
_struct
field in JPublisher-generated code for SQL object types. This is an instance of the internal class MutableStruct
; this instance contains the data in original SQL format. In general, you should not reference this field directly. Instead, use the setting -methods=always
or -methods=named
as necessary to ensure that JPublisher produces .sqlj
files, then use the methods setFrom()
and setValueFrom()
when subclassing. See "The setFrom() and setValueFrom() Methods".
_ctx
field that is a SQLJ connection context instance. See "Oracle8i Compatibility Mode" for more information.
CustomDatum
and CustomDatumFactory
interfaces, instead of ORAData
and ORADataFactory
. In fact, it is still possible to do this through the JPublisher -compatible
option, and this is required if you are using an Oracle8i JDBC driver.
JPublisher also generates an EmployeeRef.java
class. The source code is displayed here:
package corp; import java.sql.SQLException; import java.sql.Connection; import oracle.jdbc.OracleTypes; import oracle.sql.ORAData; import oracle.sql.ORADataFactory; import oracle.sql.Datum; import oracle.sql.REF; import oracle.sql.STRUCT; public class EmployeeRef implements ORAData, ORADataFactory { public static final String _SQL_BASETYPE = "SCOTT.EMPLOYEE"; public static final int _SQL_TYPECODE = OracleTypes.REF; REF _ref; static final EmployeeRef _EmployeeRefFactory = new EmployeeRef(); public static ORADataFactory getORADataFactory() { return _EmployeeRefFactory; } /* constructor */ public EmployeeRef() { } /* ORAData interface */ public Datum toDatum(java.sql.Connection c) throws SQLException { return _ref; } /* ORADataFactory interface */ public ORAData create(Datum d, int sqlType) throws SQLException { if (d == null) return null; EmployeeRef r = new EmployeeRef(); r._ref = (REF) d; return r; } public Employee getValue() throws SQLException { return (Employee) Employee.getORADataFactory().create( _ref.getSTRUCT(), OracleTypes.REF); } public void setValue(Employee c) throws SQLException { _ref.setValue((STRUCT) c.toDatum(_ref.getJavaSqlConnection())); } }
You can find more examples of object mappings in "Example: JPublisher Object Attribute Mapping".
This section discusses issues of backwards 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
or classes12
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
will result in .java
files being generated instead of .sqlj
files if the underlying SQL object type or PL/SQL package does not define any methods.
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" 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 backwards-compatibility options you can set. These are detailed in Table 1-1. See descriptions of these options under "Detailed Descriptions--General JPublisher Options" for more information.
See "Oracle8i Compatibility Mode" for a single setting that results in the same behavior as for Oracle8i JPublisher--backwards-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 backwards-compatibility settings, however, it is recommended that you accept the current default (or other) settings.
The following setting results in what is called Oracle8i compatibility mode:
-compatible=8i
See "Backwards-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:
-codegen=oracle
setting.
runtime12
or runtime12ee
library, or it will execute in the Oracle9i release of the Oracle JVM.
-methods=false
(or none
) setting.
JPublisher has the following functionality in Oracle8i compatibility mode:
CustomDatum
API instead of the ORAData
API (as with -compatible=customdatum
).
-methods=true
setting, JPublisher 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.
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 JPublisher setting: as well as any of the following settings: then there may be significant memory leakage caused by implicit connection context instances that are not closed.
Avoid the |
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. 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 1-2 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.
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. Refer to the Oracle9i JDBC Developer's Guide and Reference for more information on the oracle.sql
package.
A few datatypes are not currently supported by JPublisher, in some cases because they are not directly supported by Oracle SQLJ or JDBC, as noted in the table.
The Object JDBC and BigDecimal mappings, which affect numeric types only, are fully described in "Mappings For Numeric Types (-numbertypes)".
You can use a subset of the PL/SQL datatypes listed in Table 1-2 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
DATE
DECIMAL
, DEC
, NUMBER
, NUMERIC
DOUBLE PRECISION
, FLOAT
INTEGER
, SMALLINT
, INT
REAL
RAW
, LONG RAW
CLOB
BLOB
BFILE
JPublisher cannot generate wrapper methods for PL/SQL methods that use datatypes not directly supported by JDBC. If you must call a PL/SQL method that uses unsupported datatypes (such as BOOLEAN
), you have two choices:
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 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=true
, JPublisher generates .sqlj
files for PL/SQL packages and for object types (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.
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:
Classes produced by JPublisher include a |
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, call the no-argument constructor once. The Oracle9i SQLJ Developer's Guide and Reference discusses reasons for using explicit connection context instances.
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, you must first initialize the Java object.
To initialize your Java object, you can:
setXXX()
methods.
The 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 run JPublisher), one constructor takes a 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:
Classes produced by JPublisher include a |
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. The object has 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; however, you may want to use them if converting from one object reference wrapper type to another.
The RationalO
example, described 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 setting) 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.
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 these 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. 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.
To initialize your object, you can:
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:
#sql
statements.
getXXX()
and setXXX()
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 getXXX()
and setXXX()
accessor methods of a class generated for an object type, the getValue()
and setValue()
methods read and write SQL objects.
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 RationalP
example, described 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 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
, then write a subclass, MyAddress
, which contains the added functionality. You then use JPublisher to map ADDRESS
objects to the MyAddress
class, not to the JAddress
class. JPublisher will also produce a reference class for MyAddress
, not JAddress
.
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
.
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 (as JAddress.java
), and MyAddress
specifies the name of the class that you have written. JPublisher will map the object type ADDRESS
to the MyAddress
class, not to the JAddress
class. Therefore, 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".
The class that you create (for example, MyAddress.java
) must have the following features:
ORAData
interface or the SQLData
interface. The simplest way to do this is to inherit the necessary methods from the superclass.
ORAData
class, you must also implement the ORADataFactory
interface, either in the same class or in a different one. For example, you could have a class Employee
that implements ORAData
and a class EmployeeFactory
that implements ORADataFactory
.
Following is a sample implementation of the ORADataFactory
create()
method:
public ORAData create(Datum d, int sqlType) throws SQLException { return create(new UserClass(),d,sqlType); }
Alternatively, the following code shows a more efficient implementation, where an initialized UserClass
instance is created through the UserClass(boolean)
constructor, which you must define. 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); }
"Example: Generating a SQLData Class" illustrates the preceding features.
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.
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); }
In addition, in .sqlj
files, JPublisher now generates a protected constructor with a boolean argument that specifies whether the object needs to be initialized or not:
protected BaseClass(boolean init) { ... }
You can use this to optimize the UserClass
code as described in "Writing 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.
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 ( ... );
And consider the following JPublisher command line to create corresponding Java classes:
jpub -user=scott/tiger -sql=PERSON:Person,STUDENT:Student -usertypes=oracle
In this example, JPublisher generates a Person
class and a Student
class. The Student
class extends the Person
class, because STUDENT
is a subtype of PERSON
.
So initialization can complete properly, create instances of these classes--at least the leaf classes--before using these mapped types in your code. For example:
new Person(); new Student();
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, may 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 of 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. At best, this process would be prone to errors, and would not even be possible if the programmer did not have access to the source code for the Java classes being extended.
Code generated by JPublisher solves this problem by creating a static initialization block in each subclass in 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 explains why a custom reference class generated for a subtype by JPublisher does not extend the reference classes of the base type, and offers a workaround for how to convert from one reference type to another.
The example here helps explain why it is not desirable for reference types to follow the hierarchy of their related object types.
Consider again 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. This section provides code to show you how to accomplish such a conversion in your program.
To convert from the reference type XxxxRef
to the reference type YyyyRef
, for example, use the following code:
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 comprises 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: Converting Between Reference Types" below provides sample code for such a conversion.
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 "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"); 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); // 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); // 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); 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
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 simple 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. Thus 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 summarizes limitations in the current release of JPublisher.
JPublisher does not currently support the following SQL and PL/SQL argument types:
JPublisher will not generate code for wrapper methods that use one or more of the unsupported datatypes. Instead, JPublisher will display one or more error messages.
For more information about datatype support, see "Datatype Mapping Tables".
INPUT
file error reporting
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".
|
Copyright © 1996-2001, Oracle Corporation. All Rights Reserved. |
|