Oracle® Database JPublisher User's Guide 11g Release 2 (11.2) Part Number E10587-05 |
|
|
PDF · Mobi · ePub |
This chapter describes the classes, interfaces, and subclasses that JPublisher generates in the following sections:
Stored procedures called through Java Database Connectivity (JDBC) do not pass parameters in the same way as ordinary Java methods. This affects the code that 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.
However, 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, then a copy of the modified parameter is 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 the following possible parameter modes: IN
, OUT
, and 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 the following alternatives for holders that handle PL/SQL OUT
or IN OUT
parameters:
Arrays
Java API for XML-based Remote Procedure Call (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 Also:
"Holder Types for Output Arguments"The following sections describe the three mechanisms:
One 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 running the method, you extract the after value from the array.
A setting of -outarguments=array
, which is the default, instructs JPublisher to use this single-element array mechanism to publish any OUT
or IN OUT
argument.
For example:
Person [] pa = {p}; x.f(pa); p = pa[0];
Assume that x
is an instance of a JPublisher-generated class that has the f()
method, which is a wrapper method for a stored procedure that uses a SQL PERSON
object as an IN OUT
parameter. The PERSON
type maps to the Person
Java class. p
is a Person
instance, and pa[]
is a single-element Person
array.
This mechanism 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, which 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 the this
object, then 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, as follows:
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, which is 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, which is 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
value as a Java string and 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, and 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. The settings are specified in the HOLDER
subtag inside the TARGETTYPE
section for appropriate mapping. If no holder class is specified, then JPublisher chooses one according to defaults.
See Also:
"JPublisher Styles and Style Files"For general information about JAX-RPC and holders, refer to the Java API for XML-based RPC, JAX-RPC 1.0 specification, available at:
http://jcp.org/aboutJava/communityprocess/final/jsr101/index.html
As an 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;
Assume that the webservices10
style file contains an entry for -outarguments=holder
and the following JPublisher command is used to publish the function, g()
:
% jpub -u scott -s toplevel"(g)":ToplevelG -style=webservices10
Enter scott password: password
The published interface is:
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;
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
.
You can use the -outarguments=return
setting as a workaround for supporting method signatures in Web services that do not use JAX-RPC holder types or arrays. If there is no support for JAX-RPC holders, the -outarguments=return
setting allows OUT
or IN OUT
data to be returned in function results.
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 to publish the function, g()
. Although the webservices10
style file specifies -outarguments=holder
, the -outarguments=return
setting comes after the -style
setting and, therefore, takes precedence.
% jpub -u scott -s toplevel"(g)":ToplevelG -style=webservices10 -outarguments=return
Enter scott password: password
The JPublisher output is as follows:
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 ToplevelGUser_g_Out
Java class to support output values of the g()
function through return data.
Note:
The _g_Out
appended to the user class name is according to the JPublisher naming convention used when creating a class to contain the output data in the scenario of passing output parameters in function returns. The _g
reflects the name of the function being processed and the _Out
reflects the OUT
modifier in the corresponding PL/SQL call specification. Therefore, ToplevelGUser_g_Out
is the Java type created for the output data of the g()
method in the ToplevelGUser
class. The user class name is according to the naming convention specified in the webservices10
style file.
Typically, JPublisher output reflects only the names of SQL or PL/SQL entities being processed, but there is no such entity that directly corresponds to ToplevelGUser_g_Out
.
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 ToplevelGUser_g_Out
return type 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. However, overloaded methods with different signatures in PL/SQL may have identical signatures in Java, especially in user subclasses. As an example, consider the following PL/SQL stored procedures:
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);
JPublisher solves such naming conflicts by appending the first letter of the return type and the first letter of each argument type, as applicable, to the method name. If conflicts still remain, then a number is also appended. JPublisher solves the preceding conflict as follows:
void foo(String x); void fooS(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 to 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, 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);
For the -methods=all
setting, which is the default, or the -methods=true
setting, JPublisher typically generates SQLJ classes for PL/SQL packages and object types, using both ORAData
and SQLData
implementations. An 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 run time.
SQLJ classes include wrapper methods that invoke the server methods, or stored procedures, of object types and packages. This section describes how to use these classes.
This section covers the following topics:
Note the following for JPublisher-generated SQLJ classes:
If you are generating Java wrapper classes for a SQL type hierarchy and any of the types contains stored procedures, then by default, JPublisher generates SQLJ classes for all the SQL types and not just the types that have stored procedures.
Note:
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. However, you can avoid this scenario 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.
Associate the wrapper class instance explicitly with a SQLJ connection context instance through the setConnectionContext()
method.
Use the static SQLJ default connection context instance implicitly for the wrapper class instance. This occurs if you do not supply any connection information.
In Oracle8i compatibility mode, instead of the constructor taking a DefaultContext
instance or an instance of a user-specified class, 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 run JPublisher. Another 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 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 the 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. You can also do this by retrieving the SQL object through JDBC calls that you write. If you are in a backward-compatibility mode and use SQLJ source files directly, then you can retrieve a copy of a SQL object through the SQLJ #sql
statements.
Construct the Java object with the no-argument constructor and set its attributes by using the set
XXX
()
methods, or construct the Java object with the constructor that accepts values for all the object attributes. Subsequently, you must use the setConnection()
or setConnectionContext()
method to associate the object with a database connection before calling any of its wrapper methods. If you do not explicitly associate the object with a JDBC or SQLJ connection before calling 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 run 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. If you are in a backward-compatibility mode and use SQLJ source files directly, then you can use the object as a host variable in the SQLJ #sql
statements.
There is a Java attribute for each attribute of the corresponding SQL object type, with the get
XXX
()
and set
XXX
()
accessor methods for each attribute. JPublisher does not generate fields for the attributes. For example, for an attribute called foo
, there is a corresponding Java attribute called foo
and the accessor methods, getFoo()
and setFoo()
.
By default, the class includes wrapper methods that call the associated Oracle object methods, which reside and run on the server. Irrespective of what the server methods are, the wrapper methods are all instance methods. The DefaultContext
in the this
object is used in the wrapper methods.
With Oracle mapping, JPublisher generates the following methods for Oracle JDBC driver to use:
create()
toDatum()
These methods are specified in the ORAData
and ORADataFactory
interfaces and are generally not intended for your direct use. In addition, JPublisher generates the setFrom(
otherObject
)
, setValueFrom(
otherObject
)
, and setContextFrom(
otherObject
)
methods that you can use to copy values or connection information from one object instance to another.
The class that JPublisher uses in creating SQLJ connection context instances depends on how you set the -context
option when you run JPublisher. The following classes can be used:
A setting of -context=DefaultContext
, which is the default setting, results in JPublisher using instances of the standard sqlj.runtime.ref.DefaultContext
class.
A setting of a user-defined class that is in CLASSPATH
and implements the standard sqlj.runtime.ConnectionContext
interface results in JPublisher using instances of that class.
A setting of -context=generated
results in the declaration of the static _Ctx
connection context class 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.
See Also:
"SQLJ Connection Context Classes"Note:
It is no longer a routine, as it was in Oracle8i Database, for JPublisher to declare a_ctx
connection context instance. However, this is used in Oracle8i compatibility mode, with _ctx
being declared as a protected
instance of the static _Ctx
connection context class.
Unless you have legacy code that depends on _ctx
, it is preferable to use the getConnectionContext()
and setConnectionContext()
methods to retrieve and manipulate connection context instances in JPublisher-generated classes.
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. The 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 that are valid only for a particular connection, such as object reference types or BLOBs.
If you have already specified a connection context instance through the constructor, then you need not set it again using the setConnectionContext()
method.
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 the generated SQLJ classes. If necessary, you can use the setting-methods=always
to ensure that SQLJ classes are produced.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 end of processing, you must use the release()
method to free resources in the SQLJ run time. This prevents a possible memory leak.
JPublisher provides the following utility methods in the 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 about 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 setFrom()
method and the combination of the setValueFrom()
and setContextFrom()
methods.
For a -methods=false
setting, 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 run time during execution. Therefore, JPublisher generates non-SQLJ classes, meaning classes that do not call the SQLJ run time application programming interfaces (APIs). All this is true regardless of whether you use an ORAData
implementation or an SQLData
implementation.
Note:
For the -methods=false
setting, JPublisher does not generate code for PL/SQL packages, because they are not useful without wrapper methods.
JPublisher generates the same Java code for reference, VARRAY
, and nested table types regardless of whether the -methods
option is set to false
or true
.
To use an instance of a class that JPublisher generates for an object type with the -methods=false
setting or for a reference, VARRAY
, or nested table type, you must first initialize the object.
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. You can also do this by retrieving the SQL object through JDBC calls that you write. If you are in a backward-compatibility mode and using SQLJ source files directly, then you can retrieve a copy of a SQL object through the SQLJ #sql
statements.
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 that is valid only for a particular connection, such as a reference or a BLOB
.
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, then 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. If you are in a backward-compatibility mode and use SQLJ source files directly, then you can use the object in the SQLJ #sql
statements.
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. You can read and write the state of the object in the following ways:
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.
If you want to update the data in the database, then you must re-insert the Java array into the database.
You cannot modify an object reference, because it is an immutable entity. However, you can read and write the SQL object that it references by using the getValue()
and setValue()
methods.
The getValue()
method returns a copy of the SQL object that is being referenced by the object reference. The setValue()
method updates a SQL object type instance in the database by taking an instance of the Java class that represents the object type as input. 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.
You can use the getORADataFactory()
method in the JDBC code to return an ORADataFactory
object. You can pass this ORADataFactory
object to the getORAData()
method in the ArrayDataResultSet
, OracleCallableStatement
, and OracleResultSet
classes in the oracle.jdbc
package. Oracle JDBC driver uses the ORADataFactory
object to create instances of your JPublisher-generated class.
In addition, classes representing VARRAY
and nested table types have methods that implement features of the oracle.sql.ARRAY
class. These methods are:
getBaseTypeName()
getBaseType()
getDescriptor()
However, JPublisher-generated classes for VARRAY
and nested table types do not extend the oracle.sql.ARRAY
class.
With Oracle mapping, JPublisher generates the following methods for Oracle JDBC driver to use:
create()
toDatum()
These methods are specified in the ORAData
and ORADataFactory
interfaces and are not generally intended for 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.
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 Also:
"JPublisher Styles and Style Files"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 in the future to regenerate the class. If you regenerate a class that you have modified in this way, then your changes, such as 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 customize as desired. Wherever the SQL type is referenced in code, such as when it is used as an argument, the SQL type will be mapped to the subclass rather than to 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, to use a Java type that is suitable.
The following topics are covered in this section:
Suppose you want JPublisher to generate the JAddress
class from the ADDRESS
SQL object type. 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 alters the code it generates in the following ways:
It generates the MyAddressRef
reference class instead of JAddressRef
.
It uses the MyAddress
class, instead of the JAddress
class, to represent attributes with the SQL type ADDRESS
or to represent VARRAY
and nested table elements with the SQL type ADDRESS
.
It uses the MyAddress
factory, instead of the JAddress
factory, when the ORADataFactory
interface is used to construct Java objects with the SQL type 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. However, if the source file for the MyAddress
class already exists, then it is left untouched by JPublisher.
JPublisher has the 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, it is:
-sql=ADDRESS:JAddress:MyAddress
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
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, then this attribute is created as an instance of MyAddress
. Or, if you retrieve an ADDRESS
object directly, then 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 is where you place your custom code. For example, the MyAddress.java
file generated by JPublisher in the preceding example.
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 create()
method, as shown:
public ORAData create(Datum d, int sqlType) throws SQLException { return create(new UserClass(),d,sqlType); }
However, when the class is part of an inheritance hierarchy, the generated method changes to protected ORAData createExact()
, with the same signature and body as create()
.
This section describes the inheritance support for the ORAData
types and explains 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
This section covers the following topics:
Consider the following SQL object types:
CREATE TYPE PERSON AS OBJECT ( ... ) NOT FINAL; CREATE TYPE STUDENT UNDER PERSON ( ... ); CREATE TYPE INSTRUCTOR UNDER PERSON ( ... );
Consider the following JPublisher command to create corresponding Java classes:
% jpub -user=scott -sql=PERSON:Person,STUDENT:Student,INSTRUCTOR:Instructor -usertypes=oracle
Enter scott password: password
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 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.
This section covers the following topics:
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 -sql=PERSON:Person,STUDENT:Student -usertypes=oracle Enter scott password: password % jpub -user=scott -sql=PERSON:Person,INSTRUCTOR:Instructor -usertypes=oracle Enter scott password: password
In this case, you should create instances of the generated classes, at least of the leaf classes, before using these mapped types in your code. For example:
new Instructor(); // required new Student(); // required new Person(); // optional
The Person
class includes the following method:
Person create(oracle.sql.Datum d, int sqlType)
This method converts a Datum
instance to its representation as a custom Java object. It is called by 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 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 custom Java class may or may not be created by JPublisher.
However, the code for the create()
method in the root class of a custom Java class hierarchy need not mention the subclasses. In fact, if it did mention the subclasses, then 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 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 run time, the type is registered in the data structure. Because of this implicit mapping mechanism, no explicit type map, such as those required in the SQLData
scenarios, is required.
Note:
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 processed 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 it is feasible.
This section shows how to convert from one custom reference class to another and also explains why a custom reference class generated by JPublisher for a subtype does not extend the reference classes of the base type.
This section covers the following topics:
Casting a Reference Type Instance into Another Reference Type
Why Reference Type Inheritance Does Not Follow Object Type Inheritance
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 method, cast()
, 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 -sql=PERSON:Person,STUDENT:Student -usertypes=oracle
Enter scott password: password
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
. However, this is not the case, 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, such as 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, then the following problems would occur:
Java would not permit the getValue()
method in StudentRef
to return a Student
object when the method it overrides 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.
You cannot 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.
You cannot convert one reference type to another directly, because reference types do not follow the hierarchy of their related object types. This is a limitation of JPublisher. For background information, this section explains how the generated cast()
methods work to convert from one reference type to another.
Note:
It is not recommended that you follow these manual steps. They are presented here for illustration only. You can use thecast()
method instead.The following example outlines the code that could be used to convert from the XxxxRef
reference type to the YyyyRef
reference type:
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);
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 the SQL definitions and Java code, illustrates the points of the preceding discussion.
SQL Definitions Consider the following SQL definitions:
CREATE TYPE person_t AS OBJECT (ssn NUMBER, name VARCHAR2(30), dob DATE) NOT FINAL; / SHOW ERRORS CREATE TYPE instructor_t UNDER person_t (title VARCHAR2(20)) NOT FINAL; / SHOW ERRORS CREATE TYPE instructorPartTime_t UNDER instructor_t (num_hours NUMBER); / SHOW ERRORS CREATE TYPE student_t UNDER person_t (deptid NUMBER, major VARCHAR2(30)) NOT FINAL; / SHOW ERRORS CREATE TYPE graduate_t UNDER student_t (advisor instructor_t); / SHOW ERRORS CREATE TYPE studentPartTime_t UNDER student_t (num_hours NUMBER); / SHOW ERRORS CREATE TABLE person_tab OF person_t; INSERT INTO person_tab VALUES (1001, 'Larry', TO_DATE('11-SEP-60')); INSERT INTO person_tab VALUES (instructor_t(1101, 'Smith', TO_DATE('09-OCT-1940'), 'Professor')); INSERT INTO person_tab VALUES (instructorPartTime_t(1111, 'Myers', TO_DATE('10-OCT-65'), 'Adjunct Professor', 20)); INSERT INTO person_tab VALUES (student_t(1201, 'John', To_DATE('01-OCT-78'), 11, 'EE')); INSERT INTO person_tab VALUES (graduate_t(1211, 'Lisa', TO_DATE('10-OCT-75'), 12, 'ICS', instructor_t(1101, 'Smith', TO_DATE ('09-OCT-40'), 'Professor'))); INSERT INTO person_tab VALUES (studentPartTime_t(1221, 'Dave', TO_DATE('11-OCT-70'), 13, 'MATH', 20));
JPublisher Mappings Assume the following mappings when you run JPublisher:
Person_t:Person,instructor_t:Instructor,instructorPartTime_t:InstructorPartTime, graduate_t:Graduate,studentPartTime_t:StudentPartTime
SQLJ Class Here is a SQLJ class with an example of reference type conversion:
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 standard SQLData
methods, readSQL()
and writeSQL()
, provide functionality equivalent to the ORAData
/ORADataFactory
methods, create()
and toDatum()
, for reading and writing data.
When JPublisher generates SQLData
classes corresponding to a SQL hierarchy, the Java types follow the same hierarchy as the SQL types. This is similar to the case when JPublisher generates ORAData
classes corresponding to a hierarchy of SQL object types. However, SQLData
implementations do not offer the implicit mapping intelligence that JPublisher automatically generates in ORAData
classes.
In a SQLData
scenario, you must manually provide a type map to ensure correct 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 Also:
Oracle Database JDBC Developer's GuideIn addition, note that there is no support for strongly typed object references in an SQLData
implementation. All object references are weakly typed java.sql.Ref
instances.
This section discusses the effect of using the SQL modifiers FINAL
, NOT FINAL
, or NOT INSTANTIABLE
on JPublisher-generated wrapper classes.
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 ensures that, in all cases, JPublisher users are able to customize generated Java wrapper classes by extending the classes and overriding the generated behavior.
Using the NOT INSTANTIABLE
SQL modifier 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 the instantiable SQL types.