You are on page 1of 62

REF CURSORS in Java Stored Procedures

Java Enhancements 1-1


Objectives

After this lesson you should be


able to discuss:
• REF CURSORS in Java stored procedures
• Oracle specific JDBC APIs for REF CURSORS
• Obtaining REF CURSORS using JDBC
• Fetching rows from REF CURSORS

Java Enhancements 1-2


Overview

• In Oracle9i
– REF CURSORS can be returned from Java stored
procedures
– REF CURSORS can be created from Java stored
procedures using JDBC
– This is an ease-of-use and flexibility
enhancement

Java Enhancements 1-3


REF CURSORS in Java Stored Procedures
REF CURSORS
– Types are mapped to the java.sql.ResultSet
Java class in call specification definitions.
– Can be returned from Java stored programs as
OUT parameters or as return types of functions
– Can be returned from a Java stored program
through java.sql.ResultSet objects.
– Can be retrieved from a Java stored procedure
– Can be used like a nested cursor or a REF CURSOR
returned from a PL/SQL block or subprogram.
– Are useful in row by row manipulations in a result
set.

Note: A REF CURSOR retrieved from a Java stored procedure can be used in PL/SQL blocks or
subprograms, as well as OCI and JDBC applications.

Java Enhancements 1-4


Example of Java Stored Procedure with a
REF CURSOR as an OUT parameter

CREATE
CREATE OR
OR REPLACE
REPLACE PACKAGE
PACKAGE emp_pack
emp_pack as
as
TYPE
TYPE ref_cur_t
ref_cur_t is
is REF
REF CURSOR;
CURSOR;
END;
END;

CREATE
CREATE procedure
procedure upd_employees
upd_employees
(ref_cur_rc
(ref_cur_rc OUT
OUT emp_pack.ref_cur_t)
emp_pack.ref_cur_t) AS
AS
LANGUAGE
LANGUAGE JAVA
JAVA NAME
NAME
'extjavaproc.proc1
'extjavaproc.proc1
(java.sql.ResultSet[])';
(java.sql.ResultSet[])';

Example of Java Stored Procedure with a REF CURSOR as an OUT parameter


In the above example ref_cur_t is a REF CURSOR type defined in a package called emp_pack.
Note: This example only shows the method to define the call specifications to publish a Java stored
procedure. It does not provide the Java code itself.

Java Enhancements 1-5


Example of Java Stored Function
Returning a REF CURSOR

CREATE
CREATE FUNCTION
FUNCTION del_employees
del_employees
RETURN
RETURN emp_pack.ref_cur_t
emp_pack.ref_cur_t AS
AS
LANGUAGE
LANGUAGE JAVA
JAVA NAME
NAME ''
extjavaproc.func()
extjavaproc.func() RETURNS
RETURNS
java.sql.ResultSet';
java.sql.ResultSet';

Example of Java Stored Function Returning a REF CURSOR


In the above example ref-cur_t is a REF CURSOR type defined in a package called emp_pack.

Java Enhancements 1-6


New Oracle Specific APIs for JDBC

• The oracle.jdbc.OracleConnection and


oracle.jdbc.driver.OracleConnection classes
have new APIs for creating JDBC statements that
generate ResultSets returned as REF CURSORs
• When set to TRUE any new statements created from
this connection is created as a REF CURSOR
• Only result sets obtained from statements that are
created as REF CURSORS can be returned from a Java
stored procedure
• This feature is only supported by the server-side
internal driver

New Oracle Specific APIs for JDBC


To use the setCreateStatementAsRefCursor statement the connection object must be cast
to
oracle.jdbc.driver.OracleConnection
@param value is true if new statements should be created as REF CURSORS otherwise it is false
To retrieve the current setting of the createStatementAsRefCursor flag, the following can
be used:
@return current value of createStatementAsRefCursor flag
To use the getCreateStatementAsRefCursor entry point, the Connection object
must be cast to the type
oracle.jdbc.driver.OracleConnection
Normally, JDBC would retrieve the first batch of rows whenever it executes a query. However, for
statements that are created while the createStatementAsRefCursor flag in the
OracleConnection object is set to true, no fetching is done when the query is executed. This
would prevent rows from being fetched automatically.

Java Enhancements 1-7


Obtaining REF CURSORS using JDBC

A Java stored procedure/function can use


