Oracle® Database JPublisher User's Guide 10g Release 1 (10.1) Part Number B10983-01 |
|
|
View PDF |
This chapter discusses details and concepts of the classes, interfaces, and subclasses that JPublisher generates, including how output parameters are treated (PL/SQL IN OUT
or OUT
parameters), how overloaded methods are translated, and how to use the generated classes and interfaces. The following topics are covered:
Stored procedures called through JDBC 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.
By contrast, when you call a stored procedure through JDBC, 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 modified parameter appear in separate objects.
A wrapper method that JPublisher generates contains JDBC statements to call the corresponding 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
, or IN OUT
. Parameters that are IN OUT
or OUT
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.
In Java, there are no OUT
or IN OUT
designations, but values can be returned through holders. In JPublisher, you can specify one of three alternatives for holders that handle PL/SQL OUT
or IN OUT
parameters:
Arrays
JAX-RPC holder types
Function returns
The -outarguments
option enables you to specify which mechanism to use. This feature is particularly useful for Web services. See "Holder Types for Output Arguments (-outarguments)" for syntax information.
The following sections describe the three mechanisms:
The simplest way to solve the problem of returning output values in Java is to pass an OUT
or IN OUT
parameter to the wrapper method in a single-element array. Think of the array as a "container" that holds the parameter. This mechanism works as follows:
You assign the "before" value of the parameter to element [0]
of an array.
You pass the array to your wrapper method.
The wrapper method assigns the "after" value of the parameter to element [0]
of the array.
After executing the method, you extract the "after" value from the array.
A setting of -outarguments=array
(the default) instructs JPublisher to use this single-element array mechanism to publish any OUT
or IN OUT
argument.
Here is an example:
Person [] pa = {p}; x.f(pa); p = pa[0];
Assume that x
is an instance of a JPublisher-generated class that has the method f()
, which is a wrapper method for a stored procedure that uses a SQL PERSON
object as an IN OUT
parameter. The type PERSON
maps to the Java class Person
; p
is a Person
instance; and pa[]
is a single-element Person
array.
The array technique for passing OUT
or IN OUT
parameters requires you to add a few extra lines of code to your program for each parameter.
As another example, consider the PL/SQL function created by the following SQL*Plus command:
SQL> create or replace function g (a0 number, a1 out number, a2 in out number, a3 clob, a4 out clob, a5 in out clob) return clob is begin return null; end;
With -outarguments=array
, this is published as follows:
public oracle.sql.CLOB g ( java.math.BigDecimal a0, java.math.BigDecimal a1[], java.math.BigDecimal a2[], oracle.sql.CLOB a3, oracle.sql.CLOB a4[], oracle.sql.CLOB a5[])
Problems similar to those described earlier arise when the this
object of an instance method is modified.
The this
object is an additional parameter, 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
, then the wrapper method must return the new value of this
. The code generated by JPublisher implements this functionality in different ways, depending on the situation:
For a stored procedure that does not return a result, the new value of this
is returned as the result of the wrapper method.
As an example, assume that 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 defines the following method:
MyJavaType f1(int[] y)
The f1()
method returns the modified this
object value as a MyJavaType
instance.
For a stored function (a stored procedure that returns a result), the wrapper method returns the result of the stored function as its result. The new value of this
is returned in a single-element array, passed as an extra argument (the last argument) to the wrapper method.
Assume that the SQL object type MYTYPE
has the following member function:
MEMBER FUNCTION f2(x IN INTEGER) RETURNS VARCHAR2;
Then the corresponding Java class, MyJavaType
, defines the following method:
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.
Note: For PL/SQL static procedures or functions, JPublisher generates instance methods, not static methods, in the wrapper class. This is the logistic for associating a database connection with each wrapper class instance. The connection instance is used in initializing the wrapper class instance so that you are not subsequently required to explicitly provide a connection or connection context instance when calling wrapper methods. |
The JAX-RPC specification explicitly specifies holder classes in the javax.xml.rpc.holders
package for the Java mapping of simple XML data types and other types. Typically, "Holder
" is appended to the type name for the holder class name. For example, BigDecimalHolder
is the holder class for BigDecimal
.
Given a setting of -outarguments=holder
, JPublisher uses holder instances to publish OUT
and IN OUT
arguments from stored procedures. Holder settings are specified in a JPublisher style file, in the HOLDER
subtag inside the TARGETTYPE
section for the appropriate mapping. If no holder class is specified, then JPublisher chooses one according to defaults. See "JPublisher Styles and Style Files" for details about style files.
For general information about JAX-RPC and holders, see the Java API for XML-based RPC, JAX-RPC 1.0 specification, available at the following location:
http://jcp.org/aboutJava/communityprocess/final/jsr101/index.html
As an example, again consider the PL/SQL function created by the following SQL*Plus command:
SQL> create or replace function g (a0 number, a1 out number, a2 in out number, a3 clob, a4 out clob, a5 in out clob) return clob is begin return null; end;
With -outarguments=holder
, the following is an example of how the function is published. In this case, there is an extra level of abstraction—because oracle.sql.CLOB
is not supported by Web services, it is mapped to String
, the JAX-RPC holder class for which is StringHolder
.Assume the following JPublisher command to publish the function g
. (The webservices10
style file contains an entry for -outarguments=holder
.)
% jpub -u scott/tiger -s toplevel"(g)":ToplevelG -style=webservices10
Here is the published interface:
public java.lang.String g (java.math.BigDecimal a0, javax.xml.rpc.holders.BigDecimalHolder _xa1_out_x, javax.xml.rpc.holders.BigDecimalHolder _xa2_inout_x, java.lang.String a3, javax.xml.rpc.holders.StringHolder _xa4_out_x, javax.xml.rpc.holders.StringHolder _xa5_inout_x) throws java.rmi.RemoteException;
Note: See "JPublisher-Generated Subclasses for Java-to-Java Type Transformations" for further discussion of how JPublisher uses style files and holder classes for Java-to-Java type transformations for PL/SQL output arguments. |
You can use the setting -outarguments=return
as a workaround for supporting method signatures in Web services that do not use JAX-RPC holder types or arrays. In a situation in which there is no support for JAX-RPC holders, the -outarguments=return
setting allows OUT
or IN OUT
data to be returned in function results.
Once again, consider the PL/SQL function created by the following SQL*Plus command:
SQL> create or replace function g (a0 number, a1 out number, a2 in out number, a3 clob, a4 out clob, a5 in out clob) return clob is begin return null; end;
Assume the following JPublisher command (a wraparound command, with output also shown) to publish the function g
. Although the webservices10
style file specifies -outarguments=holder
, the -outarguments=return
setting comes after the -style
setting so takes precedence.
% jpub -u scott/tiger -s toplevel"(g)":ToplevelG -style=webservices10 -outarguments=return SCOTT.top_level_scope ToplevelGUser_g_Out
The JPublisher output acknowledges that it is processing the SCOTT
top level, and also indicates the creation of the Java class ToplevelGUser_g_Out
to support output values of the function g
through return data.
Notes:
|
JPublisher generates the following interface to take input parameters and return output parameters:
public ToplevelGUser_g_Out g (java.math.BigDecimal a0, java.math.BigDecimal xxa2_inoutxx, java.lang.String a3, java.lang.String xxa5_inoutxx) throws java.rmi.RemoteException;
JPublisher generates the TopLevelGUser_g_Out
class as follows:
public class ToplevelGUser_g_Out{ public ToplevelGUser_g_Out() { } public java.math.BigDecimal getA1Out() { return a1_out; } public void setA1Out(java.math.BigDecimal a1_out) { this.a1_out = a1_out; } public java.math.BigDecimal getA2Inout() { return a2_inout; } public void setA2Inout(java.math.BigDecimal a2_inout) { this.a2_inout = a2_inout; } public java.lang.String getA4Out() { return a4_out; }}
The return type ToplevelGUser_g_Out
encapsulates the values of the OUT
and IN OUT
parameters to be passed back to the caller of the function. As in the preceding section, oracle.sql.CLOB
is mapped to String
by the webservices10
style file.
PL/SQL, like Java, lets you create overloaded methods, meaning two or more methods with the same name but different signatures. Overloaded methods with different signatures in PL/SQL may have identical signatures in Java, however, especially in user subclasses. As an example, consider the following PL/SQL stored procedures.
procedure foo(x sys.xmltype); procedure foo(x clob); procedure foo(x nchar);
If you process these with a JPublisher setting of -style=webservices-common
, then they will all have the same signature in Java:
void foo(String x); void foo(String x); void foo(String x);
(See "Style File Specification and Locations" for information about -style
settings and properties files.)
JPublisher solves such naming conflicts by appending the first letter of the return type (as applicable) and the first letter of each argument type (as applicable) to the method name. If conflicts still remain, a number is also appended. JPublisher solves the preceding conflict as follows:
void foo(String x); void fooS(String x); void fooS1(String x);
Note that PL/SQL does not allow overloading for types from the same family. The following, for example, is illegal:
procedure foo(x decimal); procedure foo(x int); procedure foo(x integer);
Now consider the procedures as functions instead, with return types from the same family. The following example is allowed because the argument types are different:
function foo(x float) return decimal; function foo(x varchar2) return int; function foo(x Student_T) return integer;
By default, these are mapped into Java methods as follows:
java.math.BigDecimal foo(Float x); java.math.BigDecimal foo(String x); java.math.BigDecimal foo(StudentT x);
JPublisher allows them all to be named foo()
because now the signatures differ. However, if you want all method names to be unique (as is required for Web services, for example), use the unique
setting of the JPublisher -methods
option. With -methods=unique
, JPublisher publishes the methods as follows, using the naming mechanism described earlier:
java.math.BigDecimal foo(Float x); java.math.BigDecimal fooBS(String x); java.math.BigDecimal fooBS1(StudentT x);
See "Generation of Package Classes and Wrapper Methods (-methods)" for additional information about the -methods
option.
Note: The term SQLJ classes refers to Java classes that call the Oracle SQLJ runtime APIs. By default in Oracle Database 10g,.sqlj source files are invisible to the user, being translated automatically and deleted. (If desired, however, you can use settings of the JPublisher -compatible option to generate visible .sqlj files, skipping the automatic translation.)
Also see "JPublisher Usage of the Oracle SQLJ Implementation" and "Backward Compatibility Option". |
When -methods=all
(the default) or -methods=true
, JPublisher typically generates SQLJ classes for PL/SQL packages and for object types, using both ORAData
and SQLData
implementations. The exception is that a SQLJ class is not generated if an object type does not define any methods, in which case the generated Java class does not require the SQLJ runtime.
SQLJ classes include wrapper methods that invoke the server methods (stored procedures) of object types and packages. This section describes how to use these classes.
Note the following for JPublisher-generated SQLJ classes:
If you are generating Java wrapper classes for a SQL type hierarchy, and any one (or more) of the types contains stored procedures, then by default JPublisher generates SQLJ classes for all the SQL types, not just the types that have stored procedures. (But you have the option of explicitly suppressing the generation of SQLJ classes through the JPublisher -methods=false
setting. This results in all non-SQLJ classes.)
Classes produced by JPublisher include a release()
method. If an instance of a JPublisher-generated wrapper class implicitly constructs a DefaultContext
instance, then you should use the release()
method to release this connection context instance when it is no longer needed. You can avoid this scenario, however, by adhering to at least one of the following suggestions in creating and using the wrapper class instance:
Construct the wrapper class instance with an explicitly provided SQLJ connection context.
Explicitly associate the wrapper class instance with a SQLJ connection context instance through the setConnectionContext()
method.
Implicitly use the static SQLJ default connection context instance for the wrapper class instance. This occurs if you do not supply any connection information.
See "More About Connection Contexts and Instances in SQLJ Classes" for additional information.
In Oracle8i compatibility mode, instead of the constructor taking a DefaultContext
instance or user-specified-class instance, there is a constructor that simply takes a ConnectionContext
instance. This could be an instance of any class that implements the standard sqlj.runtime.ConnectionContext
interface, including the DefaultContext
class.
Take the following steps to use a class that JPublisher generates for a PL/SQL package:
Construct an instance of the class.
Call the wrapper methods of the class.
The constructors for the class associate a database connection with an instance of the class. One constructor takes a SQLJ DefaultContext
instance (or an instance of a class specified through the -context
option when you ran JPublisher); one constructor takes a JDBC Connection
instance; 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. JPublisher provides the constructor that takes a Connection
instance for the convenience of JDBC programmers unfamiliar with SQLJ concepts such as connection contexts and the default context.
The wrapper methods are all instance methods, because the connection context in the this
object is used 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 that you use. If the default context is the only one you use, then you can call the no-argument constructor once.
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.
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:
Assign an already initialized Java object to your Java object.
Retrieve a copy of a SQL object into your Java object. You can do this by using the SQL object as an OUT
argument or as the function return of a JPublisher-generated wrapper method, or by retrieving the SQL object through JDBC calls that you write (or through SQLJ #sql
statements, if you are in a backward compatibility mode and using SQLJ source files directly).
Construct the Java object with the no-argument constructor and set its attributes using the set
XXX
()
methods, or construct the Java object with the constructor that accepts values for all the object attributes. Typically, you will 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 before invoking a method on it, then it becomes implicitly associated with the SQLJ default 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 JDBC programmers unfamiliar with SQLJ concepts such as connection contexts and the default context.
Once you have initialized your Java object, you can do the following:
Call the accessor methods of the object.
Call the wrapper methods of the object.
Pass the object to other wrapper methods.
Use the object as a host variable in JDBC calls (or in SQLJ #sql
statements if you are in a backward compatibility mode and using SQLJ source files directly).
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 (stored procedures) executing in the server. The wrapper methods are all instance methods, whether or not the server methods are. The DefaultContext
in the this
object is used 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:
create()
toDatum()
These methods are not generally intended for your direct use. In addition, JPublisher generates the methods setFrom(
otherObject
)
, setValueFrom(
otherObject
)
, and setContextFrom(
otherObject
)
that you can use to copy the value or connection information from one object instance to another.
Note: "Connection context" is a SQLJ term regarding database connections. For those unfamiliar with SQLJ, the term is briefly defined in "JPublisher Usage of the Oracle SQLJ Implementation". |
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:
A setting of -context=DefaultContext
(the default) results in JPublisher using instances of the standard sqlj.runtime.ref.DefaultContext
class.
A setting of a user-defined class that is in the classpath and that implements the standard sqlj.runtime.ConnectionContext
interface results in JPublisher using instances of that class.
A setting of -context=generated
results in declaration of the static
connection context class _Ctx
in the JPublisher-generated class. JPublisher uses instances of this class for connection context instances. This is appropriate for Oracle8i compatibility mode, but generally not recommended otherwise.
See "SQLJ Connection Context Classes (-context)" for more information about the -context
option.
Note: It is no longer routine (as it was in the Oracle8i version) for JPublisher to declare a connection context instance_ctx . This is used in Oracle8i compatibility mode, however (-compatible=8i or -compatible=both8i ), with _ctx being declared as a protected instance of the static connection context class _Ctx .
Unless you have legacy code that depends on |
Consider the following points in using SQLJ connection context instances or JDBC connection instances in instances of JPublisher-generated wrapper classes:
Wrapper classes generated by JPublisher provide a setConnectionContext()
method that you can use to explicitly specify a SQLJ connection context instance. (This is not necessary if you have already specified a connection context instance through the constructor.)
This method is defined as follows:
void setConnectionContext(conn_ctxt_instance);
This installs the passed connection context instance as the SQLJ connection context in the wrapper class instance. The connection context instance must be an instance of the class specified through the -context
setting for JPublisher connection contexts (typically DefaultContext
).
Note 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 valid only for a particular connection.
Note: Using thesetConnectionContext() method to explicitly set a connection context instance avoids the problem of the connection context not being closed properly. This problem occurs only with implicitly created connection context instances. |
Use either of the following methods of a wrapper class instance, as appropriate, to retrieve a connection or connection context instance.
The getConnectionContext()
method returns an instance of the connection context class specified through the JPublisher -context
setting (typically DefaultContext
).
The returned connection context instance may be either an explicitly set instance or one that was created implicitly by JPublisher.
Note: These methods are available only in generated SQLJ classes. If necessary, you can use the setting-methods=always to ensure that SQLJ classes are produced. See "Generation of Package Classes and Wrapper Methods (-methods)". |
If no connection context instance is explicitly set for a JPublisher-generated SQLJ class, then one will be created implicitly from the JDBC connection instance when the getConnectionContext()
method is called.
In this circumstance, at the conclusion of processing, use the release()
method to free resources in the SQLJ runtime. This prevents a possible memory leak.
JPublisher provides the following utility methods in generated SQLJ classes:
setFrom(
anotherObject
)
This method 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 method 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 method 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:
x.setValueFrom(y); x.setContextFrom(y);
When -methods=false
, or when SQL object types do not define any methods, JPublisher does not generate wrapper methods for object types. In this case, the generated class does not require the SQLJ runtime during execution, so JPublisher generates what is referred to as non-SQLJ classes, meaning classes that do not call the SQLJ runtime APIs. All this is true regardless of whether you use an ORAData
implementation or a SQLData
implementation.
Notes:
|
To use an instance of a class that 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:
Assign an already initialized Java object to your Java object.
Retrieve a copy of a SQL object into your Java object. You can do this by using the SQL object as an OUT
argument or as the function return accessed through a JPublisher-generated wrapper method in some other class, or by retrieving the SQL object through JDBC calls that you write (or through SQLJ #sql
statements, if you are in a backward compatibility mode and using SQLJ source files directly).
Construct the Java object with a no-argument constructor and initialize its data, or construct the Java object based on its attribute values.
Unlike the constructors generated in SQLJ classes, the constructors generated in non-SQLJ classes do not take a connection argument. Instead, when your object is passed to or returned from a JDBC 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, which is not always possible. An object may 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 construct only a null reference. All non-null references come from the database.
Once you have initialized your object, you can do the following:
Pass the object to wrapper methods in other classes.
Use the object as a host variable in JDBC calls (or in SQLJ #sql
statements if you are in a backward compatibility mode and using SQLJ source files directly).
Call the methods that read and write the state of the object. These methods operate on the Java object in your program and do not affect data in the database.
For a class that represents an object type, call the get
XXX
()
and set
XXX
()
accessor methods.
For a class that represents a VARRAY or nested table, call the 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.
You cannot modify an object reference, because it is an immutable entity; however, you can read and write the SQL object it references, using the 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()
result in a database round trip to read or write 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
object to the getORAData()
method in the classes ArrayDataResultSet
, OracleCallableStatement
, and OracleResultSet
in the oracle.jdbc
package. The Oracle JDBC driver uses the ORADataFactory
object to create instances of your JPublisher-generated class.
In addition, classes representing VARRAYs and nested tables have methods that implement features of the oracle.sql.ARRAY
class:
getBaseTypeName()
getBaseType()
getDescriptor()
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:
create()
toDatum()
These methods are not generally intended for your direct use; however, you may want to use them if converting from one object reference Java wrapper type to another.
JPublisher has the ability to generate interfaces as well as classes. This feature is especially useful for Web services, because it eliminates the necessity to manually create Java interfaces that represent the API from which WSDL content is generated.
"Publishing SQL User-Defined Types" and "Publishing PL/SQL Packages" discuss how to use the JPublisher -sql
option to publish user-defined types and PL/SQL packages.
In addition, the -sql
option supports the following syntax:
-sql=sql_package_or_type:JavaClass#JavaInterface
or:
-sql=sql_package_or_type:JavaClass:JavaUserSubclass#JavaSubInterface
Whenever an interface name is specified in conjunction with a class, then the public attributes or wrapper methods (or both) of that class are provided in the interface, and the generated class implements the interface.
You can specify an interface for either the generated class or the user subclass, but not both. The difference between an interface for a generated base class and one for a user subclass involves Java-to-Java type transformations. Method signatures in the subclass may be different from signatures in the base class because of Java-to-Java mappings. See "JPublisher Styles and Style Files" for related information.
In translating a SQL user-defined type, you may want to enhance the functionality of the custom Java class generated by JPublisher.
One way to accomplish this is to manually add methods to the class generated by JPublisher. 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, then your changes (that is, the methods you have added) will be overwritten. Even if you direct JPublisher output to a separate file, you still must merge your changes into the file.
The preferred way to enhance the functionality of a generated class is to extend the class. JPublisher has a mechanism for this, where it will generate the original "base" class along with a stub subclass, which you can then customize as desired. Wherever the SQL type is referenced in code (such as where it is used as an argument), the SQL type will be mapped into the subclass, rather than into the base class.
There is also a scenario for JPublisher-generated subclasses for Java-to-Java type transformations. You may have situations in which JPublisher mappings from SQL types to Java types use Java types unsuitable for your purposes—for example, types unsupported by Web services. JPublisher uses a mechanism of "styles" and style files to allow an additional Java-to-Java transformation step in order to use a Java type that is suitable.
These topics are covered in the following sections:
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 that JAddress
provides.
Under this scenario, you can use JPublisher to generate both a base Java class, JAddress
, and an initial version of a subclass, MyAddress
, to which you can 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:
It generates the reference class MyAddressRef
rather than JAddressRef
.
It uses the 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
.
It uses the MyAddress
factory instead of the JAddress
factory when the ORADataFactory
interface is used to construct Java objects whose SQL type is ADDRESS
.
It generates or regenerates the code for the JAddress
class. In addition, it 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 is left untouched by JPublisher.
Note: For information about changes between Oracle8i and Oracle9i for user-written subclasses of classes generated by JPublisher, see "Changes in JPublisher Behavior Between Oracle8i and Oracle9i". |
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_base_class:map_class
For the MyAddress
/JAddress
example, this is:
-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
is the name of the base class that JPublisher generates, in JAddress.java
, but MyAddress
is the name of the class that actually maps to ADDRESS
. You are ultimately responsible for the code in MyAddress.java
. Update this as necessary to add your custom functionality. If you retrieve an object that has an ADDRESS
attribute, this attribute is created as an instance of MyAddress
. Or, if you retrieve an ADDRESS
object directly, it is retrieved into an instance of MyAddress
.
For convenience, an initial version of the user subclass is automatically generated by JPublisher, unless it already exists. This subclass—generated in MyAddress.java
in the preceding example—is where you place your custom code.
Note the following:
The class has a no-argument constructor. The easiest way to construct a properly initialized object is to invoke the constructor of the superclass, either explicitly or implicitly.
The class implements the ORAData
interface or the SQLData
interface. This happens implicitly by inheriting the necessary methods from the superclass.
When extending an ORAData
class, the subclass also implements the ORADataFactory
interface, with an implementation of the ORADataFactory
create()
method such as the following.
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()
.
JPublisher style files, described in "JPublisher Styles and Style Files", enable you to specify Java-to-Java type mappings. A typical use for such mappings is to ensure that generated classes can be used in Web services. As a particular example, CLOB types such as java.sql.Clob
and oracle.sql.CLOB
cannot be used in Web services. However, the data can be used if converted to a type, such as java.lang.String
, that is supported by Web services.
If you use the JPublisher -style
option to specify a style file, JPublisher generates subclasses that implement the Java-to-Java type mappings specified in the style file. This includes the use of "holder" classes, introduced in "Passing Output Parameters in JAX-RPC Holders", for handling output arguments—data corresponding to PL/SQL OUT
or IN OUT
types.
For example, consider the following PL/SQL package, foo_pack
, consisting of the stored function foo
:
create or replace package foo_pack as function foo(a IN OUT sys.xmltype, b integer) return CLOB; end; /
Assume that you translate the foo_pack
package as follows:
% jpub -u scott/tiger -s foo_pack:FooPack -style=webservices10
This command uses the style file webservices10.properties
for Java-to-Java type mappings. (This style file is supplied by Oracle and is typically appropriate for using Web services in an Oracle Database 10g environment.) The webservices10.properties
file specifies the following (among other things):
The mapping of the Java type oracle.sql.SimpleXMLType
(which is not supported by Web services) to the Java type javax.xml.transform.Source
(which is):
SOURCETYPE oracle.sql.SimpleXMLType TARGETTYPE javax.xml.transform.Source ...
The use of holder classes for PL/SQL OUT
and IN OUT
arguments:
jpub.outarguments=holder
This setting directs JPublisher to use instances of the appropriate holder class, in this case javax.xml.rpc.holders.SourceHolder
, for the PL/SQL output argument of type XMLTYPE
.
The inclusion of webservices-common.properties
:
INCLUDE webservices-common
The webservices-common.properties
file (also supplied by Oracle) specifies the following:
The mapping of SYS.XMLTYPE
to oracle.sql.SimpleXMLType
in the JPublisher default type map:
jpub.adddefaulttypemap=SYS.XMLTYPE:oracle.sql.SimpleXMLType
A code generation naming pattern:
jpub.genpattern=%2Base:%2User#%2
Based on the "-s foo_pack:FooPack
" specification to JPublisher, the genpattern
setting results in generation of the interface FooPack
, the base class FooPackBase
, and the user subclass FooPackUser
, which extends FooPackBase
and implements FooPack
. See "Class and Interface Naming Pattern (-genpattern)" for general information about the -genpattern
option.
The mapping of the Java type oracle.sql.CLOB
(which is not supported by Web services) to the Java type java.lang.String
(which is):
SOURCETYPE oracle.sql.CLOB TARGETTYPE java.lang.String ...
Recall the calling sequence for the foo
stored function:
function foo(a IN OUT sys.xmltype, b integer) return CLOB;
The base class generated by JPublisher, FooPackBase
, has the following corresponding method declaration:
public oracle.sql.CLOB _foo (oracle.sql.SimpleXMLType a[], Integer b);
The base class uses an array to support the output argument. (See "Passing Output Parameters in Arrays".)
The user subclass has the following corresponding method declaration:
public java.lang.String foo (SourceHolder _xa_inout_x, Integer b);
This is because of the specified mapping of oracle.sql.SimpleXMLType
to javax.xml.transform.Source
, the specified use of holder classes for output arguments, and the specified mapping of oracle.sql.CLOB
to java.lang.String
(all as described earlier).
Following is the class SourceHolder
, the holder class for Source
:
// Holder class for javax.xml.transform.Source public class SourceHolder implements javax.xml.rpc.holders.Holder { public javax.xml.transform.Source value; public SourceHolder() { } public SourceHolder(javax.xml.transform.Source value) { this.value = value; } }
Generated user subclasses employ the following general functionality for Java-to-Java type transformations in the wrapper method:
User subclass method { Enter Holder layer (extract IN data from the holder) Enter Java-to-Java mapping layer (from target to source) Call base class method (uses JDBC to invoke wrapped procedure) Exit Java-to-Java mapping layer (from source to target) Exit Holder layer (update the holder) }
For the example, this is as follows in the foo()
method of the class FooPackUser
:
foo (SourceHolder, Integer) { SourceHolder -> Source Source -> SimpleXMLType _foo (SimpleXMLType[], Integer); SimpleXMLType -> Source Source -> SourceHolder }
Note: Do not confuse the term "source" with the class nameSource . In this example, Source is a target type and SimpleXMLType is the corresponding source type. |
The holder layer retrieves and assigns the holder instance.
In the example, the holder layer in foo()
performs the following:
It retrieves a Source
object from the SourceHolder
object that is passed in to the foo()
method (data input).
After processing (which occurs inside the type conversion layer), it assigns the SourceHolder
object from the Source
object that was retrieved and processed (data output).
The type conversion layer first takes the target type (TARGETTYPE
from the style file), next converts it to the source type (SOURCETYPE
from the style file), then calls the corresponding method in the base class (which uses JDBC to invoke the wrapped stored function), and finally converts the source type returned by the base class method back into the target type to return to the holder layer.
In this example, the type conversion layer in foo()
performs the following:
It takes the Source
object from the holder layer (data input).
It converts the Source
object to a SimpleXMLType
object.
It passes the SimpleXMLType
object to the _foo()
method of the base class, which uses JDBC to invoke the foo
stored function.
It takes the SimpleXMLType
object returned by the _foo()
method (output from the foo
stored function).
It converts the SimpleXMLType
object back to a Source
object for the holder layer (data output).
This section primarily discusses inheritance support for ORAData
types, explaining the following related topics:
How JPublisher implements support for inheritance
Why a reference class for a subtype does not extend the reference class for the base type, and how you can convert from one reference type to another reference type (typically a subclass or superclass)
Following this information is 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. The type map of the class hierarchy is appropriately populated.
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 to ensure that the type map at the root of the class hierarchy is properly initialized.
In our previous example, you may have run the following JPublisher commands:
% 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, may actually be a STUDENT
object. In this case, the create()
method must create a Student
instance rather than a Person
instance.
To handle this kind of situation, the create()
method of a custom Java class (whether or not 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 matches the actual type of the SQL object.
The code for the create()
method in the root class of a custom Java class hierarchy need not mention the subclasses, however. In fact, if it did mention the subclasses, you would have to modify the code for the base class whenever you write or create a new subclass. The base class is modified automatically if you use JPublisher to regenerate the entire class hierarchy, but regenerating the hierarchy may not always be possible. For example, you may not have access to the source code for the Java classes being extended.
Instead, code generated by JPublisher permits incremental extension of a class hierarchy by creating a static initialization block in each subclass of the custom Java class hierarchy. This static initialization block initializes a data structure (equivalent to a type map) declared in the root-level Java class, giving the root class the information it needs about the subclass. When an instance of a subclass is created at runtime, the type is registered in the data structure. Because of this implicit mapping mechanism, no explicit type map, such as those required in SQLData
scenarios, is required.
Important: This implementation makes it possible to extend existing classes without having to modify them, but it also carries a penalty. The static initialization blocks of the subclasses must be executed before the class hierarchy can be used to read objects from the database. This occurs if you instantiate an object of each subclass by callingnew() . It is sufficient to instantiate just the leaf classes, because the constructor for a subclass invokes the constructor for its immediate superclass.
As an alternative, you can generate (or regenerate) the entire class hierarchy, if feasible. |
This section shows how to convert from one custom reference class to another, and also 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", PersonRef
, StudentRef
, and InstructorRef
are obtained for strongly typed references, in addition to the underlying object type wrapper classes.
There may be situations in which 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 generated in strongly typed reference classes:
StudentRef s_ref = ...; PersonRef p_ref = PersonRef.cast(s_ref);
Conversely, you may have a PersonRef
instance and know that you can narrow it to an InstructorRef
instance:
PersonRef pr = ...; InstructorRef ir = InstructorRef.cast(pr);
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 ( ... );
And consider the following JPublisher command:
% jpub -user=scott/tiger -sql=PERSON:Person,STUDENT:Student -usertypes=oracle
In addition to generating the Person
and Student
Java types, JPublisher generates PersonRef
and StudentRef
types.
Because the Student
class extends the Person
class, you may 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
object can perform something that a StudentRef
object cannot: modifying a Person
object in the database.
The most important methods of the PersonRef
class are the following:
Person getValue()
void setValue(Person c)
The corresponding methods of the StudentRef
class are as follows:
Student getValue()
void setValue(Student c)
If the StudentRef
class extended the PersonRef
class, two problems would occur:
Java would not permit the 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.
The 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. You can use the cast()
method instead.
The following example outlines code that 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.
Convert xref
from its strong XxxxRef
type to the weak oracle.sql.REF
type:
oracle.sql.REF ref = (oracle.sql.REF) xref.toDatum(conn);
Convert from the oracle.sql.REF
type to the target YyyyRef
type:
YyyyRef yref = (YyyyRef) YyyyRef.getORADataFactory(). create(ref, Oracle.jdbc.OracleTypes.REF);
"Example: Manually Converting Between Reference Types", which immediately follows, provides sample code for such a conversion.
Note: This conversion does not include 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 SQLJ class with an example of reference type conversion as discussed earlier, in "Manually Converting Between Reference Types":
import java.sql.*; import oracle.jdbc.*; import oracle.sql.*; public class Inheritance { public static void main(String[] args) throws SQLException { System.out.println("Connecting."); java.sql.DriverManager.registerDriver(new oracle.jdbc.OracleDriver()); oracle.jdbc.OracleConnection conn = (oracle.jdbc.OracleConnection) java.sql.DriverManager.getConnection ("jdbc:oracle:oci8:@", "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; OraclePreparedStatement stmt = (OraclePreparedStatement)conn.prepareStatement ("select ref(p) FROM PERSON_TAB p WHERE p.NAME=:1"); OracleResultSet rs; System.out.println("Selecting a person."); stmt.setString(1, "Larry"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); p_ref = (PersonRef) rs.getORAData(1, PersonRef.getORADataFactory()); rs.close(); System.out.println("Selecting an instructor."); stmt.setString(1, "Smith"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); i_ref = (InstructorRef) rs.getORAData(1, InstructorRef.getORADataFactory()); rs.close(); System.out.println("Selecting a part time instructor."); stmt.setString(1, "Myers"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); ipt_ref = (InstructorPartTimeRef) rs.getORAData (1, InstructorPartTimeRef.getORADataFactory()); rs.close(); System.out.println("Selecting a student."); stmt.setString(1, "John"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); s_ref = (StudentTRef) rs.getORAData(1, StudentTRef.getORADataFactory()); rs.close(); System.out.println("Selecting a part time student."); stmt.setString(1, "Dave"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); spt_ref = (StudentPartTimeRef) rs.getORAData (1, StudentPartTimeRef.getORADataFactory()); rs.close(); System.out.println("Selecting a graduate student."); stmt.setString(1, "Lisa"); rs = (OracleResultSet) stmt.executeQuery(); rs.next(); g_ref = (GraduateRef) rs.getORAData(1, GraduateRef.getORADataFactory()); rs.close(); stmt.close(); // 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 produce an error, demonstrating that refs // are type safe. 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); conn.close(); } }
If you use the JPublisher -usertypes=jdbc
setting instead of -usertypes=oracle
, then the custom Java class generated by JPublisher implements 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 follow the same hierarchy as the SQL types.
SQLData
implementations do not, however, offer the implicit mapping intelligence that JPublisher automatically generates in 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 Oracle Database JDBC Developer's Guide and Reference for information.
In addition, note 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
, 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 generated Java wrapper classes by extending the classes 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, to call such a method, you must cast to some wrapper class that corresponds to an instantiable SQL subtype.
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 be created only through subclasses that correspond to instantiable SQL types.