Skip Headers
Oracle® Database JPublisher User's Guide
11g Release 2 (11.2)

Part Number E10587-03
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

5 Generated Classes and Interfaces

This chapter describes the classes, interfaces, and subclasses that JPublisher generates in the following sections:

Treatment of Output Parameters

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:

The -outarguments option enables you to specify which mechanism to use. This feature is particularly useful for Web services.

The following sections describe the three mechanisms:

Passing Output Parameters in Arrays

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:

  1. You assign the before value of the parameter to element [0] of an array.

  2. You pass the array to your wrapper method.

  3. The wrapper method assigns the after value of the parameter to element [0] of the array.

  4. 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.

Passing Output Parameters in JAX-RPC Holders

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.

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.

Passing Output Parameters in Function Returns

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.

Translation of Overloaded Methods

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);

Generation of SQLJ Classes

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:

Important Notes About Generation of SQLJ Classes

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.

Use of SQLJ Classes for PL/SQL Packages

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

  1. Construct an instance of the class.

  2. Call the wrapper methods of the class.

The constructors for the class associate a database connection with an instance of the class. One constructor takes a SQLJ DefaultContext instance or an instance of a class specified through the -context option when you 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.

Use of SQLJ Classes for Object Types

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

  • 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 setXXX() 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 getXXX() and setXXX() 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.

Connection Contexts and Instances in SQLJ Classes

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.

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 the setConnectionContext() 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:

    • Connection getConnection()

    • ConnCtxtType getConnectionContext()

    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.

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

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.

Generation of Non-SQLJ Classes

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:

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 setXXX() methods, if your class represents an object type, or the setArray() or setElement() method, if your class represents a VARRAY or nested table type. If your class represents a reference type, 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:

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:

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:

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.

Generation of Java Interfaces

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.

JPublisher Subclasses

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:

Extending JPublisher-Generated Classes

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.

Syntax for Mapping to Alternative Classes

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.

Format of the Class that Extends the Generated Class

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().

Support for Inheritance

This section describes the inheritance support for the ORAData types and explains the following related topics:

This section covers the following topics:

ORAData Object Types and Inheritance

Consider the following SQL object types:

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

CREATE TYPE STUDENT UNDER PERSON (
...
);

CREATE TYPE INSTRUCTOR UNDER PERSON (
...
);

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:

Precautions when Combining Partially Generated Type Hierarchies

If you run JPublisher several times on a SQL type hierarchy, each time generating only part of the corresponding Java wrapper classes, then you must take precautions in the user application 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

Mapping of Type Hierarchies in JPublisher-Generated Code

The Person class includes the following method:

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

This method 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 calling new(). 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.

ORAData Reference Types and Inheritance

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

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);

Why Reference Type Inheritance Does Not Follow Object Type Inheritance

The example here helps explain why it is not desirable for reference types to follow the hierarchy of their related object types. Consider again a subset of the example given in the previous section (repeated here for convenience):

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

CREATE TYPE STUDENT UNDER PERSON (
...
);

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.

Manually Converting Between Reference Types

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 the cast() 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.

  1. Convert xref from its strong XxxxRef type to the weak oracle.sql.REF type:

    oracle.sql.REF ref  = (oracle.sql.REF) xref.toDatum(conn);
    
  2. Convert from the oracle.sql.REF type to the target YyyyRef type:

    YyyyRef yref = (YyyyRef) YyyyRef.getORADataFactory().
                              create(ref,oracle.jdbc.OracleTypes.REF);
    

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.

Example: Manually Converting Between Reference Types

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(); 
  } 
} 

SQLData Object Types and Inheritance

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.

In 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.

Effects of Using SQL FINAL, NOT FINAL, NOT INSTANTIABLE

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.