Oracle8i SQLJ Developer's Guide and Reference Release 8.1.5 A64684-01 |
|
Standard SQLJ provides three specialized classes, listed below, for convenient handling 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 handling and transferring large data items in manageable chunks.
These classes are in the sqlj.runtime
package.
This section discusses general use of these classes, Oracle SQLJ extended functionality, and stream class methods.
With respect to an Oracle8i database, Table 5-1 in "Supported Types for Host Expressions" lists the datatypes you would typically process using these stream classes. To summarize: AsciiStream
and UnicodeStream
are typically used for datatype LONG
(java.sql.Types.LONGVARCHAR
) but might also be used for datatype VARCHAR2
(Types.VARCHAR
); BinaryStream
is typically used for datatype LONG RAW
(Types.LONGVARBINARY
) but might also be used for datatype RAW
(Types.BINARY
or Types.VARBINARY
).
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). Oracle8i supports large object types--see "Support for BLOB, CLOB, and BFILE".
All three SQLJ stream classes are subclasses of the standard Java input stream class, java.io.InputStream
, and act as wrappers to provide the functionality required by SQLJ. This functionality is to communicate to SQLJ the type and length of data in the underlying stream so that it can be handled and formatted properly.
You can use the SQLJ stream types for host variables to send data to the database or iterator columns to receive data from the database.
Note:
In using any method that takes an |
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:
java.io.InputStream
or some subclass--as you normally would.
int
) to the constructor.
This section now goes 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 an ASCII or Unicode file to update a LONG
column.)
LONG RAW
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.FileInputStream
object from your File
object. You can pass the File
object to the FileInputStream
constructor.
BinaryStream
object for a binary file, an AsciiStream
object for an ASCII file, or a UnicodeStream
object for a Unicode file. Pass the FileInputStream
object and data length (as an int
) to the SQLJ stream class constructor.
The SQLJ stream constructor signatures are all identical, as follows:
BinaryStream (InputStream in, int length) AsciiStream (InputStream in, int length) UnicodeStream (InputStream in, int length)
An instance of java.io.InputStream
or of any subclass, such as FileInputStream
, can be input to these constructors.
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 asciidata
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. FileInputStream fileinstream = new FileInputStream(myfile); AsciiStream asciistream = new AsciiStream(fileinstream, length); #sql { INSERT INTO filetable (asciidata) VALUES (:asciistream) }; asciistream.close(); ...
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 ByteArrayInputStream
.
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 from the database, 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 an Oracle8i database and 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 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 from a database column 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. This discussion takes into account the precautions documented in "Retrieving Data into Streams--Precautions".
These are general steps. For more information, see "Processing SQLJ Streams" 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
if desired. (They do not have to be SQLJ stream types because the issue of correctly formatting the data from the database was already taken care of 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 from an iterator or the database, the length attribute of the stream will not be meaningful. This attribute is only meaningful 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. In either case, standard input stream methods are supported.
If the local stream variable is a SQLJ stream type--BinaryStream
, AsciiStream
, or UnicodeStream
--you have the option of either reading data directly from the SQLJ stream object, or retrieving the underlying java.io.InputStream
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 following important methods of the InputStream
class--the skip()
method, close()
method, and three forms of the read()
method--are supported by the SQLJ stream classes 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, the value -1 is returned. This method blocks 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 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.
In addition, SQLJ stream classes support the following important method:
InputStream getInputStream()
--Returns the underlying input stream being wrapped, as a java.io.InputStream
object.
This section provides examples of various scenarios of retrieving stream data from the database, as follows:
SELECT
statement to select data from a LONG
column and populate a SQLJ AsciiStream
column in a named iterator
SELECT
statement to select data from a LONG RAW
column and populate a SQLJ BinaryStream
column in a positional iterator
This example selects data from a LONG
database column, populating a SQLJ AsciiStream
column in a named iterator.
Presume 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 ASCII.
Imports and declarations:
import sqlj.runtime.*; import java.io.*; #sql iterator MyNamedIter (String filename, AsciiStream filecontents);
Executable code:
MyNamedIter namediter = null; String fname; AsciiStream ascstream; #sql namediter = { SELECT filename, filecontents FROM filetable }; while (namediter.next()) { fname = namediter.filename(); ascstream = namediter.filecontents(); System.out.println("Contents for file " + fname + ":"); printStream(ascstream); ascstream.close(); } namediter.close(); ... public void printStream(InputStream in) throws IOException { int asciichar; while ((asciichar = in.read()) != -1) { System.out.print((char)asciichar); } }
Remember that you can pass a SQLJ stream to any method that takes a standard java.io.InputStream
as an input parameter.
This example selects data from a LONG RAW
database 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.
Presume 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
, AsciiStream
, and UnicodeStream
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 are using an Oracle database, Oracle JDBC driver, and the Oracle customizer:
OUT
or INOUT
host variables from a stored procedure or function call.
You can use the types AsciiStream
, BinaryStream
and UnicodeStream
as the assignment type for a stored procedure or stored function OUT
or INOUT
parameter.
Presume 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:
AsciiStream 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 AsciiStream
, BinaryStream
and UnicodeStream
as the assignment type for a stored function return result.
Presume the same streamexample
table definition as in the preceding stored procedure example.
Also presume 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:
AsciiStream data; #sql data = { VALUES(get_longdata('StreamExample')) }; int c; while ((c = data.read ()) != -1) System.out.print((char)c); System.out.flush(); data.close(); ...
The SQLJ stream classes in the sqlj.runtime
package--BinaryStream
, AsciiStream
, and UnicodeStream
--are all subclasses of the sqlj.runtime.StreamWrapper
class.
The StreamWrapper
class provides the following methods that are inherited by the SQLJ stream classes:
InputStream getInputStream()
--As discussed in "Processing SQLJ Streams", you can optionally use this method to get the underlying java.io.InputStream
object of any SQLJ stream 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.
Bear in mind that 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 only meaningful 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.
Note:
The |