Oracle9i SQLJ Developer's Guide and Reference Release 2 (9.2) Part Number A96655-01 |
|
This chapter documents datatypes supported by Oracle SQLJ, listing supported SQL types and the Java types that correspond to them, including information about backward compatibility to Oracle8 and Oracle7. This is followed by details about support for streams and Oracle type extensions. SQLJ "support" of Java types refers to types that can be used in host expressions.
For information about Oracle SQLJ support for user-defined types--SQL objects, object references, and collections--see Chapter 6, "Objects, Collections, and OPAQUE Types".
This chapter covers the following topics:
This section summarizes the types supported by Oracle SQLJ, including information about new support for JDBC 2.0 types, and backward compatibility for the 8.0.x and 7.3.x Oracle JDBC drivers.
For a complete list of legal Java mappings for each Oracle SQL type, see the reference information in the Oracle9i JDBC Developer's Guide and Reference.
Table 5-1 lists the Java types that you can use in host expressions when employing the Oracle JDBC drivers. This table also documents the correlation between Java types, SQL types whose typecodes are defined in the class oracle.jdbc.OracleTypes
, and datatypes in Oracle9i.
SQL data output to a Java variable is converted to the corresponding Java type. A Java variable input to SQL is converted to the corresponding Oracle datatype.
You can refer to the Oracle9i JDBC Developer's Guide and Reference for more information about Oracle type support.
The following points relate to type support for standard features:
char
and Character
types. Instead, use the Java String
type to represent character data.java.sql.Date
type with java.util.Date
, which is not directly supported. The java.sql.Date
class is a wrapper for java.util.Date
that allows JDBC to identify the data as a SQL DATE
and adds formatting and parsing operations to support JDBC escape syntax for date values.NUMBER
. Although you can specify additional precision when you declare a NUMBER
during table creation (by declaring the total number of places and the number of places to the right of the decimal point), this precision may be lost when retrieving the data through the Oracle JDBC drivers, depending on the Java type that you use to receive the data. An oracle.sql.NUMBER
instance would preserve full information.Integer
and Float
) are useful in cases where null values may be returned by the SQL statement. Primitive types (such as int
and float
) cannot contain null values. See "Null-Handling" for more information.OUT
or INOUT
parameters. This applies to the Struct
, Ref
, and Array
standard JDBC 2.0 types, as well as to corresponding Oracle extended types.oracle.jdbc
package, is added in Oracle 9i JDBC in place of classes of the oracle.jdbc.driver
package. These new interfaces provide a more generic way for users to access Oracle-specific features using Oracle JDBC drivers. Specifically, when creating programs for the middle tier, you should use the new API. The Oracle 8i API will continue to be supported for backward compatibility, so no change is required for existing JDBC code to upgrade from Oracle 8i to Oracle 9i. (SQLJ programmers, however, will not typically use these interfaces directly. They are used transparently by the SQLJ runtime or in Oracle-specific generated code.)
For more information, see "Custom Java Class Interface Specifications".
The following points relate to Oracle extensions, most of which are covered in "Support for JDBC 2.0 LOB Types and Oracle Type Extensions" and in Chapter 6, "Objects, Collections, and OPAQUE Types":
oracle.sql.ORAData
to set the public static _SQL_TYPECODE
parameter according to values defined in the OracleTypes
class. In some cases, an additional parameter must be set as well, such as _SQL_NAME
for objects and _SQL_BASETYPE
for object references. This occurs automatically if you use the Oracle JPublisher utility to generate the class.
oracle.sql
classes are wrappers for SQL data for each of the Oracle datatypes. The ARRAY
, STRUCT
, REF
, BLOB
, and CLOB
classes correspond to standard JDBC 2.0 interfaces. For background information about these classes and Oracle extensions, see the Oracle9i JDBC Developer's Guide and Reference.ORAData
or SQLData
), references (implementing ORAData
only), collections (implementing ORAData
only), OPAQUE types (implementing ORAData
only), or other SQL types (for customized handling, implementing ORAData
only). See "Custom Java Classes".
You can use the Oracle JPublisher utility to automatically generate custom Java classes. See "JPublisher and the Creation of Custom Java Classes".
CHAR
column value for a WHERE
clause. Otherwise the string would have to be padded to match the number of characters in the database column. This is available as a SQLJ translator option for Oracle-specific code generation, or as an Oracle customizer option for ISO standard code generation. See "CHAR Comparisons with Blank Padding (-fixedchar)" and "Oracle Customizer CHAR Comparisons with Blank Padding (fixedchar)".OUT
or INOUT
parameters. This applies to the STRUCT
, REF
, and ARRAY
Oracle extended types and corresponding standard JDBC 2.0 types, as well as to Oracle OPAQUE types.As indicated in Table 5-1 above, Oracle JDBC and SQLJ support JDBC 2.0 types in the standard java.sql
package.
This section lists JDBC 2.0 supported types and related Oracle extensions.
Table 5-2 lists the JDBC 2.0 types supported by Oracle SQLJ. You can use them wherever you can use the corresponding Oracle extensions, summarized in the table.
The Oracle extensions have been available in prior releases and are still available as well. These oracle.sql.*
classes provide functionality to wrap raw SQL data, and are described in the Oracle9i JDBC Developer's Guide and Reference.
ORAData
functionality is an Oracle-specific alternative to standard SQLData
functionality for Java support of user-defined types. For information, see "Custom Java Classes".
For information about support for other types in Table 5-2, see "Support for BLOB, CLOB, and BFILE" and "Support for Weakly Typed Objects, References, and Collections".
The following JDBC 2.0 types are currently not supported in Oracle JDBC or SQLJ:
JAVA_OBJECT
--Represents an instance of a Java type in a SQL column.DISTINCT
--A distinct SQL type represented in or retrievable from a basic SQL type (for example, SHOESIZE
--> NUMBER
).Oracle SQLJ and JDBC do not support calling arguments or return values of the PL/SQL BOOLEAN
type or RECORD types. Also, when using the Thin driver, they do not support calling arguments or return values of PL/SQL TABLE types (known as indexed-by tables). TABLE types are supported for the OCI driver, however.
The Oracle JDBC OCI driver has supported scalar PL/SQL indexed-by tables since Oracle8i release 8.1.7. For details about the JDBC support, see the Oracle9i JDBC Developer's Guide and Reference.
Oracle9i SQLJ simplifies the process of writing and retrieving data in scalar indexed-by tables. The following array types are supported:
int[]
, long[]
, float[]
, double[]
, short[]
, java.math.BigDecimal[]
, oracle.sql.NUMBER[]
java.lang.String[]
, oracle.sql.CHAR[]
Here is an example of writing indexed-by table data to the database:
int[] vals = {1,2,3}; #sql { call procin(:vals) };
Here is an example of retrieving indexed-by table data from the database:
oracle.sql.CHAR[] outvals; #sql { call procout(:OUT outvals/*[111](22)*/) };
You must specify the maximum length of the output array being retrieved, using [xxx]
syntax inside /*...*/
syntax as shown. Also, for character-like binds, you can optionally include (xx)
syntax, as shown, to specify the maximum length (in bytes) of an array element.
Note: The |
As a workaround for an unsupported type, you can create wrapper procedures that process the data using supported types. For example, to wrap a stored procedure that uses PL/SQL boolean values, you can create a stored procedure that takes a character or number from JDBC and passes it to the original procedure as BOOLEAN
, or, for an output parameter, accepts a BOOLEAN
argument from the original procedure and passes it as a CHAR
or NUMBER
to JDBC. Similarly, to wrap a stored procedure that uses PL/SQL records, you can create a stored procedure that handles a record in its individual components (such as CHAR
and NUMBER
). To wrap a stored procedure that uses PL/SQL TABLE types, you can break the data into components or perhaps use Oracle collection types.
Here is an example of a PL/SQL wrapper procedure MY_PROC
for a stored procedure PROC
that takes a BOOLEAN
as input:
PROCEDURE MY_PROC (n NUMBER) IS BEGIN IF n=0 THEN proc(false); ELSE proc(true); END IF; END; PROCEDURE PROC (b BOOLEAN) IS BEGIN ... END;
Note: When using these non-supported PL/SQL types in method signatures in PL/SQL packages or SQL objects, consider using the Oracle9i JPublisher utility. This facilitates the creation of Java types to call such methods. See "JPublisher and the Creation of Custom Java Classes" for an overview of JPublisher, and the Oracle9i JPublisher User's Guide for more information. |
This section summarizes backward compatibility issues when using Oracle SQLJ with previous Oracle JDBC releases.
The following Oracle9i features are not supported, or are supported differently, in the Oracle8i JDBC drivers:
oracle.sql.ORAData
and ORADataFactory
interfaces for Java mapping of user-defined SQL types
Use the Oracle8i oracle.sql.CustomDatum
and CustomDatumFactory
interfaces instead. See "ORAData Versus CustomDatum Interfaces".
NCHAR
, NCLOB
, NString
, and NcharCharacterStream
(or NcharAsciiStream
and NcharUnicodeStream
in previous releases)Some of the Oracle type extensions supported by the Oracle9i JDBC drivers are either not supported or supported differently by the Oracle 8.0.x and 7.3.x JDBC drivers. Following are the key points:
oracle.sql
package, meaning there are no wrapper types such as oracle.sql.NUMBER
and oracle.sql.CHAR
that you can use to wrap raw SQL data.ROWID
datatype with the OracleRowid
class in the oracle.jdbc
package.BLOB
, CLOB
, and BFILE
datatypes with the OracleBlob
, OracleClob
, and OracleBfile
classes in the oracle.jdbc
package. These classes do not include LOB and BFILE manipulation methods such as those discussed in "Support for BLOB, CLOB, and BFILE". You must, instead, use the PL/SQL DBMS_LOB
package, which is discussed in the same section.BLOB
, CLOB
, and BFILE
.Table 5-3 summarizes these differences.
Standard SQLJ provides two specialized classes, listed below, for convenient processing of long data in streams. These stream types can be used for iterator columns to retrieve data from the database, or for input host variables to send data to the database. As with Java streams in general, these classes allow the convenience of processing and transferring large data items in manageable chunks.
This section discusses general use of these classes, Oracle SQLJ extended functionality, and stream class methods.
With respect to Oracle9i, Table 5-1 lists the datatypes you would typically process using these stream classes. To summarize:
BinaryStream
is typically used for datatype LONG RAW
(Types.LONGVARBINARY
), but might also be used for datatype RAW
(Types.BINARY
or Types.VARBINARY
).CharacterStream
is typically used for datatype LONG
(java.sql.Types.LONGVARCHAR
), but might also be used for datatype VARCHAR2
(Types.VARCHAR
).Of course, any use of streams is at your discretion. As Table 5-1 documents, LONG
and VARCHAR2
data can also be manifested in Java strings, while RAW
and LONGRAW
data can also be manifested in Java byte arrays. Furthermore, if your database supports large object types such as BLOB
(binary large object) and CLOB
(character large object), you may find these to be preferable to using types such as LONG
and LONG RAW
(although streams may still be used in extracting data from large objects). Oracle SQLJ and JDBC support large object types--see "Support for BLOB, CLOB, and BFILE".
Both SQLJ stream classes are subclasses of standard Java classes, java.io.InputStream
for BinaryStream
and java.io.Reader
for CharacterStream
, and act as wrappers to provide the functionality required by SQLJ. This functionality is to communicate to SQLJ the type and length of the underlying data so that it can be processed and formatted properly.
You can use the SQLJ stream types for host variables to either send or retrieve data.
The following abbreviated code illustrates key aspects of the BinaryStream
class--what it extends, constructor signatures, and key method signatures:
public class sqlj.runtime.BinaryStream extends sqlj.runtime.StreamWrapper { public sqlj.runtime.BinaryStream(java.io.InputStream); public sqlj.runtime.BinaryStream(java.io.InputStream,int); public java.io.InputStream getInputStream(); public int getLength(); public void setLength(int); }
And the following abbreviated code illustrates key aspects of the CharacterStream
class:
public class sqlj.runtime.CharacterStream extends java.io.FilterReader { public sqlj.runtime.CharacterStream(java.io.Reader); public sqlj.runtime.CharacterStream(java.io.Reader,int); public int getLength(); public java.io.Reader getReader(); public void setLength(int); }
Constructor int
parameters are for data length, in bytes or characters as applicable.
Standard SQLJ allows you to use streams as host variables to update the database.
A key point in sending a SQLJ stream to the database is that you must somehow determine the length of the data and specify that length to the constructor of the SQLJ stream. This will be further discussed below.
You can use a SQLJ stream to send data to the database as follows:
BinaryStream
this would be an input stream--an instance of java.io.InputStream
or some subclass. For CharacterStream
this would be a reader object--an instance of java.io.Reader
or some subclass.int
) to the constructor.The following subsections now go into more detail regarding two typical examples of sending a SQLJ stream to the database:
LONG
or LONG RAW
column
This can be either a binary file to update a LONG RAW
column, or a character file to update a LONG
column.
LONG RAW
columnThis section shows how to create a CharacterStream
object or a BinaryStream
object from a File
object and use it to update the database. The code example at the end uses a CharacterStream
for a LONG
column.
In updating a database column (presumably a LONG
or LONG RAW
column) from a file, a step is needed to determine the length. You can do this by creating a java.io.File
object before you create your input stream.
Here are the steps in updating the database from a file:
java.io.File
object from your file. You can specify the file path name to the File
class constructor.length()
method of the File
object to determine the length of the data. This method returns a long
value, which you must cast to an int
for input to the SQLJ stream class constructor.
java.io.FileReader
object from your File
object. You can pass the File
object to the FileReader
constructor.
For binary data, create a java.io.FileInputStream
object from your File
object. You can pass the File
object to the FileInputStream
constructor.
CharacterStream
object for a character file or a BinaryStream
object for a binary file. Pass the FileReader
or FileInputStream
object, as applicable, and the data length (as an int
) to the SQLJ stream class constructor.The following is an example of writing LONG
data to the database from a file. Presume you have an HTML file in /private/mydir/myfile.html
and you want to insert the file contents into a LONG
column called chardata
in a database table named filetable
.
Imports:
import java.io.*; import sqlj.runtime.*;
Executable code:
File myfile = new File ("/private/mydir/myfile.html"); int length = (int)myfile.length(); // Must cast long output to int. FileReader filereader = new FileReader(myfile); CharacterStream charstream = new CharacterStream(filereader, length); #sql { INSERT INTO filetable (chardata) VALUES (:charstream) }; charstream.close(); ...
This section and code example at the end shows how to create a BinaryStream
object from a byte array and uses it to update the database.
You must determine the length of the data before updating the database from a byte array. (Presumably you would be updating a LONG RAW
column.) This is more trivial for arrays than for files, though, because all Java arrays have functionality to return the length.
Here are the steps in updating the database from a byte array:
length
functionality of the array to determine the length of the data. This returns an int
, which is what you will need for the constructor of any of the SQLJ stream classes.java.io.ByteArrayInputStream
object from your array. You can pass the byte array to the ByteArrayInputStream
constructor.BinaryStream
object. Pass the ByteArrayInputStream
object and data length (as an int
) to the BinaryStream
class constructor.
The constructor signature is as follows:
BinaryStream (InputStream in, int length)
You can use an instance of java.io.InputStream
or of any subclass, such as the ByteArrayInputStream
class.
The following is an example of writing LONG RAW
data to the database from a byte array. Presume you have a byte array bytearray[]
and you want to insert its contents into a LONG RAW
column called BINDATA
in a database table named BINTABLE
.
Imports:
import java.io.*; import sqlj.runtime.*;
Executable code:
byte[] bytearray = new byte[100]; (Populate bytearray somehow.) ... int length = bytearray.length; ByteArrayInputStream arraystream = new ByteArrayInputStream(bytearray); BinaryStream binstream = new BinaryStream(arraystream, length); #sql { INSERT INTO bintable (bindata) VALUES (:binstream) }; binstream.close(); ...
Note: It is not necessary to use a stream as in this example--you can also update the database directly from a byte array. |
You can also use the SQLJ stream classes to retrieve data, but the logistics of using streams make certain precautions necessary with some database products.
When reading long data and writing it to a stream using Oracle9i and an Oracle JDBC driver, you must be careful in how you access and process the stream data.
As the Oracle JDBC drivers access data from an iterator row, they must flush any stream item from the communications pipe before accessing the next data item. Even though the stream data is written to a local stream as the iterator row is processed, this stream data will be lost if you do not read it from the local stream before the JDBC driver accesses the next data item. This is because of the way streams must be processed, due to their potentially large size and unknown length.
Therefore, as soon as your Oracle JDBC driver has accessed a stream item and written it to a local stream variable, you must read and process the local stream before anything else is accessed from the iterator.
This is especially problematic in using positional iterators, with their requisite FETCH INTO
syntax. With each fetch, all columns are read before any are processed. Therefore, there can be only one stream item, and it must be the last item accessed.
To summarize the precautions you must take:
Furthermore, in processing each row of a named iterator, you must call the column accessor methods in the same order in which the database columns were selected in the query that populated the iterator. As mentioned in a similar preceding discussion, this is because stream data remains in the communications pipe after the query. If you try to access columns out of order, then the stream data may be skipped over and lost in the course of accessing other columns.
To retrieve data as a stream, standard SQLJ allows you to select data into a named or positional iterator that has a column of the appropriate SQLJ stream type.
This section covers the basic steps in retrieving data into a SQLJ stream using a positional iterator or a named iterator, taking into account the precautions documented in "Retrieving Data into Streams: Precautions".
These are general steps. For more information, see "Stream Class Methods" and "Examples of Retrieving and Processing Stream Data".
Use the following steps to retrieve data into a SQLJ stream using a positional iterator:
FETCH INTO
statement must be in the same order as the columns of the positional iterator, the local input stream variable is the last host variable in the list.Use the following steps to retrieve data into one or more SQLJ streams using a named iterator:
java.io.InputStream
or java.io.Reader
(as applicable) if desired. They do not have to be SQLJ stream types, because the data was already correctly formatted as a result of the iterator columns being of appropriate SQLJ stream types.To ensure that stream data will not be lost, call the column accessor methods in the same order in which columns were selected in the query in step 4.
Note: When you populate a SQLJ stream object with data, the length attribute of the stream will not be meaningful. This attribute is meaningful only when you set it explicitly, either using the |
In processing a SQLJ stream column in a named or positional iterator, the local stream variable used to receive the stream data can be either a SQLJ stream type or the standard java.io.InputStream
type or java.io.Reader
type (as applicable). In either case, standard methods of the input data object are supported.
If the local stream variable is a SQLJ stream type--BinaryStream
or CharacterStream
--you have the option of either reading data directly from the SQLJ stream object, or retrieving the underlying InputStream
or Reader
object and reading data from that. This is just a matter of preference--the former approach is simpler; the latter approach involves more direct and efficient data access.
The BinaryStream
class is a subclass of the sqlj.runtime.StreamWrapper
class. The StreamWrapper
class provides the following key methods:
InputStream getInputStream()
--You can optionally use this method to get the underlying java.io.InputStream
object. This is not required, however, as you can also process SQLJ stream objects directly.void setLength(int length)
--You can use this to set the length
attribute of a SQLJ stream object. This is not necessary if you have already set length
in constructing the stream object, unless you want to change it for some reason.
The length
attribute must be set to an appropriate value before you send a SQLJ stream to the database.
int getLength()
--This method returns the value of the length
attribute of a SQLJ stream. This value is meaningful only if you explicitly set it using the stream object constructor or the setLength()
method. When you retrieve data into a stream, the length
attribute is not set automatically.The sqlj.runtime.StreamWrapper
class is a subclass of the java.io.FilterInputStream
class, which is a subclass of the java.io.InputStream
class. The following important methods of the InputStream
class--the skip()
method, close()
method, and three forms of the read()
method--are supported by the SQLJ BinaryStream
class as well.
int read ()
--Reads the next byte of data from the input stream. The byte of data is returned as an int
value in the range 0 to 255. If the end of the stream has already been reached, then the value -1 is returned. This method blocks program execution until one of the following: 1) input data is available; 2) the end of the stream is detected; or 3) an exception is thrown.int read (byte b[])
--Reads up to b.length
bytes of data from the input stream, writing the data into the specified b[]
byte array. It returns an int
value indicating how many bytes were read or -1 if the end of the stream has already been reached. This method blocks program execution until input is available.int read (byte b[], int off, int len)
--Reads up to len
(length) bytes of data from the input stream, starting at the byte specified by the offset, off
, and writing the data into the specified b[]
byte array. It returns an int
value indicating how many bytes were read or -1 if the end of the stream has already been reached. This method blocks until input is available.long skip (long n)
--Skips over and discards n
bytes of data from the input stream. In some circumstances, however, this method will actually skip a smaller number of bytes. It returns a long
value indicating the actual number of bytes skipped.void close()
--Closes the stream and releases any associated resources.The CharacterStream
class provides the following key methods:
Reader getReader()
--You can optionally use this method to get the underlying java.io.Reader
object. This is not required, however, as you can also process SQLJ stream objects directly.void setLength(int length)
int getLength()
Use these to set or get the length of the stream object. This is the same functionality as for binary streams--see "Binary Stream Methods" immediately above.
The sqlj.runtime.CharacterStream
class is a subclass of the java.io.FilterReader
class, which is a subclass of the java.io.Reader
class. The following important methods of the Reader
class--the skip()
method, close()
method, and three forms of the read()
method--are supported by the SQLJ CharacterStream
class as well.
int read ()
--Reads the next character of data from the reader. The data is returned as an int
value in the range 0 to 65535. If the end of the data has already been reached, then the value -1 is returned. This method blocks program execution until one of the following: 1) input data is available; 2) the end of the data is detected; or 3) an exception is thrown.int read (char cbuf[])
--Reads characters into an array, writing the data into the specified cbuf[]
char array. It returns an int
value indicating how many characters were read or -1 if the end of the data has already been reached. This method blocks program execution until input is available.int read (char cbuf[], int off, int len)
--Reads up to len
(length) characters of data from the input, starting at the character specified by the offset, off
, and writing the data into the specified char[]
char array. It returns an int
value indicating how many characters were read or -1 if the end of the data has already been reached. This method blocks until input is available.long skip (long n)
--Skips over and discards n
characters of data from the input. In some circumstances, however, this method will actually skip a smaller number of characters. It returns a long
value indicating the actual number of characters skipped.void close()
--Closes the stream and releases any associated resources.This section provides examples of various scenarios of retrieving stream data, as follows:
SELECT
statement to select data from a LONG
column and populate a SQLJ CharacterStream
column in a named iteratorSELECT
statement to select data from a LONG RAW
column and populate a SQLJ BinaryStream
column in a positional iteratorThis example selects data from a LONG
database column, populating a SQLJ CharacterStream
column in a named iterator.
Assume there is a table named FILETABLE
with a VARCHAR2
column called FILENAME
that contains file names, and a LONG
column called FILECONTENTS
that contains file contents in character format.
Imports and declarations:
import sqlj.runtime.*; import java.io.*; ... #sql iterator MyNamedIter (String filename, CharacterStream filecontents);
Executable code:
MyNamedIter namediter = null; String fname; CharacterStream charstream; #sql namediter = { SELECT filename, filecontents FROM filetable }; while (namediter.next()) { fname = namediter.filename(); charstream = namediter.filecontents(); System.out.println("Contents for file " + fname + ":"); printStream(charstream); charstream.close(); } namediter.close(); ... public void printStream(Reader in) throws IOException { int character; while ((character = in.read()) != -1) { System.out.print((char)character); } }
Remember that you can pass a SQLJ character stream to any method that takes a standard java.io.Reader
as an input parameter.
This example selects data from a LONG RAW
column, populating a SQLJ BinaryStream
column in a positional iterator.
As explained in "Retrieving Data into Streams: Precautions", there can be only one stream column in a positional iterator, and it must be the last column.
Assume there is a table named BINTABLE
with a NUMBER
column called IDENTIFIER
and a LONG RAW
column called BINDATA
that contains binary data associated with the identifier.
Imports and declarations:
import sqlj.runtime.*; ... #sql iterator MyPosIter (int, BinaryStream);
Executable code:
MyPosIter positer = null; int id=0; BinaryStream binstream=null; #sql positer = { SELECT identifier, bindata FROM bintable }; while (true) { #sql { FETCH :positer INTO :id, :binstream }; if (positer.endFetch()) break; (...process data as desired...) binstream.close(); } positer.close(); ...
As described in the preceding sections, standard SQLJ supports use of the BinaryStream
and CharacterStream
classes in the package sqlj.runtime
for retrieval of stream data into iterator columns.
In addition, the Oracle SQLJ implementation allows the following uses of SQLJ stream types if you use Oracle9i, an Oracle JDBC driver, Oracle-specific code generation or the Oracle customizer, and the Oracle SQLJ runtime:
OUT
or INOUT
host variables from a stored procedure or function call.You can use the types BinaryStream
and CharacterStream
as the assignment type for a stored procedure or stored function OUT
or INOUT
parameter.
Assume the following table definition:
CREATE TABLE streamexample (name VARCHAR2 (256), data LONG); INSERT INTO streamexample (data, name) VALUES ('0000000000111111111112222222222333333333344444444445555555555', 'StreamExample');
Also presume the following stored procedure definition, which uses the STREAMEXAMPLE
table:
CREATE OR REPLACE PROCEDURE out_longdata (dataname VARCHAR2, longdata OUT LONG) IS BEGIN SELECT data INTO longdata FROM streamexample WHERE name = dataname; END out_longdata;
The following sample code uses a call to the out_longdata
stored procedure to read the long data.
Imports:
import sqlj.runtime.*;
Executable code:
CharacterStream data; #sql { CALL out_longdata('StreamExample', :OUT data) }; int c; while ((c = data.read ()) != -1) System.out.print((char)c); System.out.flush(); data.close(); ...
You can use the types BinaryStream
and CharacterStream
as the assignment type for a stored function return result.
Assume the same STREAMEXAMPLE
table definition as in the preceding stored procedure example.
Also assume the following stored function definition, which uses the STREAMEXAMPLE
table:
CREATE OR REPLACE FUNCTION get_longdata (dataname VARCHAR2) RETURN long IS longdata LONG; BEGIN SELECT data INTO longdata FROM streamexample WHERE name = dataname; RETURN longdata; END get_longdata;
The following sample code uses a call to the get_longdata
stored function to read the long data.
Imports:
import sqlj.runtime.*;
Executable code:
CharacterStream data; #sql data = { VALUES(get_longdata('StreamExample')) }; int c; while ((c = data.read ()) != -1) System.out.print((char)c); System.out.flush(); data.close(); ...
Oracle SQLJ offers extended functionality for the following JDBC 2.0 and Oracle-specific datatypes:
BLOB
and CLOB
)BFILE
datatypeROWID
datatypeNUMBER
and RAW
)These datatypes are supported by classes in the oracle.sql
package, discussed below. LOBs and BFILEs are handled similarly in many ways, so are discussed together.
Additionally, Oracle SQLJ offers extended support for the following standard JDBC type:
JDBC 2.0 functionality for user-defined SQL objects (both weakly and strongly typed), object references, and collections (variable arrays and nested tables) are also supported. These are discussed in Chapter 6, "Objects, Collections, and OPAQUE Types".
Note that using Oracle extensions in your code requires the following:
-codegen=oracle
setting) or, for ISO code generation (-codegen=iso
), customize the profiles appropriately. The default customizer, oracle.sqlj.runtime.util.OraCustomizer
, is recommended.The Oracle SQLJ runtime and an Oracle JDBC driver are required whenever you use the Oracle customizer, even if you do not actually use Oracle extensions in your code.
For Oracle-specific semantics-checking, you must use an appropriate checker. The default checker, oracle.sqlj.checker.OracleChecker
, acts as a front end and will run the appropriate checker based on your environment. This will be one of the Oracle-specific checkers if you are using an Oracle JDBC driver.
Oracle-specific types are defined in the oracle.sql
package, discussed in "Package oracle.sql" below.
SQLJ users, as well as JDBC users, should be aware of the oracle.sql
package, which includes classes to support all the Oracle9i datatypes (for example, oracle.sql.ROWID
, oracle.sql.CLOB
, and oracle.sql.NUMBER
). The oracle.sql
classes are wrappers for the raw SQL data and provide appropriate mappings and conversion methods to Java formats. An oracle.sql.*
object contains a binary representation of the corresponding SQL data in the form of a byte array.
Each oracle.sql.*
datatype class is a subclass of the oracle.sql.Datum
class.
For Oracle-specific semantics-checking, you must use an appropriate checker. The default checker, oracle.sqlj.checker.OracleChecker
, acts as a front end and will run the appropriate checker based on your environment. This will be one of the Oracle-specific checkers if you are using an Oracle JDBC driver.
For information about translator options relating to semantics-checking, see "Connection Options" and "Semantics-Checking and Offline-Parsing Options".
For more information about the oracle.sql
classes, see the Oracle9i JDBC Developer's Guide and Reference.
Oracle JDBC and SQLJ support JDBC 2.0 large object (LOB) datatypes--BLOB
(binary LOB) and CLOB
(character LOB)--and provide similar support for the Oracle-specific BFILE
type (read-only binary files stored outside the database). These datatypes are supported by the following classes:
See the Oracle9i JDBC Developer's Guide and Reference for more information about LOBs and files and use of supported stream APIs.
The oracle.sql.BLOB
, oracle.sql.CLOB
, and oracle.sql.BFILE
classes can be used in Oracle-specific SQLJ applications in the following ways:
IN
, OUT
, or INOUT
host variables in executable SQLJ statements (including use in INTO-lists)You can manipulate LOBs by using methods defined in the BLOB
and CLOB
classes (recommended) or by using the procedures and functions defined in the PL/SQL package DBMS_LOB
. All procedures and functions defined in this package can be called by SQLJ programs.
You can manipulate BFILEs by using methods defined in the BFILE
class (recommended) or by using the file-handling routines of the DBMS_LOB
package.
Using methods of the BLOB
, CLOB
, and BFILE
classes in a Java application is more convenient than using the DBMS_LOB
package and may also lead to faster execution in some cases.
Note that the type of the chunk being read or written depends on the kind of LOB being manipulated. For example, CLOBs contain character data; therefore, Java strings are used to hold chunks of data. BLOBs contain binary data; therefore, Java byte arrays are used to hold chunks of data.
Note:
|
The following examples contrast use of the oracle.sql
methods with use of the DBMS_LOB
package for BFILEs.
This example manipulates a BFILE using file-handling methods of the oracle.sql.BFILE
class.
BFILE openFile (BFILE file) throws SQLException { String dirAlias, name; dirAlias = file.getDirAlias(); name = file.getName(); System.out.println("name: " + dirAlias + "/" + name); if (!file.isFileOpen()) { file.openFile(); } return file; }
The BFILE
getDirAlias()
and getName()
methods construct the full path and file name. The openFile()
method opens the file. You cannot manipulate BFILEs until they have been opened.
This example manipulates a BFILE using file-handling routines of the DBMS_LOB
package.
BFILE openFile(BFILE file) throws SQLException { String dirAlias, name; #sql { CALL dbms_lob.filegetname(:file, :out dirAlias, :out name) }; System.out.println("name: " + dirAlias + "/" + name); boolean isOpen; #sql isOpen = { VALUES(dbms_lob.fileisopen(:file)) }; if (!isOpen) { #sql { CALL dbms_lob.fileopen(:inout file) }; } return file; }
The openFile()
method prints the name of a file object then returns an opened version of the file. Note that BFILEs can be manipulated only after being opened with a call to DBMS_LOB.FILEOPEN
or equivalent method in the BFILE
class.
The following examples contrast use of the oracle.sql
methods with use of the DBMS_LOB
package for BLOBs and CLOBs. For each example using oracle.sql
methods, the example that follows it is functionally identical but uses DBMS_LOB
instead.
This example reads data from a CLOB using methods of the oracle.sql.CLOB
class.
void readFromClob(CLOB clob) throws SQLException { long clobLen, readLen; String chunk; clobLen = clob.length(); for (long i = 0; i < clobLen; i+= readLen) { chunk = clob.getSubString(i, 10); readLen = chunk.length(); System.out.println("read " + readLen + " chars: " + chunk); } }
This method contains a loop that reads from the CLOB and returns a 10-character Java string each time. The loop continues until the entire CLOB has been read.
This example uses routines of the DBMS_LOB
package to read from a CLOB.
void readFromClob(CLOB clob) throws SQLException { long clobLen, readLen; String chunk; #sql clobLen = { VALUES(dbms_lob.getlength(:clob)) }; for (long i = 1; i <= clobLen; i += readLen) { readLen = 10; #sql { CALL dbms_lob.read(:clob, :inout readLen, :i, :out chunk) }; System.out.println("read " + readLen + " chars: " + chunk); } }
This method reads the contents of a CLOB in chunks of 10 characters at a time. Note that the chunk host variable is of the type String
.
This example writes data to a BLOB using methods of the oracle.sql.BLOB
class. Input a BLOB and specified length.
void writeToBlob(BLOB blob, long blobLen) throws SQLException { byte[] chunk = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; long chunkLen = (long)chunk.length; for (long i = 0; i < blobLen; i+= chunkLen) { if (blobLen < chunkLen) chunkLen = blobLen; chunk[0] = (byte)(i+1); chunkLen = blob.putBytes(i, chunk); } }
This method goes through a loop that writes to the BLOB in 10-byte chunks until the specified BLOB length has been reached.
This example uses routines of the DBMS_LOB
package to write to a BLOB.
void writeToBlob(BLOB blob, long blobLen) throws SQLException { byte[] chunk = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; long chunkLen = (long)chunk.length; for (long i = 1; i <= blobLen; i += chunkLen) { if ((blobLen - i + 1) < chunkLen) chunkLen = blobLen - i + 1; chunk[0] = (byte)i; #sql { CALL dbms_lob.write(:INOUT blob, :chunkLen, :i, :chunk) }; } }
This method fills the contents of a BLOB in 10-byte chunks. Note that the chunk host variable is of the type byte[]
.
Host variables of type BLOB
, CLOB
, and BFILE
can be assigned to the result of a stored function call. The following example is for a CLOB, but code for BLOBs and BFILEs would be functionally the same.
First, presume the following function definition:
CREATE OR REPLACE function longer_clob (c1 clob, c2 clob) return clob is result clob; BEGIN if dbms_lob.getLength(c2) > dbms_lob.getLength(c1) then result := c2; else result := c1; end if; RETURN result; END longer_clob;
The following example uses a CLOB as the assignment type for a return value from the function defined above.
void readFromLongest(CLOB c1, CLOB c2) throws SQLException { CLOB longest; #sql longest = { VALUES(longer_clob(:c1, :c2)) }; readFromClob(longest); }
The readFromLongest()
method prints the contents of the longer passed CLOB, using the readFromClob()
method defined previously.
Host variables of type BLOB
, CLOB
, and BFILE
can appear in the INTO-list of a SELECT INTO
executable statement. The following example is for a BLOB and CLOB, but code for BFILEs would be functionally the same.
Assume the following table definition:
CREATE TABLE basic_lob_table(x varchar2(30), b blob, c clob);
INSERT INTO basic_lob_table
VALUES('one', '010101010101010101010101010101', 'onetwothreefour');
INSERT INTO basic_lob_table
VALUES('two', '020202020202020202020202020202', 'twothreefourfivesix');
The following example uses a BLOB and a CLOB as host variables that receive data from the table defined above, using a SELECT INTO
statement.
... BLOB blob; CLOB clob; #sql { SELECT one.b, two.c INTO :blob, :clob FROM basic_lob_table one, basic_lob_table two WHERE one.x='one' AND two.x='two' }; #sql { INSERT INTO basic_lob_table VALUES('three', :blob, :clob) }; ...
This example selects the BLOB from the first row and the CLOB from the second row of the BASIC_LOB_TABLE
. It then inserts a third row into the table using the BLOB and CLOB selected in the previous operation.
The types BLOB
, CLOB
, and BFILE
can be used as column types for SQLJ positional and named iterators. Such iterators can be populated as a result of compatible executable SQLJ operations.
Here are sample declarations that will be repeated and used below.
#sql iterator NamedLOBIter(CLOB c); #sql iterator PositionedLOBIter(BLOB); #sql iterator NamedFILEIter(BFILE bf);
The following example employs the table BASIC_LOB_TABLE
and the method readFromLongest()
defined in previous examples, and uses a CLOB in a named iterator. Similar code could be written for BLOBs and BFILEs.
Declaration:
#sql iterator NamedLOBIter(CLOB c);
Executable code:
... NamedLOBIter iter; #sql iter = { SELECT c FROM basic_lob_table }; if (iter.next()) CLOB c1 = iter.c(); if (iter.next()) CLOB c2 = iter.c(); iter.close(); readFromLongest(c1, c2); ...
This example uses an iterator to select two CLOBs from the first two rows of the BASIC_LOB_TABLE
, then prints the larger of the two using the readFromLongest()
method.
Host variables of type BLOB
, CLOB
, and BFILE
can be used with positional iterators and appear in the INTO-list of the associated FETCH INTO
statement if the corresponding column attribute in the iterator is of the identical type.
The following example employs table BASIC_LOB_TABLE
and method writeToBlob()
defined in previous examples. Similar code could be written for CLOBs and BFILEs.
Declaration:
#sql iterator PositionedLOBIter(BLOB);
Executable code:
... PositionedLOBIter iter; BLOB blob = null; #sql iter = { SELECT b FROM basic_lob_table }; for (long rowNum = 1; ; rowNum++) { #sql { FETCH :iter INTO :blob }; if (iter.endFetch()) break; writeToBlob(blob, 512*rowNum); } iter.close(); ...
This example calls writeToBlob()
for each BLOB in BASIC_LOB_TABLE
. Each row writes an additional 512 bytes of data.
The Oracle-specific type ROWID
stores the unique address for each row in a database table. The class oracle.sql.ROWID
wraps ROWID information and is used to bind and define variables of type ROWID
.
Variables of type oracle.sql.ROWID
can be employed in SQLJ applications connecting to Oracle9i in the following ways:
IN
, OUT
or INOUT
host variables in SQLJ executable statements (including use in INTO-lists)You can use the type oracle.sql.ROWID
as a column type for SQLJ positional and named iterators, as shown in the following declarations:
#sql iterator NamedRowidIter (String ename, ROWID rowid); #sql iterator PositionedRowidIter (String, ROWID);
You can employ ROWID
objects as IN
, OUT
and INOUT
parameters in SQLJ executable statements. In addition, you can populate iterators whose columns include ROWID
types. This code example uses the preceding example declarations.
Declaration:
#sql iterator NamedRowidIter (String ename, ROWID rowid);
Executable code:
... NamedRowidIter iter; ROWID rowid; #sql iter = { SELECT ename, rowid FROM emp }; while (iter.next()) { if (iter.ename().equals("CHUCK TURNER")) { rowid = iter.rowid(); #sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid }; } } iter.close(); ...
The preceding example increases the salary of the employee named Chuck Turner by $500 according to the ROWID. Note that this is the recommended way to encode WHERE CURRENT OF
semantics.
Presume the following function exists in Oracle9i.
CREATE OR REPLACE function get_rowid (name varchar2) return rowid is rid rowid; BEGIN SELECT rowid INTO rid FROM emp WHERE ename = name; RETURN rid; END get_rowid;
Given the preceding stored function, the following example indicates how a ROWID
object is used as the assignment type for the function return result.
ROWID rowid; #sql rowid = { values(get_rowid('AMY FEINER')) }; #sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid };
This example increases the salary of the employee named Amy Feiner by $500 according to the ROWID.
Host variables of type ROWID
can appear in the INTO-list of a SELECT INTO
statement.
ROWID rowid; #sql { SELECT rowid INTO :rowid FROM emp WHERE ename='CHUCK TURNER' }; #sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid };
This example increases the salary of the employee named Chuck Turner by $500 according to the ROWID.
Host variables of type ROWID
can appear in the INTO-list of a FETCH INTO
statement if the corresponding column attribute in the iterator is of the identical type.
Declaration:
#sql iterator PositionedRowidIter (String, ROWID);
Executable code:
... PositionedRowidIter iter; ROWID rowid = null; String ename = null; #sql iter = { SELECT ename, rowid FROM emp }; while (true) { #sql { FETCH :iter INTO :ename, :rowid }; if (iter.endFetch()) break; if (ename.equals("CHUCK TURNER")) { #sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid }; } } iter.close(); ...
This example is similar to the previous named iterator example, but uses a positional iterator with its customary FETCH INTO
syntax.
Oracle PL/SQL and Oracle SQLJ support the use of cursor variables that represent database cursors.
Cursor variables are functionally equivalent to JDBC result sets, essentially encapsulating the results of a query. A cursor variable is often referred to as a REF CURSOR, but REF CURSOR
itself is a type specifier, not a type name. Instead, named REF CURSOR types must be specified. The following example shows a REF CURSOR type specification:
TYPE EmpCurType IS REF CURSOR;
Stored procedures and stored functions can return parameters of Oracle REF CURSOR types. You must use PL/SQL to return a REF CURSOR parameter; you cannot accomplish this using SQL alone. A PL/SQL stored procedure or function can declare a variable of some named REF CURSOR type, execute a SELECT
statement, and return the results in the REF CURSOR variable.
For more information about cursor variables, see the PL/SQL User's Guide and Reference.
In Oracle SQLJ, a REF CURSOR type can be mapped to iterator columns or host variables of any iterator class type or of type java.sql.ResultSet
, but host variables can be OUT
only. Support for REF CURSOR types can be summarized as follows:
You can use the Oracle SQL CURSOR
operator for a nested SELECT
within an outer SELECT
statement. This is how you can write a REF CURSOR object to an iterator column or ResultSet
column in an iterator, or write a REF CURSOR object to an iterator host variable or ResultSet
host variable in an INTO-list.
"Using Iterators and Result Sets as Host Variables" has examples showing the use of implicit REF CURSOR variables, including an example of the CURSOR
operator.
The following sample method shows a REF CURSOR type being retrieved from an anonymous block.
private static EmpIter refCursInAnonBlock(String name, int no) throws java.sql.SQLException { EmpIter emps = null; System.out.println("Using anonymous block for ref cursor.."); #sql { begin INSERT INTO emp (ename, empno) VALUES (:name, :no); OPEN :out emps FOR SELECT ename, empno FROM emp ORDER BY empno; end }; return emps; }
All oracle.sql
classes can be used for iterator columns or for input, output, or input-output host variables in the same way that any standard Java type can be used. This includes the classes mentioned in the preceding sections and others, such as the oracle.sql.NUMBER
, oracle.sql.CHAR
, and oracle.sql.RAW
classes.
Because the oracle.sql.*
classes do not require conversion to Java type format, they offer greater efficiency and precision than equivalent Java types. You would have to convert the data to standard Java types, however, to use it with standard Java programs or to display it to end users.
SQLJ supports java.math.BigDecimal
in the following situations:
Standard SQLJ has the limitation that a value can be retrieved as BigDecimal
only if that is the JDBC default mapping, which is the case only for numeric and decimal data. (See Table 5-1 for more information about JDBC default mappings.)
In Oracle SQLJ, however, you can map to nondefault types as long as the datatype is convertible from numeric and you use Oracle9i, an Oracle JDBC driver, Oracle-specific code generation (or the Oracle customizer), and the Oracle SQLJ runtime. The datatypes CHAR
, VARCHAR2
, LONG
, and NUMBER
are convertible. For example, you can retrieve data from a CHAR
column into a BigDecimal
variable. To avoid errors, however, you must be careful that the character data consists only of numbers.
|
Copyright © 1999, 2002 Oracle Corporation. All Rights Reserved. |
|