この章では、Oracle SQLJ実装でサポートされているデータ型について説明します。サポートされているSQL型とそれに対応するJava型のリストを示します。さらに、ストリームとOracle拡張型のサポートについても詳しく説明します。SQLJでサポートされているJava型は、ホスト式で使用される型です。
この章では、次の項目について説明します。
ここでは、Oracle SQLJ実装でサポートされている型について概説します。具体的には、Java Database Connectivity(JDBC)2.0の型に関して新規にサポートされた点について説明します。
|
参照: 各Oracle SQL型ごとの正当なJavaマッピングに関するすべてのリストは、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。 |
|
注意: SQLJでは、SQL型とJava型との変換が暗黙的に実行されるようになっています。この型変換は、一般的には便利ですが、予想と異なる結果を招く場合もあります。コードが正確かどうかの確認は、変換時の型チェックのみに頼らないでください。 |
ここでは、次の項目について説明します。
Oracle JDBCドライバを使用した場合にホスト式で使用できるJava型を、表5-1に示します。また、Java型、SQL型(oracle.jdbc.OracleTypesクラスに型コードが定義されている)およびOracle Database 11g のデータ型との相関関係もこの表からわかります。
|
注意: OracleTypesクラスは、各Oracleデータ型の型コード(整数の定数)のみを定義します。標準JDBC型のOracleTypesの値は、標準java.sql.Typesの値と同じです。 |
Java変数に出力されるSQLデータは、該当するJava型に変換されます。SQLに入力されるJava変数は、該当するOracleデータ型に変換されます。
表5-1 ホスト式でサポートされている型の対応
| Java型 | Oracle型定義 | Oracle SQLデータ型 |
|---|---|---|
|
標準JDBC 1.x型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
標準JDBC 2.0型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
オブジェクト型 |
|
|
|
参照型 |
|
|
|
コレクション型 |
|
|
|
オブジェクト型 |
|
Javaラッパー・クラス |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SQLJストリーム・クラス |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Oracle拡張型 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
オブジェクト型 |
|
|
|
参照型 |
|
|
|
コレクション型 |
|
|
|
|
|
|
|
オブジェクト型 |
|
|
|
参照型 |
|
|
|
コレクション型 |
|
|
|
|
|
|
任意 |
任意 |
|
SQLJオブジェクトJava型( |
|
SQLJオブジェクトSQL型(背後で使用される |
|
Java型(PL/SQL型用) |
|
|
|
スカラーの索引付き表で、Java数値配列または |
なし |
なし 注意: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
問合せ結果オブジェクト |
|
|
|
|
|
|
|
SQLJイテレータ・オブジェクト |
|
|
|
参照: Oracle型のサポートの詳細は、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。 |
標準機能での型のサポートについては、次に要点を示します。
Java char型とCharacter型は、JDBCおよびSQLJではサポートされていません。文字データを表現するときは、かわりにJavaのString型を使用してください。
サポートされているjava.sql.Date型と、直接にはサポートされていないjava.util.Date型とを混同しないようにしてください。java.sql.Dateクラス(java.util.Dateのラッパー)を使用すると、JDBCの日付値に関して、SQLのDATEデータを識別したり、JDBCエスケープ構文をサポートする書式設定操作および解析操作を加えたりできるようになります。
Oracle Database 11g のすべての数値型は、NUMBERとして格納されます。 表作成時にNUMBERを宣言すると、さらに詳細に精度を指定できます。ただし、Oracle JDBCドライバを介してデータを取得する場合、データの受取り側のJava型によっては、この精度が損なわれることがあります。完全な情報は、oracle.sql.NUMBERインスタンスに保持されます。
Javaのラッパー・クラス(IntegerやFloatなど)を使用すると、SQL文からNULLが戻された場合に役立ちます。基本型(intやfloatなど)には、NULL値を格納できないためです。
ストリームをホスト変数として使用するときは、SQLJストリーム・クラスが必要です。
弱い型指定は、OUTパラメータまたはINOUTパラメータには使用できません。対応するOracle拡張型と同様に、これはStruct、RefおよびArrayの標準的なJDBC 2.0型に適用されます。
一連の新しいインタフェースは、oracle.jdbcパッケージにあり、oracle.jdbc.driverパッケージに代わり、Oracle9i JDBC実装で最初に追加されました。これらのインタフェースには、ユーザーがOracle JDBCドライバによってOracle固有機能を利用するためのより汎用的な方法が用意されています。特に、中間層のプログラムを作成する場合は、oracle.jdbc Application Program Interface(API)を使用してください。ただし、SQLJプログラマは通常これらのインタフェースを直接使用しません。これらのインタフェースは、SQLJランタイムまたはOracle固有生成コードで透過的に使用されます。
結果セットおよびイテレータのホスト変数に対するSQLJのサポートの詳細は、「ホスト変数としてのイテレータおよび結果セットの使用」を参照してください。
次に、Oracleの拡張型について要点を示します。
Oracle SQLJ実装でOracleTypesクラスに定義されている値に応じて静的_SQL_TYPECODEパラメータを設定するには、oracle.sql.ORADataを実装するクラスが必要です。場合によっては、オブジェクト用の_SQL_NAMEやオブジェクト参照用の_SQL_BASETYPEなど、_SQL_TYPECODE以外のパラメータを設定する必要がある場合もあります。 Oracle JPublisherユーティリティを使用すれば、必要なクラスを自動的に生成できます。
oracle.sqlクラスはSQLデータのラッパーです。Oracleの各データ型に対応しています。ARRAY、STRUCT、REF、BLOBおよびCLOBクラスは、標準JDBC 2.0インタフェースに対応します。
|
参照: これらのクラスとOracle拡張型の詳細は、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。 |
カスタムJavaクラスでマッピングの対象となるのは、Oracleオブジェクト(ORADataまたはSQLDataを実装)、参照(ORADataのみを実装)、コレクション(ORADataのみを実装)、OPAQUE型(ORADataのみを実装)またはその他のカスタマイズ処理に使用するSQL型(ORADataのみを実装)です。 Oracle JPublisherユーティリティを使用すれば、カスタムJavaクラスを自動的に生成できます。
Oracle SQLJ実装では、文字列をWHERE句のCHAR列値と比較するときに、自動的に空白埋めを考慮する機能があります。この機能がない場合は、データベース列の文字数と一致させるために文字列に対する埋込みが必要です。この機能は、Oracle固有コード生成ではSQLJトランスレータ・オプション、ISO標準コード生成ではOracleカスタマイザ・オプションとして使用できます。
弱い型指定は、OUTパラメータまたはINOUTパラメータには使用できません。Oracle OPAQUE型と同様に、これはSTRUCT、REFおよびARRAYのOracle拡張型および対応する標準的なJDBC 2.0型に適用されます。
次に、Oracle拡張機能の使用に必要な項目を示します。
Oracle JDBCドライバ
変換時のOracle固有コード生成またはOracleのカスタマイズ
アプリケーション実行時のOracle SQLJランタイム
表5-1に示したとおり、Oracle JDBCおよびSQLJの実装で標準java.sqlパッケージのJDBC 2.0型がサポートされています。ここでは、JDBC 2.0でサポートされている型および関連するOracleの拡張機能について説明します。
Oracle SQLJ実装でサポートされているJDBC 2.0型を、表5-2に示します。このJDBC 2.0型とそれに対応するOracle拡張型については、この表を参照してください。
Oracle拡張型は、以前のリリースから提供されていたもので、引き続き現行のリリースでも利用できます。これらのoracle.sql.*クラスでは、SQLのRAWデータをラップする機能が用意されています。
|
参照: 『Oracle Database JDBC開発者ガイドおよびリファレンス』 |
表5-2 Oracle拡張型とJDBC 2.0型との相関関係
| JDBC 2.0型 | Oracle拡張型 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
なし |
|
なし |
|
ORAData機能は、ユーザー定義型のJavaサポートの標準SQLData機能に対するOracle固有の代替機能です。
次に示すJDBC 2.0型は、現在のOracle JDBCやSQLJの実装ではサポートされていません。
JAVA_OBJECT: SQL列のJava型のインスタンスを表す型。
DISTINCT: 基本SQL型と区別して表現や取出しが可能なSQL型。たとえば、SHOESIZE --> NUMBERなど。
Oracle SQLJおよびJDBCの実装では、コール用引数または戻り値として、PL/SQLのBOOLEAN型またはRECORD型をサポートしていません。
TABLE型のサポート
Oracle JDBC OCIドライバでは、PL/SQLのスカラー索引付き表がサポートされています。
|
参照: 『Oracle Database JDBC開発者ガイドおよびリファレンス』 |
Oracle SQLJ実装によって、スカラー索引付き表でのデータのやりとりが簡素化されます。次の配列型がサポートされます。
数値型: int[]、long[]、float[]、double[]、short[]、java.math.BigDecimal[]、oracle.sql.NUMBER[]
文字型: java.lang.String[]、oracle.sql.CHAR[]
次に、索引付き表のデータをデータベースに書き込む例を示します。
int[] vals = {1,2,3};
#sql { call procin(:vals) };
次に、索引付き表のデータをデータベースから取り出す例を示します。
oracle.sql.CHAR[] outvals;
#sql { call procout(:OUT outvals/*[111](22)*/) };
このように/*...*/内の[xxx]構文を使用して、取得する出力配列の最大長を指定する必要があります。また、文字などのバインドの場合は、オプションで(xx)構文を指定し、例のように配列要素の最大長をバイト単位で指定できます。
|
注意: oracle.sql.Datumクラスは、直接サポートされません。oracle.sql.CHARまたはoracle.sql.NUMBERなどの適切なサブクラスを使用する必要があります。 |
サポートされない型に対する次善策
サポートされない型の問題を回避するには、ラッパー・プロシージャを作成し、サポートされている型を使用してデータを処理します。たとえば、PL/SQLのブール値を使用するストアド・プロシージャをラッピングするには、JDBCから文字または数字をとり、これを元のプロシージャにBOOLEANとして渡すストアド・プロシージャを作成します。出力パラメータの場合は、元のプロシージャからBOOLEAN引数を受け取り、これをCHARまたはNUMBERとしてJDBCに渡すようにします。同様に、PL/SQLのRECORD型を使用するストアド・プロシージャをラッピングするには、レコードの各コンポーネント(CHARやNUMBERなど)を処理するストアド・プロシージャを作成します。PL/SQLのTABLE型を使用するストアド・プロシージャをラッピングするには、データを複数のコンポーネントに分解するか、またはOracleのコレクション型を使用します。
次に、PL/SQLのラッパー・プロシージャMY_PROCの例を示します。このプロシージャは、入力としてBOOLEANをとるストアド・プロシージャPROCをラップします。
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;
|
注意: PL/SQLパッケージまたはSQLオブジェクトで、メソッドとしてサポートされないPL/SQL型のシグネチャを使用する場合は、Oracle Database 11g JPublisherユーティリティを使用することを検討してください。このユーティリティによって、そのようなメソッドをコールするJava型の作成が容易になります。JPublisherの概要は、「JPublisherとカスタムJavaクラスの作成」、JPublisherの詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。 |
標準SQLJには、ロング・データをストリームとして処理する次の2つの専用クラスがあります。
sqlj.runtime.BinaryStream
sqlj.runtime.CharacterStream
イテレータ列でデータベースからデータを取り出すとき、または入力ホスト変数を使用してデータをデータベースに送信するときに、これらのストリーム型を使用します。一般のJavaストリームと同じように、これらのクラスでは、大きなデータ項目を扱いやすい大きさの塊に分割して処理し、転送できます。
ここでは、これらのクラス、Oracle固有のSQLJ拡張機能およびストリーム・クラスのメソッドの一般的な使用方法について説明します。次の項目について説明します。
|
注意: JDBC 2.0では、AsciiStreamクラスおよびUnicodeStreamクラスは、CharacterStreamクラスに置換されています。CharacterStreamによって、ユーザーはエンコーディングに関する不要なロジスティクスから解放されます。 |
通常、表5-1のデータ型をこれらのストリーム・クラスで処理します。つまり、
BinaryStreamは通常、LONG RAW(Types.LONGVARBINARY)で使用されますが、RAW(Types.BINARYまたはTypes.VARBINARY)でも使用されます。
CharacterStreamは通常、LONG(java.sql.Types.LONGVARCHAR)で使用されますが、VARCHAR2(Types.VARCHAR)でも使用されます。
ストリームの使用方法は自由です。ホスト変数にSQLJストリーム型を使用して、データの送信または取出しが実行できます。
表5-1に示したように、LONGデータとVARCHAR2データは、JavaのStringでも使用できます。一方、RAWデータとLONGRAWデータは、Javaのbyte[]配列でも使用できます。また、データベースがBLOBやCLOBなどのラージ・オブジェクト型をサポートする場合は、LONGやLONG RAWなどの型よりBLOBやCLOBの方が便利な場合もあります。ラージ・オブジェクトからのデータの抽出には、ストリームも使用できます。Oracle SQLJおよびJDBC実装では、ラージ・オブジェクト型がサポートされます。
SQLJストリーム・クラスは両方ともに標準Javaクラスのサブクラス(BinaryStreamのjava.io.InputStreamおよびCharacterStreamのjava.io.Reader)で、SQLJに必要な機能を提供するラッパーとして動作します。つまり、基になるデータが適切に処理および変換されるように、データのデータ型とデータ長をSQLJに通知します。
次の略記されたコードは、BinaryStreamクラスの主な特長(拡張される内容、コンストラクタのシグネチャ、および主要メソッドのシグネチャなど)を示しています。
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);
}
次の略記されたコードは、CharacterStreamクラスの主な特長を示しています。
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);
}
|
注意:
|
標準SQLJでは、ストリームをホスト変数として使用して、データベースを更新できます。SQLJストリームをデータベースに送信するときは、データの長さを特定し、この長さをSQLJストリームのコンストラクタに対して指定する必要があります。
次の手順で、SQLJストリームを使用して、データをデータベースに送信します。
データの長さを特定します。
入力として適切な標準Javaデータ・オブジェクトを作成します。BinaryStreamの場合は、入力ストリーム(java.io.InputStreamまたはいくつかのサブクラスのインスタンス)を作成します。CharacterStreamの場合は、Readerオブジェクト(java.io.Readerまたはいくつかのサブクラスのインスタンス)を作成します。
データ型に応じたSQLJストリーム・クラスのインスタンスを作成するには、データ・オブジェクトと長さをコンストラクタに渡します。
このSQLJのストリーム・インスタンスは、SQLJ実行文のSQL操作用のホスト変数として使用します。
ストリームを終了します。
|
注意: ストリームを使用した後に終了することは必須ではありませんが、終了することをお薦めします。 |
ファイルからのLONGまたはLONG RAWの更新
ここでは、FileオブジェクトからCharacterStreamオブジェクトまたはBinaryStreamオブジェクトを作成し、そのオブジェクトをデータベースの更新に使用する方法を示します。この項の最後にあるコード例では、LONG列にCharacterStreamを使用しています。
データベース列をファイルから更新するときは、長さを特定する必要があります。この作業を行うには、入力ストリームを作成する前に、java.io.Fileオブジェクトを作成します。
次の手順で、ファイルからデータベースを更新します。
ファイルからjava.io.Fileオブジェクトを作成します。このためには、Fileクラスのコンストラクタへのファイル・パス名を指定してください。
Fileオブジェクトのlength()メソッドで、データの長さを特定します。このメソッドから戻される値はlong値なので、この値をSQLJストリーム・クラスのコンストラクタに入力するには、int値にキャストする必要があります。
|
注意: このキャストを行う前に、long値がint変数に収まることを確認してください。クラスjava.lang.Integerの静的定数MAX_VALUEは、Javaの最大許容値intを示します。 |
文字データの場合は、Fileオブジェクトからjava.io.FileReaderオブジェクトを作成します。このためには、FileオブジェクトをFileReaderコンストラクタに渡してください。
バイナリ・データの場合は、Fileオブジェクトからjava.io.FileInputStreamオブジェクトを作成します。このためには、FileオブジェクトをFileInputStreamコンストラクタに渡してください。
該当するSQLJストリーム・オブジェクトを作成します。テキスト・ファイルの場合はCharacterStreamオブジェクト、バイナリ・ファイルの場合はBinaryStreamオブジェクトを作成します。FileReaderまたはFileInputStreamの該当するオブジェクトとデータ長(int値)をSQLJのストリーム・クラスのコンストラクタに渡します。
SQLJのストリーム・オブジェクトは、SQLJ実行文中でSQL操作用のホスト変数として使用します
次に、ファイルからデータベースにLONGデータを書き込む例を示します。ここでは、/private/mydir/myfile.htmlにあるHTMLファイルの内容をfiletableデータベース表のLONG列chardataに挿入します。
import java.io.*;
import sqlj.runtime.*;
...
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();
...
バイト配列からのLONG RAWの更新
ここでは、バイト配列からBinaryStreamオブジェクトを作成し、そのオブジェクトをデータベースの更新に使用する方法を示します。
バイト配列からデータベースを更新する場合は、あらかじめデータの長さを特定する必要があります。配列の場合は、ファイルの場合より簡単です。Javaではどの配列も、データ長を戻す機能を備えているためです。
次の手順で、バイト配列からデータベースを更新します。
配列のlength機能を使用して、データの長さを特定します。この機能により、int値が戻されます。SQLJストリーム・クラスのすべてのコンストラクタがこの値を必要とします。
配列からjava.io.ByteArrayInputStreamオブジェクトを作成します。このためには、バイト配列をByteArrayInputStreamコンストラクタに渡してください。
BinaryStreamオブジェクトを作成します。ByteArrayInputStreamオブジェクトとデータ長(int値)をBinaryStreamクラスのコンストラクタに渡してください。
コンストラクタのシグネチャは、次のように指定します。
BinaryStream (InputStream in, int length)
このシグネチャには、java.io.InputStreamクラスまたは任意のサブクラス(ByteArrayInputStreamなど)のインスタンスを指定してください。
SQLJのストリーム・オブジェクトは、SQLJ実行文中でSQL操作用のホスト変数として使用します
次に、バイト配列からLONG RAWデータをデータベースに書き込む例を示します。ここでは、バイト配列bytearray[]の内容をBINTABLEデータベース表にあるLONG RAW型の列BINDATAに挿入するとします。
import java.io.*; import sqlj.runtime.*; ... 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(); ...
|
注意: この例に示したように、ストリームは必ずしも必要ではありません。かわりに、バイト配列から直接データベースを更新することも可能です。 |
SQLJストリーム・クラスでも、データを取り出せます。ただし、ストリームの使用時は、一部のデータベース製品で注意が必要です。ストリームでのロング・データの読取り/書込みにOracle Database 11g とOracle JDBCドライバを使用する場合は、ストリーム・データへのアクセス方法およびデータの処理方法に注意する必要があります。
Oracle JDBCドライバはイテレータ行からデータにアクセスするため、通信パイプからストリーム項目をフラッシュした後で次のデータ項目にアクセスするようになっています。イテレータ行の処理時にストリーム・データがローカル・ストリームに書き込まれる場合でも、JDBCドライバが次のデータ項目にアクセスする前にローカル・ストリームからデータを読み取らないと、ストリーム・データが失われます。このようなストリーム処理は、ストリームの特性である大型化と長さが不明な点を考慮したものです。
したがって、Oracle JDBCドライバでストリーム項目にアクセスし、ローカル・ストリーム変数への書込みが完了した時点で、このローカル・ストリームの読取りと処理を行い、その後で、イテレータから別のアクセスを行う必要があります。
特に、位置イテレータの場合は必須のFETCH INTO構文があるため、注意が必要です。各回のフェッチでは、すべての列の読取りが完了した後で、処理が開始されます。したがって、ストリーム項目は1つのみ、つまり最後にアクセスした項目のみとなります。
次に、注意点をまとめます。
位置イテレータを使用するときは、一度に1つのストリーム列、つまり最後の列のみを操作できます。イテレータの各行をフェッチし、ストリーム項目をプロセスのローカル入力ストリーム変数に書き込むたびに、ただちにローカル・ストリーム変数を読み込み、処理する必要があります。その後、イテレータの次の行に進みます。
名前付きイテレータを使用すると、複数のストリーム列を操作できます。ただし、各イテレータ行を処理するときに、ストリーム・フィールドにアクセスし、データをプロセスのローカル・ストリーム変数に書き込むたびに、ただちにローカル・ストリームを読み込み、処理する必要があります。その後、イテレータの他の部分を読み込みます。
また、名前付きイテレータの各行を処理するときは、イテレータ設定時の問合せでデータベース列を選択したときと同じ順番で、列のアクセッサ・メソッドをコールする必要があります。前述のように、問合せ後の通信パイプにストリーム・データが残っているためです。列へのアクセス順が異なると、ストリーム・データがスキップされ、他の列へのアクセス時に失われることがあります。
|
注意:
|
データをストリームとして取り出すとき、標準SQLJでは、名前付きまたは位置イテレータのSQLJストリーム型列へのデータの読込みができます。
ここでは、位置イテレータまたは名前付きイテレータを使用してデータをSQLJストリームに取り出す基本的な手順を示します。前の項で述べた注意事項に留意してください。
位置イテレータのSQLJストリーム列の使用方法
次の手順で、位置イテレータを使用して、データをSQLJストリームに取り出します。
位置イテレータ・クラスを宣言します。最終列を該当するSQLJストリーム型として指定します。
イテレータ型のローカル変数を宣言します。
該当するSQLJストリーム型のローカル変数を宣言します。これをホスト変数として、イテレータのSQLJストリーム列の各行からデータを取得します。
問合せを実行して、手順2で宣言したイテレータにデータを設定します。
通常どおり、イテレータを処理します。FETCH INTO文のINTOリスト内のホスト変数は、位置イテレータの列順に記述する必要があるので、ローカル入力ストリーム変数をリストの最後のホスト変数にします。
イテレータの処理ループでは、各イテレータ行にアクセスした時点でローカル入力ストリームの読取りと処理を行い、必要に応じてストリーム・データの格納や出力を行います。
イテレータ処理ループが一巡するたびに、ローカル入力ストリームを終了します。
イテレータを終了します。
名前付きイテレータのSQLJストリーム列の使用方法
次の手順で、名前付きイテレータを使用して、データを1つ以上のSQLJストリームに取り出します。
該当するSQLJストリーム型の列を1つ以上指定して、名前付きイテレータ・クラスを宣言します。
イテレータ型のローカル変数を宣言します。
イテレータの各SQLJストリーム列に対して、入力ストリーム型またはリーダー型のローカル変数を宣言します。ストリーム列のアクセッサ・メソッドからデータを受け取るときに、この変数を使用します。これらのローカル・ストリーム変数は、SQLJのストリーム型にする必要はありません。標準のjava.io.InputStreamまたはjava.io.Readerの該当する型でもかまいません。
|
注意: ローカル・ストリーム変数は、SQLJのストリーム型にする必要はありません。イテレータ列をSQLJのストリーム型にすると、データの型も正しく変換されます。 |
問合せを実行して、手順2で宣言したイテレータにデータを設定します。
通常どおり、イテレータを処理します。イテレータの各行の処理では、各ストリーム列のアクセッサ・メソッドがストリーム・データを戻すたびに、手順3で宣言したローカル入力ストリーム変数にデータが書き込まれます。
ストリーム・データの欠損を防ぐには、手順4の問合せで列を選択したときと同じ順番で、列のアクセッサ・メソッドをコールする必要があります。
イテレータの処理ループで、ストリーム列のアクセッサ・メソッドをコールし、データをローカル入力ストリーム変数に書き込んでから、ただちにローカル入力ストリームを読み取って処理し、ストリーム・データを格納または出力します。
イテレータ処理ループが一巡するたびに、ローカル入力ストリームを終了します。
イテレータを終了します。
|
注意:
|
名前付きまたは位置イテレータのSQLJストリーム列を処理するときは、ストリーム・データを受け取るローカル・ストリーム変数をSQLJのストリーム型または標準のjava.io.InputStreamまたはjava.io.Readerのいずれか該当する型にできます。いずれの場合も、入力データ・オブジェクトの標準のメソッドを使用できます。
ローカル・ストリーム変数をSQLJのストリーム型、つまりBinaryStreamまたはCharacterStreamにすると、データをSQLJのストリーム・オブジェクトから直接読み取ることも、基になるInputStreamオブジェクトまたはReaderオブジェクトを取得し、このオブジェクトからデータを読み取ることも可能です。
|
注意: どちらの方法をとるかは、好みの問題です。最初の方法の方が簡単です。ただし、2番目の方法は、データへのアクセスがより直接的で効率的です。 |
バイナリ・ストリームのメソッド
BinaryStreamクラスは、sqlj.runtime.StreamWrapperクラスのサブクラスです。StreamWrapperクラスには、次の主なメソッドがあります。
InputStream getInputStream(): このメソッドを使用して、基になるjava.io.InputStreamオブジェクトを取得することもできます。SQLJのストリーム・オブジェクトは、このメソッドを使用せずに、直接処理できます。
void setLength(int length): SQLJストリーム・オブジェクトのlength属性を設定できます。ストリーム・オブジェクトの作成時に設定したlengthの値をそのまま使用する場合は、このメソッドを使用する必要はありません。
SQLJストリームをデータベースに送信する場合は、あらかじめlength属性の値を設定する必要があります。
int getLength(): SQLJストリームのlength属性の値を戻します。ストリーム・オブジェクトのコンストラクタまたはsetLength()メソッドで明示的にデータ長を設定すると、この属性値が有効になります。データをストリームに取り出すときは、length属性が自動的に設定されません。
sqlj.runtime.StreamWrapperクラスはjava.io.FilterInputStreamクラスのサブクラスです。このjava.io.FilterInputStreamはjava.io.InputStreamクラスのサブクラスです。InputStreamクラスの次の主要メソッドは、SQLJのBinaryStreamクラスでもサポートされています。
int read(): 入力ストリームから次のバイト・データを読み取ります。このバイト・データはint値(0から255)で戻されます。ストリームの終端に達した場合の戻り値は、-1です。次のいずれかの状態になるまで、このメソッドはプログラムを実行しません。
入力データが用意されたとき
ストリームの終端に達したとき
例外がスローされたとき
int read (byte b[]): 入力ストリームから最大b.lengthバイトのデータを読み取り、指定されたb[]バイト配列に書き込みます。戻り値として、読み取ったバイト数を示すint値が戻されます。ストリームの終端に達した場合は、-1が戻されます。入力が用意されるまで、このメソッドはプログラムを実行しません。
int read (byte b[], int off, int len): 入力ストリームのオフセットoffで指定されたバイト位置から最大lenバイトのデータを読み取り、指定されたb[]バイト配列に書き込みます。戻り値として、読み取ったバイト数を示すint値が戻されます。ストリームの終端に達した場合は、-1が戻されます。入力が用意されるまで、このメソッドはブロックします。
long skip (long n): 入力ストリームのnバイトのデータをスキップし、破棄します。実際にスキップするバイト数が、指定値より少ない場合もあります。実際にスキップしたバイト数は、long値で戻されます。
void close(): ストリームを終了し、関連リソースを解放します。
文字ストリームのメソッド
CharacterStreamクラスには、次の主なメソッドがあります。
Reader getReader(): このメソッドを使用して、基になるjava.io.Readerオブジェクトを取得することもできます。SQLJのストリーム・オブジェクトは、このメソッドを使用せずに、直接処理できます。
void setLength(int length): このメソッドを使用して、ストリーム・オブジェクトの長さを設定できます。
int getLength(): このメソッドを使用して、ストリーム・オブジェクトの長さを取得できます。
sqlj.runtime.CharacterStreamクラスはjava.io.FilterReaderクラスのサブクラスです。このjava.io.FilterReaderはjava.io.Readerクラスのサブクラスです。Readerクラスの次の主要メソッドは、SQLJのCharacterStreamクラスでもサポートされています。
int read(): リーダーから次の文字データを読み取ります。このデータはint値(0から65535)で戻されます。データの終端に達した場合の戻り値は、-1です。次のいずれかの状態になるまで、このメソッドはプログラムを実行しません。
入力データが用意されたとき
データの終端に達したとき
例外がスローされたとき
int read (char cbuf[]): 文字を配列に読み込み、データを指定されたcbuf[] char配列に書き込みます。戻り値として、読み取った文字数を示すint値が戻されます。データの終端に達した場合は、-1が戻されます。入力が用意されるまで、このメソッドはプログラムを実行しません。
int read (char cbuf[], int off, int len): 入力のオフセットoffで指定された文字位置から最大len文字のデータを読み取り、指定されたchar[] char配列に書き込みます。戻り値として、読み取った文字数を示すint値が戻されます。データの終端に達した場合は、-1が戻されます。入力が用意されるまで、このメソッドはブロックします。
long skip (long n): 入力のn文字のデータをスキップし、破棄します。実際にスキップする文字数が、指定値より少ない場合もあります。実際にスキップした文字数は、long値で戻されます。
void close(): ストリームを終了し、関連リソースを解放します。
ここでは、ストリーム・データを取り出す例を示します。
例5-1では、SELECT文でLONG列からデータを選択し、名前付きイテレータのSQLJ CharacterStream列に設定します。
例5-2では、SELECT文でLONG RAW列からデータを選択し、位置イテレータのSQLJ BinaryStream列に設定します。
例5-1: 名前付きイテレータのCharacterStream列へのLONGデータの格納
この例では、データベースのLONG列からデータを選択し、名前付きイテレータのSQLJ CharacterStream列に格納します。
表FILETABLEのVARCHAR2型の列FILENAMEにファイル名が格納されており、LONG型の列FILECONTENTSにファイルの内容が文字形式で格納されているものとします。このコードを次に示します。
import sqlj.runtime.*;
import java.io.*;
...
#sql iterator MyNamedIter (String filename, CharacterStream filecontents);
...
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);
}
}
すでに述べたように、入力パラメータとして標準java.io.Readerをとるメソッドには、SQLJ文字ストリームを渡すことが可能です。
例5-2: 位置イテレータのBinaryStream列へのLONG RAWの格納
この例では、LONG RAW列からデータを選択し、位置イテレータのSQLJ BinaryStream列に移入します。
前の項で説明したように、位置イテレータには1つの入力ストリームのみ可能で、必ず最終列の必要があります。表BINTABLEにはNUMBER型の列IDENTIFIERとLONG RAW型の列BINDATAがあり、識別子に該当するバイナリ・データがBINDATA列に格納されているものとします。このコードを次に示します。
import sqlj.runtime.*;
...
#sql iterator MyPosIter (int, BinaryStream);
...
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();
...
前述のように、標準SQLJでは、sqlj.runtimeパッケージのBinaryStreamおよびCharacterStreamクラスを使用して、ストリーム・データを取り出し、イテレータ列に格納できます。
また、Oracle SQLJ実装では、Oracle9i 以上のデータベース、Oracle JDBCドライバ、Oracle固有コード生成またはOracleカスタマイザ、およびOracle SQLJランタイムを使用すると、SQLJのストリーム型を次のように使用できます。
ストアド・プロシージャまたはストアド・ファンクションのコールからのOUTまたはINOUTホスト変数として
ストアド・ファンクションのコールの戻り値として
BinaryStreamおよびCharacterStream型は、ストアド・プロシージャまたはストアド・ファンクションのOUTまたはINOUTパラメータの代入型として使用できます。
次の表定義を想定します。
CREATE TABLE streamexample (name VARCHAR2 (256), data LONG);
INSERT INTO streamexample (data, name)
VALUES
('0000000000111111111112222222222333333333344444444445555555555',
'StreamExample');
また、次のストアド・プロシージャ定義を想定します。この定義には、STREAMEXAMPLE表が使用されています。
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;
次のサンプル・コードでは、out_longdataストアド・プロシージャをコールして、ロング・データを読み取ります。
import sqlj.runtime.*;
...
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();
...
|
注意: ストリームを必ずしも終了する必要はありませんが、終了することをお薦めします。 |
BinaryStream型およびCharacterStream型は、ストアド・ファンクションの戻り値の代入型として使用できます。
前に示したストアド・プロシージャ例と同じSTREAMEXAMPLE表定義を想定します。また、次のストアド・ファンクション定義を想定します。この定義には、STREAMEXAMPLE表が使用されています。
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;
次のサンプル・コードでは、get_longdataストアド・ファンクションをコールして、ロング・データを読み取ります。
import sqlj.runtime.*;
...
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実装では、次に示したようにJDBC 2.0のデータ型とOracle固有のデータ型に対して拡張機能が用意されています。
JDBC 2.0ラージ・オブジェクト(LOB)型(BLOBおよびCLOB)
Oracle BFILE型
Oracle ROWID型
Oracle REF CURSOR型
その他のOracle Database 11g データ型(NUMBERやRAWなど)
ここであげたデータ型は、oracle.sqlパッケージ中のクラスでサポートされています。LOBとバイナリ・ファイル(BFILE)は多くの点で同様に処理されるため、まとめて説明します。Oracle SQLJ実装では、標準BigDecimal JDBC型に対するサポートも強化されています。
ユーザー定義のSQLオブジェクト、オブジェクト参照およびコレクションについては、JDBC 2.0機能もサポートされています。
コード中にOracle拡張型を使用する場合は、次の要件が伴うので注意してください。
Oracle JDBCドライバを使用する必要があります。
Oracle固有コード生成を使用するか、またはISOコード生成の場合は必要に応じてプロファイルをカスタマイズします。カスタマイザはデフォルトのカスタマイザoracle.sqlj.runtime.util.OraCustomizerをお薦めします。
アプリケーションの実行時にOracle SQLJランタイムを使用します。
Oracle SQLJランタイムとOracle JDBCドライバは、Oracle拡張型をコード中に使用しない場合であっても、Oracleカスタマイザを使用する場合は常に必要です。
Oracle固有のセマンティクス・チェックを行うには、適切なチェッカを使用する必要があります。デフォルトのチェッカoracle.sqlj.checker.OracleCheckerは、フロントエンドとして機能し、環境に応じたチェッカを起動します。 JDBCドライバを使用すると、Oracle固有のチェッカが起動されます。
ここでは、次の項目について説明します。
oracle.sqlパッケージは、SQLJユーザーにもJDBCユーザーにも重要です。このパッケージ内のクラスによって、Oracle Database 11g のすべてのデータ型(oracle.sql.ROWID、oracle.sql.CLOB、oracle.sql.NUMBERなど)がサポートされます。oracle.sqlクラスは、SQLロー・データ用のラッパーであり、該当のJava形式へのマッピングと変換メソッドを提供します。oracle.sql.*オブジェクトは、対応するSQLデータのバイナリ表現をバイト配列の形式で保持しています。各oracle.sql.*データ型クラスは、oracle.sql.Datumクラスのサブクラスです。
Oracle固有のセマンティクス・チェックを行うには、適切なチェッカを使用する必要があります。デフォルトのチェッカoracle.sqlj.checker.OracleCheckerは、フロントエンドとして機能し、環境に応じたチェッカを起動します。 JDBCドライバを使用すると、Oracle固有のチェッカが起動されます。
Oracle SQLJおよびJDBC実装では、JDBC 2.0 LOBデータ型がサポートされています。このサポート内容は、Oracle固有のBFILE型(データベースの外部に格納されている読取り専用バイナリ・ファイル)とほとんど同じです。これらのデータ型は、次のクラスでサポートされています。
oracle.sql.BLOB
oracle.sql.CLOB
oracle.sql.BFILE
これらのクラスは、Oracle固有のSQLJアプリケーションで次のように使用できます。
SQLJ実行文およびINTOリスト中のIN、OUTまたはINOUTホスト変数として
ストアド・ファンクション・コールの戻り値として
イテレータ宣言での列型として
|
参照: LOBとBFILEの詳細およびサポートされているストリームAPIの使用方法は、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。 |
LOBの操作には、BLOBおよびCLOBクラスで定義したメソッドを使用することをお薦めします。DBMS_LOB PL/SQLパッケージで定義したプロシージャやファンクションも使用できます。このパッケージで定義されているプロシージャとファンクションは、SQLJプログラムからコールできます。
BFILEの操作には、BFILEクラスで定義したメソッドを使用することをお薦めします。DBMS_LOBパッケージのファイル処理ルーチンも使用できます。
Javaアプリケーションでは、BLOB、CLOBおよびBFILEクラスのメソッドの方がDBMS_LOBパッケージより便利です。実行時間が短縮される場合もあります。
読取りまたは書込み対象のチャンクの型は、操作対象のLOBの種類によります。たとえば、キャラクタ・ラージ・オブジェクト(CLOB)の内容は文字データです。したがって、データ・チャンクをJava文字列で保持します。バイナリ・ラージ・オブジェクト(BLOB)の内容はバイナリ・データです。したがって、チャンク・データをJavaバイト配列で保持します。
|
注意: DBMS_LOBパッケージでは、サーバーへのラウンドトリップが必要になります。BLOB、CLOBおよびBFILEクラスのメソッドも、サーバーへのラウンドトリップが必要になることがあります。 |
BFILEでBFILEクラスを使用した場合とDBMS_LOB機能を使用した場合の比較
例5-3および例5-4では、BFILEでoracle.sqlのメソッドを使用した場合とDBMS_LOBパッケージを使用した場合を比較します。
例5-3: BFILEでのoracle.sql.BFILEファイル処理メソッドの使用
この例では、oracle.sql.BFILEクラスのファイル処理メソッドでBFILEを操作します。
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;
}
BFILEのgetDirAlias()およびgetName()メソッドは、フルパスとファイル名を取得するためのメソッドです。openFile()メソッドは、ファイルをオープンするためのメソッドです。操作するBFILEは、あらかじめオープンしておく必要があります。
例5-4: BFILEでのDBMS_LOBファイル処理ルーチンの使用
この例では、DBMS_LOBパッケージのファイル処理ルーチンを使用してBFILEを操作します。
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;
}
openFile()メソッドは、ファイル・オブジェクトの名前を出力し、その後で、そのファイルのオープンしているバージョンを戻すためのメソッドです。BFILEを操作するには、あらかじめDBMS_LOB.FILEOPENまたはBFILEクラスの該当メソッドをコールし、ファイルをオープンにしておく必要があります。
LOBでBLOBおよびCLOBクラスを使用した場合とDBMS_LOB機能を使用した場合の比較
例5-5および例5-6では、BLOBでoracle.sqlメソッドを使用した場合とDBMS_LOBパッケージを使用した場合を比較します。例5-7および例5-8では、CLOBでoracle.sqlメソッドを使用した場合とDBMS_LOBパッケージを使用した場合を比較します。
例5-5: CLOBに対するoracle.sql.CLOB読取りメソッドの使用
oracle.sql.CLOBクラスのメソッドを使用して、CLOBからデータを読み取ります。
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);
}
}
このメソッドには、CLOBからJava文字列を10文字ずつ読み取って戻すループがあります。CLOB全体を読み取るまで、ループが繰り返されます。
例5-6: CLOBでのDBMS_LOB読取りルーチンの使用
DBMS_LOBパッケージのルーチンを使用して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);
}
}
このメソッドはCLOBの内容を10文字ずつ読み取ります。チャンクのホスト変数の型は、Stringです。
例5-7: BLOBでのoracle.sql.BLOB書込みルーチンの使用
oracle.sql.BLOBクラスのメソッドを使用して、BLOBへデータを書き込みます。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 = 0; i < blobLen; i+= chunkLen) {
if (blobLen < chunkLen) chunkLen = blobLen;
chunk[0] = (byte)(i+1);
chunkLen = blob.putBytes(i, chunk);
}
}
指定されたBLOB長に達するまで、このメソッドはBLOBに10バイトずつ書き込むループを繰り返します。
例5-8: BLOBでのDBMS_LOB書込みルーチンの使用
この例では、DBMS_LOBパッケージのルーチンを使用して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) };
}
}
このメソッドは、BLOBに10バイトずつ書き込みます。チャンクのホスト変数はbyte[]型です。
BLOB、CLOBおよびBFILE型のホスト変数は、ストアド・ファンクション・コールの結果に代入できます。次に示した例は、CLOBを対象としていますが、BLOBおよびBFILEのコードにも同じ機能があります。
最初に、次のファンクション定義を想定します。
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;
次の例では、longer_clobファンクションからの戻り値の代入型としてCLOBを使用します。
void readFromLongest(CLOB c1, CLOB c2) throws SQLException
{
CLOB longest;
#sql longest = { VALUES(longer_clob(:c1, :c2)) };
readFromClob(longest);
}
readFromLongest()メソッドは、前に定義したreadFromClob()メソッドを使用して、渡されたCLOBの長い方の内容を出力します。
LOBおよびBFILEホスト変数とSELECT INTOターゲット
BLOB、CLOBおよびBFILE型のホスト変数は、SELECT INTO実行文のINTOリストに記述できます。次に示した例は、BLOBとCLOBを対象としていますが、BFILEのコードにも同じ機能があります
次の表定義を想定します。
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');
次の例では、BLOBとCLOBをホスト変数として使用し、定義した表のデータをSELECT INTO文を使用して受け取ります。
...
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) };
...
この例では、BASIC_LOB_TABLEの先頭行からBLOBを取り出し、2番目の行からCLOBを取り出します。その後で、前の操作で選択したBLOBとCLOBを使用して、3番目の行を表に挿入します。
BLOB、CLOBおよびBFILE型は、SQLJの位置および名前付きイテレータの列型として使用できます。これらのイテレータには、該当する実行可能なSQLJ操作の結果を格納できます。
次に、宣言の例を示します。
#sql iterator NamedLOBIter(CLOB c); #sql iterator PositionedLOBIter(BLOB); #sql iterator NamedFILEIter(BFILE bf);
LOBおよびBFILE型ホスト変数と名前付きイテレータの結果
次の例では、前の例で定義したreadFromLongest()メソッドとBASIC_LOB_TABLE表を使用し、名前付きイテレータでCLOBを使用します。BLOBおよびBFILEのコードも、同様の記述になります。
#sql iterator NamedLOBIter(CLOB c);
...
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);
...
この例では、イテレータを使用して、BASIC_LOB_TABLEの最初の2行からCLOBを2つ選択します。次にreadFromLongest()メソッドを使用して、2つのうちの大きい方を出力します。
LOBおよびBFILE型ホスト変数と、位置イテレータのFETCH INTOターゲット
BLOB、CLOBおよびBFILE型のホスト変数は、位置イテレータで使用できます。イテレータの該当列の属性が同じ型の場合は、対応付けられているFETCH INTO文のINTOリストに、これらのホスト変数を記述できます。
次の例では、前の例で定義したwriteToBlob()メソッドとBASIC_LOB_TABLE表を使用します。CLOBおよびBFILEのコードも、同様の記述になります。
#sql iterator PositionedLOBIter(BLOB);
...
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();
...
この例では、BASIC_LOB_TABLE内の各BLOBに対して、writeToBlob()をコールします。各行から、512バイトずつデータが書き込まれます。
Oracle固有のROWID型は、データベース表の各行の一意のアドレスを格納するための型です。oracle.sql.ROWIDクラスは、ROWID情報をラッピングします。ROWID型の変数をバインドおよび定義するときに、このクラスを使用します。
oracle.sql.ROWID型の変数は、Oracle Database 11g に接続するSQLJアプリケーションで次のように使用できます。
SQLJ実行文およびINTOリスト中のIN、OUTまたはINOUTホスト変数として
ストアド・ファンクション・コールの戻り値として
イテレータ宣言での列型として
ROWIDを使用したイテレータ宣言
oracle.sql.ROWID型は、次の宣言のように、SQLJの位置および名前付きイテレータの列型として使用できます。
#sql iterator NamedRowidIter (String ename, ROWID rowid); #sql iterator PositionedRowidIter (String, ROWID);
ROWIDホスト変数と名前付きイテレータのSELECT結果
ROWIDオブジェクトは、SQLJ実行文でIN、OUTおよびINOUTパラメータとして使用できます。また、イテレータのROWID型の列を設定できます。次のコード例では、前の例で示した宣言を使用します。
#sql iterator NamedRowidIter (String ename, ROWID rowid);
...
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();
...
この例では、ROWIDに従って、従業員Chuck Turnerの給与が$500増えます。
次のファンクションについて考えてみます。
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;
このストアド・ファンクションの場合は、このファンクションの戻り値の代入型として、ROWIDオブジェクトが次のように使用されます。
ROWID rowid;
#sql rowid = { values(get_rowid('AMY FEINER')) };
#sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid };
この例では、ROWIDに従って、従業員Amy Feinerの給与が$500増えます。
ROWID型のホスト変数は、SELECT INTO文のINTOリストに記述できます。
ROWID rowid;
#sql { SELECT rowid INTO :rowid FROM emp WHERE ename='CHUCK TURNER' };
#sql { UPDATE emp SET sal = sal + 500 WHERE rowid = :rowid };
この例では、ROWIDに基づき、従業員Chuck Turnerの給与が$500増えます。
ROWIDホスト変数と位置イテレータのFETCH INTOターゲット
ROWID型のホスト変数は、FETCH INTO文のINTOリストに記述できます(イテレータ内の該当列の属性が同じ型の場合)。
#sql iterator PositionedRowidIter (String, ROWID);
...
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();
...
この例は、前述の名前付きイテレータの例に似ていますが、位置イテレータの必須FETCH INTO構文を使用します。
Oracle Database 11g リリース1(11.1)では、SQLJで位置指定更新および位置指定削除の操作がサポートされています。位置指定更新または位置指定削除の操作は、イテレータを使用して実行できます。位置指定更新または位置指定削除に使用されるイテレータは、sqlj.runtime.ForUpdateインタフェースを実装している必要があります。名前付きイテレータ、位置イテレータまたはスクロール可能なイテレータを使用できます。
次のコードは、位置指定更新を示しています。
...
#sql iterator iter implements sqlj.runtime.ForUpdate(String str)
...
#sql iter = {SELECT ename FROM emp WHERE dpetno=10};
...
while(iter.next())
{
#sql {UPDATE emp SET sal=sal+5000 WHERE CURRENT OF :iter};
}
...
前述のコードでは、イテレータiterが作成され、emp表を更新する際に使用されます。
同様に位置指定削除を実行できます。次にその例を示します。
...
#sql {DELETE FROM emp WHERE CURRENT OF :iter}
...
前述の例で、iterは、位置指定削除を実行する際に使用されるイテレータです。
WHERE CURRENT OF句で使用可能なイテレータには、次の制限事項があります。
イテレータへの移入に使用される問合せは、複数の表に対して実行しないでください。
イテレータでREF CURSORを戻すPL/SQLプロシージャは使用できません。
結果セットから移入されているイテレータは使用できません。つまり、次の文を使用して移入されたイテレータです。ここでrsは結果セットです。
#sql iter = {cast :rs}
for_updateオプション
変換時にfor_updateオプションを設定すると、SELECT文にFOR UPDATEが追加されます。次に、SELECT文はForUpdateイテレータに結果を次のように戻します。
% sqlj –for_update abc.sqlj
/* abc.sqlj */
#sql iterator SalByName (double sal, String ename) implements sqlj.runtime.ForUpdate;
public class abc {
…..
void func1()
{
SalByName salbn;
#sql salbn = {select sal, ename from emp };
}
…..
}
ここで、SELECT文にFOR UPDATEが追加され、次のようにForUpdateイテレータsalbnが戻されます。
……… String theSqlTS = ÒSELECT rowid sjT_rowid,ename, sal FROM emp WHERE ename = :1 FOR UPDATE"; ………
表5-3に、for_updateオプションに使用可能な値と前述の例の対応するSQL文を示しています。
表5-3 for_updateオプションの値と対応するSQL文
| for_updateオプション | SQLJ文 | SQL文 |
|---|---|---|
|
none |
|
|
|
|
|
|
|
|
|
|
|
注意: アプリケーションの選択問合せにFOR UPDATEが含まれる場合、新しい変換オプションを使用すると、変換時のオンライン・チェック中に警告がスローされます。変換中にオフライン解析を選択すると、変換時にエラーは検出されません。 |
Oracle PL/SQLとOracle SQLJ実装では、データベースのカーソルを表すカーソル変数を使用できます。
カーソル変数は、機能的にはJDBCの結果セットに相当し、問合せ結果をカプセル化します。カーソル変数はREF CURSORとも呼ばれますが、REF CURSOR自体は型指定子であって、型名ではありません。指定子でなく、名前付きREF CURSOR型を指定する必要があります。次に、REF CURSORの型指定の例を示します。
TYPE EmpCurType IS REF CURSOR;
OracleのREF CURSOR型のパラメータは、ストアド・プロシージャとストアド・ファンクションで戻されます。REF CURSORパラメータを戻すには、PL/SQLを使用する必要があります。SQLのみを使用した場合は戻りません。PL/SQLのストアド・プロシージャやストアド・ファンクションでは、任意の名前付きREF CURSOR型の変数を宣言したり、SELECT文を実行したり、REF CURSOR変数に戻り値としての結果を代入したりできます。
|
参照: 『Oracle Database PL/SQL言語リファレンス』 |
Oracle SQLJ実装では、REF CURSOR型を任意のイテレータ・クラス型またはjava.sql.ResultSet型のイテレータ列またはホスト変数にマッピングできます。ただし、マッピングの対象となるホスト変数はOUT型のみです。次に、REF CURSOR型の用途をまとめます。
ストアド・ファンクションの戻り値に使用する結果式として
ストアド・プロシージャまたはストアド・ファンクションの出力パラメータの出力ホスト式として
INTOリスト中の出力ホスト式として
イテレータ列として
SQLのCURSOR演算子は、外部のSELECT文の内側にネストされているSELECTに対して使用できます。これによって、REF CURSORオブジェクトをイテレータ列またはイテレータのResultSet列に書き込むか、REF CURSORオブジェクトをイテレータ・ホスト変数またはINTOリストのResultSetホスト変数に書き込むことが可能になります。
|
注意:
|
無名ブロックからREF CURSOR型を取り出すサンプル・メソッドを次に示します。
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;
}
oracle.sqlのあらゆるクラスは、イテレータ列にも、入力、出力または入出力のホスト変数にも、標準Java型と同じように使用できます。たとえば、前述したクラスでは、oracle.sql.NUMBER、oracle.sql.CHAR、oracle.sql.RAWなどのクラスがサポートされています。
oracle.sql.*クラスはJava型の形式に変換する必要がないため、同等のJava型より効率および精度面で優れています。ただし、標準Javaプログラムで使用する場合、またはエンド・ユーザーで表示する場合は、データを標準Java型に変換する必要があります。
SQLJでは、java.math.BigDecimalを次の用途に使用できます。
SQLJ実行文中のホスト変数として
ストアド・ファンクション・コールの戻り値として
イテレータの列型として
標準SQLJで数値データおよび10進データの値をBigDecimalとして取得するには、JDBCのデフォルト・マッピングを使用する必要があります。
標準SQLJに対し、Oracle SQLJ実装では、Oracle9i 以上のデータベース、Oracle JDBCドライバ、Oracle固有コード生成またはOracleカスタマイザ、およびOracle SQLJランタイムを使用すると、数値から変換可能なデータ型をデフォルト以外の型にマッピングできます。変換可能な型としては、CHAR、VARCHAR2、LONGおよびNUMBERがあります。たとえば、CHAR列のデータは、BigDecimal変数への取込みができます。ただし、エラーを防ぐために、文字データの内容を数字のみにする必要があります。
|
注意: BigDecimalクラスは標準java.mathパッケージにあります。 |