JDBC to retrieve a java.sql.ResultSet
object from the following and return it as a REF
CURSOR. Example
CallableStatement
CallableStatement stmt
stmt ==
conn.prepareCall("begin
conn.prepareCall("begin open
open ?? for
for
select
select ** from
from employees;
employees; end;");
end;");
stmt.registerOutParameter
stmt.registerOutParameter
(1,
(1, OracleTypes.CURSOR);
OracleTypes.CURSOR);
stmt.execute();
stmt.execute();
ResultSet
ResultSet rset
rset ==
((OracleCallableStatement)stmt).getCursor(1);
((OracleCallableStatement)stmt).getCursor(1);

Obtaining REF CURSORs using JDBC


In the above example rset is used as the return value or out value of the Java stored procedure.

Java Enhancements 1-8


Example of Nested Cursor Opened From a
Query With CURSOR Expression

ResultSet
ResultSet rset
rset == null;
null;
ResultSet
ResultSet rr == stmt.executeQuery("SELECT
stmt.executeQuery("SELECT
CURSOR(SELECT
CURSOR(SELECT ** FROM
FROM employees
employees )) FROM
FROM
employees");
employees");
IF
IF (r.next())
(r.next())
{{
System.out.println("func3
System.out.println("func3 got
got ename
ename "" ++
r.getString(1));
r.getString(1));
rset
rset == ((OracleResultSet)r).getCursor(2)
((OracleResultSet)r).getCursor(2) ;;
}}

Example of Nested Cursor Opened From a Query With CURSOR Expression


In the above example rset is used as the return value or out value of the Java stored procedure.

Java Enhancements 1-9


Example of A ResultSet Object From a JDBC
Query Opened as REF CURSOR

((OracleConnection)conn).setCreateStatement
((OracleConnection)conn).setCreateStatement As
As
RefCursor(true);
RefCursor(true);
Statement
Statement stmt
stmt == conn.createStatement();
conn.createStatement();

ResultSet
ResultSet rset
rset == stmt.executeQuery
stmt.executeQuery
("SELECT
("SELECT ** FROM
FROM employees");
employees");
...
...

Example of A ResultSet Object From a JDBC Query Opened as REF CURSOR


In the above example rset is used as the return value or out value of the Java stored procedure

Java Enhancements 1-10


Fetching Rows From REF CURSORS Within
Java Stored Procedures

• This features enhances ease-of-use and flexibility of


Java programs
• Java stored procedures can fetch rows from
ResultSet objects that are returned as REF
CURSORS.
• As JDBC fetches rows in batches, even a one row
request may cause the Oracle JDBC driver to pre-
fetch the next few rows
• Users cannot fetch those rows using the REF
CURSOR obtained from the Java stored procedure.

Fetching Rows From REF CURSORs Within Java Stored Procedures


The prefetch size by can be adjusted using the setRowPrefetch() method in
oracle.jdbc.OracleStatement class or the setDefaultRowPrefetch() method in
oracle.jdbc.OracleConnection class.

Java Enhancements 1-11


Summary

In this lesson, you should have learned about:


• REF CURSORS in Java stored procedures
• Oracle specific JDBC APIs
• Obtaining REF CURSORS using JDBC
• Fetching rows from REF CURSORS

Java Enhancements 1-12


JDBC Inheritance Enhancements

Java Enhancements 2-1


Objectives

After this lesson, you should be


able to describe:
• JDBC and object type inheritance
• Implementing customized classes for subtypes
• JPublisher utility (production only)
• Subtype objects
• Metadata APIs

Java Enhancements 2-2


Overview

• Oracle9i database supports the inheritance feature


for object types
• Subtype instances can be defined, stored and
retrieved within the database, and manipulated in
languages, interfaces, utilities and tools .
• Oracle9i JDBC drivers provide the flexible and
easy to use APIs to leverage the Oracle9i
inheritance feature.
• JDBC applications can choose between the default
mapping, SQLData mapping and OraData mapping
to access the subtype objects in different format.
• This feature improves usage and flexibility of Java
programs

Overview
Oracle9i database supports inheritance for object types, including the ability to define, store and
retrieve subtype instances within the database, and manipulating them in languages, interfaces,
utilities and tools.
Oracle9i JDBC drivers transparently recognize the object subtypes, build the type inheritance
hierarchy, determine the supertype attributes, check the attribute substitution and handle several other
tasks.
REFERENCE
Note: For more information on Object Inheritance please refer to the Objects module under
Development Platform

Java Enhancements 2-3


Substitutability in JDBC

• Substitutability is the ability of a variable of a


given type to hold a value of the type or any of
its subtypes.
• Oracle9i JDBC drivers handle substitutability
transparently.
• A database object is guaranteed to be
returned with its most specific type without
losing information.
• For example, if a subtype object is stored in a
parent type slot, Oracle JDBC drivers
guarantee that a Java object that represents
sub type is returned.

Substitutability in JDBC
Object type inheritance is introduced in Oracle9i object-relational database. It allows a new object
type to be created by extending another object type. The new object type is then a subtype of the
object type it extends from. The subtype automatically inherits all attributes and methods defined in
the superclass. The subtype can add attributes and methods, and overload /override methods inherited
from the supertype.
REFERENCE:
Note : For more information on Object enhancements please refer to the Objects module under
Development Platform

Java Enhancements 2-4


Type Hierarchy

Person_t

Student_t

Parttimestudent_t

Type Hierarchy
An object type can be created as a subtype of an existing object type. This is based on a single
inheritance model which means that the subtype can be derived from only one parent type. A type
inherits all the attributes and methods of its direct supertype. It can add new attributes and methods
and further, override any of the inherited methods.
The above figure illustrates a subtypes Student_t created under Person_t. Further a subtype can itself
be refined by defining another subtype under it thus forming type hierarchies. In the figure above
parttimestudent_t is derived from subtype student_t.
To create a database subtype in JDBC, the most common way is to pass the "create type" statement
to the Statement::execute() API.

Java Enhancements 2-5


Example of Creating Subtypes in JDBC
Connection
Connection conn
conn == ....
....
Statement
Statement stmt
stmt == conn.createStatement();
conn.createStatement();
stmt.execute("CREATE
stmt.execute("CREATE TYPE
TYPE Person_T
Person_T ((
SSN
SSN NUMBER,
NUMBER,
name
name VARCHAR2(30),
VARCHAR2(30),
address
address VARCHAR2(255))");
VARCHAR2(255))");
stmt.execute("CREATE
stmt.execute("CREATE TYPE
TYPE Student_T
Student_T UNDER
UNDER
Person_t
Person_t (deptid
(deptid NUMBER,
NUMBER,
major
major VARCHAR2(100))");
VARCHAR2(100))");
stmt.execute("CREATE
stmt.execute("CREATE TYPE
TYPE PartTimeStudent_t
PartTimeStudent_t
UNDER
UNDER Student_t
Student_t (numHours
(numHours NUMBER)");
NUMBER)");
...
...
stmt.close();
stmt.close();
conn.close();
conn.close();

Java Enhancements 2-6


Implementing Customized Classes for
Subtypes

• Each customized class represents a database


object type.
• A subtype's customized class can either
– mirror the database object type hierarchy
– or ignore the database object hierarchy.
• The methods for creating customized classes
for a subtype are :
– SQLData mapping
– OraData mapping

Note: In prior releases CustomDatum was used for object access. CustomDatum is still available for
backward compatibility

Java Enhancements 2-7


SQLData Mapping

• The SQLData customized classes can mirror the


database object type hierarchy, but does not
always have to do this.
• The SQLData::readSQL and SQLData::writeSQL
of the subclass ensure that each call reads or
writes the super class attributes before reading or
writing the sub class attributes.
• A class can be declared without a superclass. In
this case, the class can contain fields to hold
inherited attributes

SQLData Mapping
For example the PERSON_T's SQLData class and STUDENT_T's SQLData class are as follows :
Person.java:
import java.sql.*;
public class Person implements SQLData
{
private String sql_type;
public int ssn;
public String name;
public String address;
public Person () {}
public String getSQLTypeName() throws SQLException { ret urn
sql_type; }

Java Enhancements 2-8


{ sql_type = typeName;
ssn = stream.readInt();
name = stream.readString();
address = stream.readString();
}
public void readSQL(SQLInput stream, String typeName) throws
SQLException
{
sql_type = typeName;
ssn = stream.readInt();
name = stream.readString();
address = stream.readString();
}
public void writeSQL(SQLOutput stream) throws SQLExcepti on
{
stream.writeInt (ssn);
stream.writeString (name);
stream.writeString (address);
}
}
Student.java:
import java.sql.*;
public class Student extends Person
{ private String sql_type;
public int deptid;
public String major;
public Student () { super(); }
public String getSQLTypeName() throws SQLException { ret urn
sql_type; }
public void readSQL(SQLInput stream, String typeName)
throws
SQLException
{
sql_type = typeName;
super.readSQL (stream, typeName); // read supertype
attributes
deptid = stream.readInt();
major = stream.readString();
}

Java Enhancements 2-9


public void writeSQL(SQLOutput stream) throws SQLException
{super.writeSQL (stream); // write supertype
attributes
stream.writeInt (deptid);
stream.writeString (major);
}
}
The SQLData class hierarchy need not mirror the database object type hierarchy. For example, the
class Student above could have been declared without a superclass. In this case, Student could
contain fields to hold the inherited attributes from PERSON_T as well as the attributes declared by
STUDENT_T as follows :
import java.sql.*;
public class Student implements SQLData
{ private String sql_type;
public int ssn;
public String name;
public String address;
public int deptid;
public String major;
public Student () {}
public String getSQLTypeName() throws SQLException { ret urn
sql_type; }
public void readSQL(SQLInput stream, String typeName)
throws
SQLException
{ sql_type = typeName;
ssn = stream.readInt();
name = stream.readString();
address = stream.readString();
deptid = stream.readInt();
major = stream.readString();
}
public void writeSQL(SQLOutput stream) throws SQLExcepti on
{ stream.writeInt (ssn);
stream.writeString (name);
stream.writeString (address);
stream.writeInt (deptid);
stream.writeString (major);
}
}

Java Enhancements 2-10


OraData Mapping

• OraData mapping is the Oracle recommended


customized mapping.
• It requires the JDBC application to implement
the OraData and OraDataFactory interface
classes.
• The OraDataFactory class contains a factory
method that produces OraData objects.
• Each OraData object represents a database
object.
• The OraData class hierarchy can mirror the
database object type hierarchy.

OraData Mapping
For example, the OraData class of PERSON_T and the OraData class of STUDENT_T can be
implemented as follows :
Person.java:
class Person implements OraData, OraDataFactory
{
static final Person _personFactory = new Person();
public NUMBER ssn;
public CHAR name;
public CHAR address;
public static OraDataFactory getFactory()
{
return _personFactory;
}
public Person () {}
public Person(NUMBER ssn, CHAR name, CHAR address)
{this.ssn = ssn;
this.name = name;
this.address = address;
} …

Java Enhancements 2-11



public Datum toDatum(OracleConnection c) throws SQLException
{
StructDescriptor sd =
StructDescriptor.createDescriptor("SCOTT.PERSON_T", c);
Object [] attributes = { ssn, name, address };
return new STRUCT(sd, c, attributes);
}

public OraData create(Datum d, int sqlType) throws


SQLException
{
if (d == null) return null;
Object [] attributes = ((STRUCT)
d).getOracleAttributes();
return new Person((NUMBER) attributes[0],
(CHAR) attributes[1],
(CHAR) attributes[2]);
}
}
Student.java:
class Student extends Person
{
static final Student _studentFactory = new Student ();
public NUMBER deptid;
public CHAR major;
public static OraDataFactory getFactory()
{
return _studentFactory;
}
public Student () {}
public Student (NUMBER ssn, CHAR name, CHAR address,
NUMBER deptid, CHAR major)
{
super (ssn, name, address);
this.deptid = deptid;
this.major = major;
}

Java Enhancements 2-12



public Datum toDatum(OracleConnection c) throws
SQLException
{
StructDescriptor sd =
StructDescriptor.createDescriptor("SCOTT.STUDENT_T",
c);
Object [] attributes = { ssn, name, address, deptid,
major
};
return new STRUCT(sd, c, attributes);
}

public OraData create(Datum d, int sqlType) throws


SQLException
{
if (d == null) return null;
Object [] attributes = ((STRUCT)
d).getOracleAttributes();
return new Student((NUMBER) attributes[0],
(CHAR) attributes[1],
(CHAR) attributes[2],
(NUMBER) attributes[3],
(CHAR) attributes[4]);
}
}

The OraData class hierarchy need not mirror the database object type hierarchy. The class Student
above could have been declared without a superclass. In this case, Student could contain fields to
hold the inherited attributes from PERSON_T as well as the attributes declared by STUDENT_T.

Java Enhancements 2-13


OraDataFactory Implementation

• OraDataFactory is the factory of OraData


objects.
• The primary purpose of implementing
OraDataFactory is to make sure the
create() method covers all the possible
types

OraDataFactory Implementation
For example the database table is created and populated
as follows -

CREATE TABLE employees (idx NUMBER, person PERSON_T);

INSERT INTO employees VALUES (1, PERSON_T (1000, 'Scott',


'100 Oracle
Parkway'));
INSERT INTO employees VALUES (2, STUDENT_T (1001, 'Peter',
'200 Oracle
Parkway', 101, 'CS'));
INSERT INTO employees VALUES (3, PARTTIMESTUDENT_T (1002,
'David', '300 Oracle
Parkway', 102, 'EE'));

Java Enhancements 2-14


and the JDBC application queries employees.person with OraData mapping as
follows :
ResultSet rset = stmt.executeQuery ("select person from
employees");
while (rset.next())
{
Object s = rset.getOraData (1, PersonFactory.getFactory());
...
}
Then it's important that the "PersonFactory.getFactory()" retur ns
a
factory that can handle PERSON_T, STUDENT_T and PARTTIMESTUDENT _T.
One
possible implementation is --
class PersonFactory implements OraDataFactory
{
static final PersonFactory _factory = new PersonFactory ();

public static OraDataFactory getFactory()


{
return _factory;
}
public OraData create(Datum d, int sqlType) throws
SQLException
{
STRUCT s = (STRUCT) d;
if (s.getSQLTypeName ().equals ("SCOTT.PERSON_T"))
return Person.getFactory ().create (d, sqlType);
else if (s.getSQLTypeName ().equals ("SCOTT.STUDENT_T" ))
return Student.getFactory ().create(d, sqlType);
else if (s.getSQLTypeName ().equals
("SCOTT.PARTTIMESTUDENT_T"))
return ParttimeStudent.getFactory ().create(d,
sqlType);
else
return null;
}
}

Java Enhancements 2-15


JPublisher Utility

• Oracle9i JPublisher generates SQLData and


OraData/OraDataFactory classes that mirror the
inheritance hierarchy.
• It is highly recommended to use Oracle9i
JPublisher to automatically generate the
customized classes.

Note: the SQLData, OraData and OraDataFactory customized classes can be created by JDBC
application developers by means other than JPublisher.

Java Enhancements 2-16


Receiving Subtype Objects

• In a typical JDBC application, subtype objects


are returned from queries as
– PL/SQL OUT parameters
– Object types
– Collection elements
• Subtype objects can be received in three
formats:
– Default mapping
– SQLData mapping
– CustomDatum mapping
– OraData Mapping

Note : CustomDatum is available for backward compatibility.

Java Enhancements 2-17


Receiving Subtype Objects with Default
Mapping

• Subtype objects are returned as instances of


oracle.sql.STRUCT by default.
• This may be an object of the declared type or a
subtype object of the declared type.
• The oracle.sql.STRUCT representing a subtype
object in the database, contains the attributes
inherited from the supertype as well as the subtype
• Oracle JDBC drivers return database objects in their
run time type, and JDBC applications use the
STRUCT::getSQLTypeName() to determine the SQL
type of the STRUCT object.

Receiving Subtype Objects with Default Mapping


For example,
// employees.person column can store PERSON_T, STUDENT_T and
PARTIMESTUDENT_T objects
ResultSet rset = stmt.executeQuery ("select person from
employees");
while (rset.next())
{
oracle.sql.STRUCT s = (oracle.sql.STRUCT)
rset.getObject(1);
if (s != null)
System.out.println (s.getSQLTypeName()); // print o ut
the
type name which
// may be
SCOTT.PERSON_T,
//
SCOTT.STUDENT_T or SCOTT.PARTTIMESTUDENT_T
}

Java Enhancements 2-18


Receiving Subtype Objects with SQLData
Mapping
• As in Oracle8i, in order for database objects to be
received with SQLData mapping the following steps
have to be done:
– The JDBC application implements the SQLData
wrapper classes for those object types that
require customized mapping.
– The JDBC connection typemap can then be
populated with the "SQL type name" to SQLData
wrapper class" entries.
• The JDBC drivers check the typemap and return
database objects as instances of the SQLData class
if there is a matched entry.

Receiving Subtype Objects with SQLData Mapping


The following example demonstrates the whole SQLData customized mapping process.
// The JDBC application developer implements Person.java for
PERSON_T, Student.java for STUDENT_T
// and ParttimeStudent.java for PARTTIMESTUDEN_T.

Connection conn = ...; // make a JDBC connection

// obtains the connection typemap


java.util.Map map = conn.getTypeMap ();

// populate the type map

map.put ("SCOTT.PERSON_T", Class.forName ("Person"));


map.put ("SCOTT.STUDENT_T", Class.forName ("Student"));
map.put ("SCOTT.PARTTIMESTUDENT_T", Class.forName
("ParttimeStudent")); …

Java Enhancements 2-19



// employees.person column can store PERSON_T, STUDENT_T and
PARTTIMESTUDENT_T objects

ResultSet rset = stmt.executeQuery ("select person from


employees");
while (rset.next())
{
// "s" is instance of Person, Student or ParttimeStudent
Object s = rset.getObject(1);

if (s != null)
{
if (s instanceof Person)
System.out.println ("This is a Person");
else if (s instanceof Student)
System.out.println ("This is a Student");
else if (s instanceof ParttimeStudent)
System.out.pritnln ("This is a PartimeStudent");
else
System.out.println ("Unknown type");
}
}

The JDBC drivers check the connection typemap for each call to ResultSet::getObject(),
CallableStatement::getObject(), java.sql.Struct::getAttribute(), java.sql.Array::getArray() and
oracle.sql.REF::getValue().

Java Enhancements 2-20


Receiving Subtype Objects with OraData
Mapping
The Oracle JDBC drivers are informed in one of the
following methods:
• The JDBC application uses getOraDataFactory
(int idx, OraDataFactory f) to access database
objects.
• The JDBC application populates the connection
typemap with "SQL typename" to
"OraDataFactory" entries, and then uses
getObject()to access the object values.

Receiving Subtype Objects with OraData Mapping (continued)


The Oracle JDBC drivers are informed as follows:
•The JDBC application uses getOraDataFactory (int idx, OraDataFactory f) to
access database objects. The second parameter of getOraData specifies the OraDataFactory to be
used. The getOraDataFactory API is available in OracleResultSet and
OracleCallableStatement.
•The JDBC application can populate the connection typemap with "SQL typename" to
"OraDataFactory" entries, and then use getObject() to access the object values.
The first approach avoids the typemap lookup and therefore is more efficient than the second
approach. However, the second approach uses the standard getObject() API.

Please refer to the following page for an example.

Java Enhancements 2-21


The following example demonstrates the first approach:
// employees.person column can store both PERSON_T and STUDENT_ T
objects
ResultSet rset = stmt.executeQuery ("select person from
employees");
while (rset.next())
{ Object s = rset.getOraData (1,
PersonFactory.getFactory());
if (s != null)
{ if (s instanceof Person)
System.out.println ("This is a Person");
else if (s instanceof Student)
System.out.println ("This is a Student");
else if (s instanceof ParttimeStudent)
System.out.pritnln ("This is a PartimeStudent");
else
System.out.println ("Unknown type"); }
}

Java Enhancements 2-22


Creating Subtype Objects
• JDBC applications can create database subtype
objects using JDBC drivers.
• These objects can be sent to the database as bind
variables, or used to exchange information within
the JDBC applications.
• Customized mapping allows the JDBC application to
create either SQLData objects or OraData objects to
represent database subtype objects.
• Default mapping allows the JDBC application to
create oracle.sql.STRUCT objects to represent
database subtype objects.
• Both the inherited fields and the fields defined in the
subtype must not be empty types.

Creating Subtype Objects


For example,
Connection conn = ... // make a JDBC connection
StructDescriptor desc = StructDescriptor.createDescriptor
("SCOTT.PARTTIMESTUDENT", conn);
Object[] attrs = {
new Integer(1234), "Scott", "500 Oracle Parkway", // dat a
fields defined in PERSON_T
new Integer(102), "CS", // dat a
fields defined in STUDENT_T
new Integer(4) // dat a
fields defined in PARTTIMESTUDENT_T
};
STRUCT s = new STRUCT (desc, conn, attrs);

"s" is initialized with data fields inherited from PERSON_T and STUDENT_T,
and data fields defined in PARTTIMESTUDENT_T.

Java Enhancements 2-23


Sending Subtype Objects

• As in prior releases a Java object that represents


a database object is sent to the database as
– a DML bind variable
– PL/SQL IN parameter
– an object type attribute value
• The Java object can be an instance of
oracle.sql.STRUCT, SQLData or OraData
• The Oracle JDBC driver converts Java objects
into the linearized format accepted by the
database SQL engine
• Binding of a subtype object is similar to the
binding of a normal object.

REFERENCE
Note: For more examples and APIs usage, please refer to Working with Oracle Object Types,
Oracle8i JDBC Developer's Guide and Reference, Release 2 (8.1.6).

Java Enhancements 2-24


Accessing Subtype Data Fields
• Customized mapping
– returns the database objects as predefined
SQLData or OraData instances.
– The logic to access subtype data fields is
encapsulated in the customized classes.
• Default mapping
– returns the database objects as instances of
oracle.sql.STRUCT.
– JDBC applications call one of the following
access methods in oracle.sql.STRUCT to
access the data fields:
– object [ ] getAttribute()
– oracle.sql.Datum[ ]getOracleAttribute()

Java Enhancements 2-25


The getAttribute() Method

The getAttribute() method


• Returns both the inherited data fields and the
subtype data fields
• The supertype data fields are listed at the
beginning and subtype's data fields are listed at
the end

Discussing the getAttribute() Method


Individual element types are determined based on the attribute's type by following the JDBC
conversion matrix. Is defined in java.sql.Struct is the JDBC 2.0
Is the standard way to access object data fields.
Returns a java.lang.Object array, and each array element represents a object
attribute. Using getAttribute(), a SQL NUMBER attribute is converted to a
java.math.BigDecimal object.
Here is a example of getAttribute() :
// employees.person column can store PERSON_T, STUDENT_T and
PARTIMESTUDENT_T objects
ResultSet rset = stmt.executeQuery ("select person from
employees");
while (rset.next())
{
oracle.sql.STRUCT s = (oracle.sql.STRUCT)
rset.getObject(1);
if (s != null)
{
String sqlname = s.getSQLTypeName();
Object[] attrs = s.getAttribute();
if (sqlname.equals ("SCOTT.PERSON")
{
System.out.println
Java Enhancements 2-26


("ssn="+((BigDecimal)attrs[0]).intValue());
System.out.println ("name="+((String)attrs[1]));
System.out.println ("address="+((String)attrs[2]));
}
else if (sqlname.equals ("SCOTT.STUDENT"))
{
System.out.println
("ssn="+((BigDecimal)attrs[0]).intValue());
System.out.println ("name="+((String)attrs[1]));
System.out.println ("address="+((String)attrs[2]));
System.out.println
("deptid="+((BigDecimal)attrs[3]).intValue());
System.out.println ("major="+((String)attrs[4]));
}
else if (sqlname.equals ("SCOTT.PARTTIMESTUDENT"))
{
System.out.println
("ssn="+((BigDecimal)attrs[0]).intValue());
System.out.println ("name="+((String)attrs[1]));
System.out.println ("address="+((String)attrs[2]));
System.out.println
("deptid="+((BigDecimal)attrs[3]).intValue());
System.out.println ("major="+((String)attrs[4]));
System.out.println
("numHours="+((BigDecimal)attrs[5]).intValue());
}
else
throw new Exception ("Invalid type name: "+sqlname);
}
}
rset.close ();
stmt.close ();
conn.close ();

With default mapping, the logic to access the subtype data fields is
defined in the JDBC application itself.

Java Enhancements 2-27


Discussing getOracleAttribute() Method
• getOracleAttribute()
– Is an Oracle extension API
– Is more efficient than getAttribute().
– Returns an oracle.sql.Datum array to hold the
data fields,
– In Oracle9i, it returns both the inherited
attributes and the subtype's own attributes
• Each element in the oracle.sql.Datum array
– Represents an attribute.
– Type is determined by following the Oracle
conversion matrix.

Discussing getOracleAttribute() Method


The supertype data fields are listed at the beginning while the subtype's data fields are listed at the
end. For example, a SQL NUMBER attribute is convert to a oracle.sql.NUMBER object.

Java Enhancements 2-28


Metadata APIs

• Oracle9i JDBC drivers' inheritance metadata APIs


are defined in StructDescriptor and
StructMetaData.
• StructDescriptor has the following methods
– String[ ] getSubtypeNames()
– Boolean isFinalType()
– String getSupertypeName()
– int getLocalAttributeCount()
• StructMetaData has the following methods:
– StructDescriptor::getMetaData()
– int getLocalColumnCount()
– Boolean isInherited(int column)

Metadata APIs
StructDescriptor has the following methods
• String[ ] getSubtypeNames() : returns the SQL type names of the direct subtypes.
•Boolean isFinalType() : indicates whether the object type is a final type.
•String getSupertypeName() : returns the SQL type names of the direct supertype.
•int getLocalAttributeCount() : returns the number of attributes defined in the subtype.
An object type is FINAL if no subtypes can be created for this type. The default is FINAL, and a
type declaration must have NOT FINAL keyword to be "subtypable". An object type is NOT
INSTANTIABLE if it's not possible to construct instances of this type.
StructMetaData provides inheritance metadata APIs for subtype attributes.
StructDescriptor::getMetaData() returns a instance of StructMetaData of the type. It contains the
following inheritance metadata APIs
• int getLocalColumnCount() : returns the number of attributes defined in the subtype.
Same as StructDescriptor::getLocalAttributeCount().
• Boolean isInherited(int column) : indicates whether the attribute is inherited. The column
begins with 1.

Java Enhancements 2-29


Summary

In this lesson, you should have learned about:


• JDBC and object type inheritance
• Implementing Customized Classes for
Subtypes
– SQLData Mapping
– OraData Mapping
• Subtype Objects
• Metadata APIs

Java Enhancements 2-30


More JDBC Enhancements

Java Enhancements 3-1


Objectives

After this lesson you should be


able to discuss:
• Multi Level Collections in JDBC
• JDBC Date/Time enhancements
• JDBC UNICODE enhancements

Java Enhancements 3-2


Overview of Multi Level Collections and
JDBC

• The Oracle9i JDBC drivers are enhanced to


access the newly introduced multiple levels of
collections in Oracle9i databases.
• The previously existing Oracle8i JDBC
collection APIs have been extended
semantically, but no new APIs have been
added.

Overview of Multi Level Collections and JDBC


Oracle JDBC drivers contain a set of Java classes that map to database datatypes. These classes inherit a
common superclass, oracle.sql.Datum class, and therefore are normally called the "Datum classes".
oracle.sql.ARRAY class is the Datum class to represent the collection datatypes in the database,
and each oracle.sql.ARRAY instance represents a database collection object. The
oracle.sql.ARRAY class implements the JDBC 2.0 java.sql.Array interface which defines a
set APIs to access the collections elements in different format. Each oracle.sql.ARRAY instance
references a oracle.sql.ArrayDescriptor object that stores the type information of the
collection type. Conceptually the oracle.sql.ARRAY stores the collection value, and
oracle.sql.ArrayDescriptor stores the collection type information.

Java Enhancements 3-3


Creating A Multi Level Collection Types

Creating a multi level collection type requires the


SQL "CREATE TYPE" statement to be passed to
the JDBC Statement::execute() API.

Connection
Connection conn
conn == ...//create
...//create database
database connection
connection
Statement
Statement stmt
stmt == conn.createStatement();
conn.createStatement();
//
// open
open aa database
database cursor
cursor
stmt.execute("CREATE
stmt.execute("CREATE TYPE
TYPE first_level
first_level ASAS TABLE
TABLE OFOF
NUMBER");
NUMBER"); //
// create
create aa nested
nested table
table of
of number
number
stmt.execute("CREATE
stmt.execute("CREATE second_level
second_level AS AS TABLE
TABLE OFOF
first_level");
first_level"); //
// create
create aa two
two levels
levels nested
nested table
table
...
... //
// other
other operations
operations here
here
stmt.close();
stmt.close();
//
// release
release the
the resource
resource
conn.close();
conn.close();
//
// close
close the
the database
database connection
connection

Java Enhancements 3-4


Receiving Multi Level Collections

The JDBC usage of receiving multi level


collection objects is exactly the same as
receiving single level collection objects in. For
example:

//
// tab1.b
tab1.b is
is aa multi
multi level
level collection
collection column
column
ResultSet
ResultSet rset
rset == stmt.executeQuery
stmt.executeQuery ("select
("select bb
from
from tab1");
tab1");
while
while (rset.next())
(rset.next())
oracle.sql.ARRAY
oracle.sql.ARRAY multilevelCollection
multilevelCollection ==
(oracle.sql.ARRAY)
(oracle.sql.ARRAY) rset.getObject(1);
rset.getObject(1);

Note: OracleResultSet::getARRAY(idx) and


java.sql.ResultSet::getArray(idx) can be used as well to retrieve collection values from
ResultSet.

Java Enhancements 3-5


Creating Multi Level Collections

• Similar to single level collections, the JDBC


application can create a oracle.sql.ARRAY
instance to represent a multilevel collection,
and then send the instance to the database .
• The oracle.sql.ARRAY constructor is defined
as follows --
public ARRAY(ArrayDescriptor type,
Connection conn, Object elements)
throws SQLException

Creating Multi Level Collections


Similar to single level collections, the JDBC application can create a oracle.sql.ARRAY instance
to represent a multilevel collection, and then sent the instance to the database . The
oracle.sql.ARRAY constructor is defined as follows --
public ARRAY(ArrayDescriptor type, Connection conn, Object eleme nts)
throws SQLException
The first argument is a oracle.sql.ArrayDescriptor object that describes the multi level
collection type, the second argument is the current database connection, and the third argument is a
java.lang.Object that holds the multi level collection elements. This is the same constructor used to
create single level collections, but since 8.2.0, this constructor is enhanced to create multi level
collections as well. The elements parameter can now be either a one dimension array or a nested Java
array. To create a single level collection, the elements is a one dimensional Java array. To create a multi
level collection, the elements can be either a array of oracle.sql.ARRAY (i.e.
oracle.sql.ARRAY[]) elements or a nested Java array or the combinations.

Java Enhancements 3-6


Creating a Nested Java Array
Connection
Connection conn
conn == ...;
...; //
// make
make aa JDBC
JDBC connection
connection
//
// create
create the
the collection
collection types
types
Statement
Statement stmt
stmt == conn.createStatement
conn.createStatement ();
();
stmt.execute
stmt.execute ("CREATE
("CREATE TYPE
TYPE varray1
varray1 AS
AS VARRAY(10)
VARRAY(10) OF
OF
NUMBER(12, 2)"); // one layer
NUMBER(12, 2)"); // one layer
stmt.execute
stmt.execute ("CREATE
("CREATE TYPE
TYPE varray2
varray2 AS
AS VARRAY(10)
VARRAY(10) OF
OF
varray1");
varray1"); //
// two
two layers
layers
stmt.execute
stmt.execute ("CREATE
("CREATE TYPE
TYPE varray3
varray3 AS
AS VARRAY(10)
VARRAY(10) OF
OF
varray2");
varray2"); //
// three
three layers
layers
stmt.execute
stmt.execute ("CREATE
("CREATE TABLE
TABLE tab2
tab2 (col1
(col1 index,
index, col2
col2
value)");
value)");
stmt.close
stmt.close ();
();
……

Creating a Nested Java Array


In the above example, another implementation is to prepare the "elems" as a Java array of
oracle.sql.ARRAY elements, and each oracle.sql.ARRAY element represents a
"SCOTT.VARRAY2".

Java Enhancements 3-7


Creating a Nested Java Array (continued)

……
//
// obtain
obtain aa type
type descriptor
descriptor of
of "SCOTT.VARRAY3"
"SCOTT.VARRAY3"
ArrayDescriptor
ArrayDescriptor desc
desc ==
ArrayDescriptor.createDescriptor("SCOTT.VARRAY3",
ArrayDescriptor.createDescriptor("SCOTT.VARRAY3", conn);
conn);

//
// prepare
prepare the
the multi
multi level
level collection
collection elements
elements as
as aa
nested
nested Java
Java array
array
int[][][]
int[][][] elems
elems == {{{1},{1,
{{{1},{1, 2}},{{2},{2,
2}},{{2},{2, 3}},{{3},{3,
3}},{{3},{3, 4}}};
4}}};

//
// create
create the
the ARRAY
ARRAY by
by calling
calling the
the constructor
constructor
ARRAY
ARRAY array3
array3 == new
new ARRAY
ARRAY (desc,
(desc, conn,
conn, elems);
elems);

//
// some
some operations
operations
...
...
//
// close
close the
the database
database connection
connection
conn.close();
conn.close();

Java Enhancements 3-8


Sending the Multi Level Collections

• The Oracle9i JDBC usage to send a multi level


collection is exactly the same as sending a
single level collection with the Oracle8i JDBC
drivers.
• An oracle.sql.ARRAY instance is sent to the
databases as
• a DML bind variable
• a PL/SQL IN parameter
• an object type attribute
• a collection element.

Sending the Multi Level Collections


The code example below binds a multi level collection in a SQL DML --
// get a ARRAY object by calling the ARRAY constructor or b y
// receiving it from the database. This object stands for a
// multi level collection
oracle.sql.ARRAY array = ...;
// prepare the insert statement
PreparedStatment pstmt =
conn.prepareStatment ("insert into tabl1 values (1, ?)");
// set up the multi level collection bind variable
pstmt.setObject (1, array, OracleTypes.ARRAY);
// execute the insert statement
pstmt.executeUpdate ();
// release the resource
pstmt.close ();
OraclePreparedStatement::setARRAY(idx, array) or
java.sql.PreparedStatement::setArray(idx, array) can also be use d in
the above example to substitute the
java.sql.PreparedStatement::setObject(idx, array, typecode).

Java Enhancements 3-9


Accessing Multi Level Collection Elements

The Oracle9i JDBC drivers extends the


oracle.sql.ARRAY class APIs to access
collection elements:
• getArray APIs - JDBC standard
• getOracleArray APIs - Oracle extension
• getResultSet APIs - JDBC standard

Accessing Multi Level Collection Elements


getArray() returns a Java array that holds the collection elements. The array element type is
determined by the collection element type and the JDBC's default conversion matrix. For example,
getArray() returns a java.math.BigDecimal array for collection of SQL NUMBER.
getOracleArray() returns a Datum array that holds the collection elements in Datum format. For
multi level collections, both getArray() and getOracleArray() return a Java array of
oracle.sql.ARRAY elements.
Please refer to following page for an example.

Java Enhancements 3-10


The following code example demonstrates the usage of getOracleArray, getArray and getResultSet APIs
--
Connection conn = ...; // make a JDBC connection
Statement stmt = conn.createStatement ();
ResultSet rset = stmt.executeQuery ("select col2 from tab2 where idx=1");

while (rset.next())
{
ARRAY varray3 = (ARRAY) rset.getObject (1);
Object varrayElems = varray3.getArray (1); // access array elements of "varray3"
Datum[] varray3Elems = (Datum[]) varrayElems;

for (int i=0; i<varray3Elems.length; i++)


{
ARRAY varray2 = (ARRAY) varray3Elems[i];
Datum[] varray2Elems = varray2.getOracleArray(); // access array elements of "varray2"

for (int j=0; j<varray2Elems.length; j++)


{
ARRAY varray1 = (ARRAY) varray2Elems[j];
ResultSet varray1Elems = varray1.getResultSet(); // access array elements of "varray1"

while (varray1Elems.next())
System.out.println ("idx="+varray1Elems.getInt(1)+" value="+varray1Elems.getInt(2));
}
}
}
rset.close ();
stmt.close ();
conn.close ();

Java Enhancements 3-11


MetaData APIs
• There is no new metadata APIs required to support
multi level collections,
• The existing APIs are extended as follows:
– oracle.sql.ArrayDescriptor
– ArrayDescriptor::getBaseType()
returns the java.sql.Types.Array
typecode.
– ArrayDescriptor::getBaseName()
returns the nested collection name.
– oracle.sql.ARRAY
– ARRAY::getBaseType() returns the
java.sql.Types.Array typecode.
– ARRAY::getBaseTypeName() returns the
nested collection name.

MetaData APIs
For example:
Connection conn = ...; // make a JDBC connection
Statement stmt = conn.createStatement ();
stmt.execute ("CREATE TYPE varray1 AS VARRAY(10) OF NUMBER(1 2,
2)");
stmt.execute ("CREATE TYPE varray2 AS VARRAY(10) OF varray1" );
stmt.execute ("CREATE TYPE varray3 AS VARRAY(10) OF varray2" );
stmt.close ();
ArrayDescriptor desc =
ArrayDescriptor.createDescriptor("VARRAY3", conn);
// expects the value of java.sql.Types.Array
System.out.println ("Element type of VARRAY3 = "+desc.getBas eType
());
// expects "SCOTT.VARRAY2"
System.out.println ("Element type of VARRAY3 = "+desc.getBas eName
());
conn.close();

Java Enhancements 3-12


Overview of JDBC DateTime Enhancements
This feature may not make Oracle9i

• New Oracle specific JDBC APIs access of


Oracle9i datetime data types.
• The JDBC drivers also allow conversions among
DATE and the new datetime types. For example, a
user can access a TSTZ column as a DATE value.
• Oracle9i JDBC drivers support time zone names
that are supported by the Oracle9i database.
• Time zones are specified by using
Java.util.Calendar.

Overview of JDBC DateTime Enhancements


•New Oracle-specific JDBC APIs access of Oracle9i datetime data types.
•The JDBC drivers also allow conversions among DATE and the new datetime types. For example, a
user can access a TSTZ column as a DATE value.
•Oracle9i JDBC drivers support time zone names that are supported by Oracle 9i. These time zone
names covers most popular time zone names used in the industry as well as most of the time zone names
defined in the JDK from Sun. Time zone are specified by using java.util.Calendar.
Note: that users must not use TimeZone.getTimeZone to create a time zone object because there
are more Oracle time zone names than Sun has defined in its JDK.

Java Enhancements 3-13


JDBC DateTime Datatypes

The Oracle9i JDBC drivers support these new


datetime data types:
• TIMESTAMP (TZ)
• TIMESTAMP WITH TIME ZONE (TSTZ)
• TIMESTAMP WITH LOCAL TIME ZONE(TSLTZ)

Java Enhancements 3-14


Example of Using TimeZone and Calendar
Objects

This is an example of creating the TimeZone


and Calendar objects for US_PACIFIC, which is
a time zone name not defined in the JDK:

TimeZone
TimeZone tz
tz == TimeZone.getDefault();
TimeZone.getDefault();
tz.setID("US_PACIFIC");
tz.setID("US_PACIFIC");
GregorianCalendar
GregorianCalendar gcal
gcal == new
new
GregorianCalendar(tz);
GregorianCalendar(tz);

Java Enhancements 3-15


JDBC Classes for DateTime

These classes are added for the datetime data


types:
• oracle.sql.TIMESTAMP
• oracle.sql.TIMESTAMPTZ
• oracle.sql.TIMESTAMPLTZ

Java Enhancements 3-16


Overview of Unicode Support in JDBC
• Oracle JDBC drivers can measure strings and
characters the same way users do
• Java clients can use UCS2 length semantics
instead of bytes length semantics
• Nchar datatypes such as NCHAR, NVARCHAR etc.
can hold Unicode character data
• Unicode character data is stored as UTF-16 and
UTF-8 character data in the Nchar types

Java Enhancements 3-17


Length Semantics
• Byte Semantics
– Will be supported for both fixed and variable
width character sets
– An n-byte string always needs n bytes of
memory
• Codepoint Semantics
– All character sets have codepoint semantics
equivalent to UCS2 semantics
– String translations from one character set to
another may change its codepoint length
• UCS2 Semantics
– Translations from one character set to another
does not change length in UCS2 codepoints

Length Semantics
Byte Semantics
A string using byte semantics measures its length in bytes. Translating the string from one character set
to another can change its length. In prior Oracle databases support only byte semantics for variable
width character sets. Nchar supports codepoint semantics for fixed width character sets. An n-byte string
will always need nbytes of memory.
Codepoint Semantics
Every character set has its own idea of what a character is. Counting characters in the way the current
character set defines character is known as Codepoint Semantics. In Oracle9i every supported character
set has codepoint semantics equivalent to UCS2 semantics. Translating a string from one character set to
another may change its codepoint length. Each character set has an upper bound on the number of bytes
required to represent one of its codepoints, usually between 1 and 4.
UCS2 Semantics
A string using UCS2 semantics measures its length by effectively being translated first to UTF-16 then
measuring UCS2 codepoints. Translating the string from onecharacter set to another will not change its
length in UCS2 codepoints. Not all strings that can fit in a 10-character display can be represented with
10 UCS2 codepoints, since some characters consist of multiple codepoints. Java and Microsoft use
UCS2 semantics for strings. Thai is a language whose characters require multiple UCS2 codepoints. A
string with n UCS2 codepoints will need between n and 3n bytes of memory in UTF-8, or exactly 2n
bytes of memory in AL16UTF16.

Java Enhancements 3-18


SQLJ Object Types
• The SQLJ Part2 standard API allows access of
object relational features.
• Oracle9i introduces a new SQL type called SQLJ
which is fully implemented in Java
• SQLJ object type is a special SQL type designed
fully implemented in Java.

REFERENCE
Note: For more information on SQLJ Objects please refer to the lesson on SQLJ Objects in the
Objects module

Java Enhancements 3-19


Summary

In this lesson, you should have learned about:

• Multi Level Collections in JDBC


• JDBC Date/Time enhancements
• JDBC UNICODE enhancements

Java Enhancements 3-20

You might also like