この章では、Java Database Connectivity(JDBC)およびoracle.sql.*クラスを使用して、ラージ・オブジェクト(LOB)ロケータとデータおよびバイナリ・ファイル(BFILE)ロケータとデータにアクセスして、操作する方法を説明します。この章では、次の項目について説明します。
|
注意:
|
LOBは、領域を最適化し効率的にアクセスできるようにして格納されます。JDBCドライバは、非構造化バイナリ・データで使用するバイナリ・ラージ・オブジェクト(BLOB)、文字データで使用するキャラクタ・ラージ・オブジェクト(CLOB)および各国語の文字データで使用する各国語キャラクタ・ラージ・オブジェクト(NCLOB)の3種類のLOBをサポートします。BLOB、CLOBおよびNCLOBデータは、ロケータを使用してアクセスおよび参照します。ロケータはデータベース表内に格納され、表の外部にあるBLOB、CLOBまたはNCLOBデータを指します。
BFILEは、データベース表領域外のオペレーティング・システム・ファイルに格納されたラージ・バイナリ・データ・オブジェクトです。これらのファイルは、参照セマンティクスを利用します。また、これらのファイルはハード・ディスク、CD-ROM、PhotoCD、DVDなどの3次ストレージ・デバイスにも配置できます。BLOBやCLOB、NCLOBと同様、BFILEは、ロケータによりアクセスまたは参照されます。ロケータは、データベース表内に格納され、BFILEデータを指します。
LOBデータを使用するには、最初に、LOBロケータを取り出す必要があります。その後、LOBデータの読取りまたは書込み、データ操作が実行できます。
JDBCドライバは、BLOB、CLOB、NCLOBおよびBFILEに対応した、次のoracle.sql.*クラスをサポートします。
oracle.sql.BLOB
oracle.sql.CLOB
oracle.sql.NCLOB
oracle.sql.BFILE
oracle.sql.BLOBクラス、oracle.sql.CLOBクラスおよびoracle.sql.NCLOBクラスは、それぞれjava.sql.Blobインタフェース、java.sql.Clobインタフェースおよびjava.sql.NClobインタフェースを実装します。これに対し、BFILEはOracle拡張機能で、対応するjava.sqlインタフェースはありません。
これらのクラスのインスタンスは、これらのデータ型のロケータのみを含み、データは含みません。ロケータへのアクセス後に、データにアクセスをするために追加処理を行う必要があります。
|
注意: 新規LOBオブジェクトを作成する場合、oracle.jdbc.OracleConnectionインタフェースのファクトリ・メソッドを使用します。 |
この項では、LOBロケータを使用し、Oracle DatabaseのBLOB、CLOBおよびNCLOBに対して、データの読取りおよび書込みを行う方法を説明します。この項には、次の項目が含まれます。
データベースからLOBロケータを取り出すとき、またはデータベースにLOBロケータを渡すときに使用可能なgetterおよびsetterメソッドには、標準のメソッドとOracle固有のメソッドがあります。この項には、次の項目が含まれます。
BLOB、CLOBまたはNCLOBロケータを含む標準JDBC結果セットまたはコール可能文がある場合は、次の標準getterメソッドを使用して、ロケータにアクセスできます。標準のgetBlob、getClobおよびgetNClobメソッドを使用できます。これらのメソッドはそれぞれ、java.sql.Blob、ClobおよびNClobオブジェクトを戻します。
|
注意: この項で説明する標準およびOracle固有のgetterメソッドはすべて、入力としてint列索引またはString列名を取ります。 |
結果セットをOracleResultSetに取り出すか、またはコール可能文をOracleCallableStatementにキャストすると、次のようにOracle拡張機能を使用できます。
getBlob、getClobおよびgetNClobを使用できます。これらのメソッドはそれぞれ、java.sql.Blob、CLOBおよびNCLOBオブジェクトを戻します。
getOracleObjectメソッドを使用することもできます。このメソッドは、oracle.sql.Datumオブジェクトを戻し、出力を適切にキャストします。
例: 結果セットからのBLOB、CLOBおよびNCLOBロケータの取出し
BLOBロケータ用のblob_col、CLOBロケータ用のclob_colおよびNCLOBロケータ用のnclob_colという列を備えたlob_tableという表がデータベースにあるものとします。この例では、Statementオブジェクトstmtが作成済であるものとします。
まず、LOBロケータを標準結果セット内にSelectします。その後、LOBデータを適切なJavaクラス内に取り込みます。
// Select LOB locator into standard result set.
ResultSet rs =
stmt.executeQuery ("SELECT blob_col, clob_col, nclob_col FROM lob_table");
while (rs.next())
{
// Get LOB locators into Java container classes.
java.sql.Blob blob = (java.sql.Blob)rs.getObject(1);
java.sql.Clob clob = (java.sql.Clob)rs.getObject(2);
java.sql.NClob nclob = (java.sql.NClob)rs.getObject(3);
(...process...)
}
出力は、java.sql.Blob、java.sql.Clobおよびjava.sql.NClobにキャストされます。また、出力をoracle.sql.BLOB、oracle.sql.CLOBおよびoracle.sql.NCLOBにキャストして、oracle.sql.*クラスが提供する拡張機能を利用する方法もあります。たとえば、前述のコードを次のように書きなおして、LOBロケータを取り出すこともできます。
// Get LOB locators into Java container classes.
oracle.sql.BLOB blob = (BLOB)rs.getObject(1);
oracle.sql.CLOB clob = (CLOB)rs.getObject(2);
oracle.sql.NCLOB nclob = (NCLOB)rs.getObject(3);
(...process...)
例: コール可能文からのCLOBロケータの取出し
LOB取出し用のコール可能文は、結果セットのメソッドと同一です。
たとえば、CLOB出力パラメータを取る関数funcをコールするOracleCallableStatementインスタンスのocsがある場合は、次のようにコール可能文を設定します。
この例では、出力パラメータの型コードとして、OracleTypes.CLOBが登録されます。
OracleCallableStatement ocs =
(OracleCallableStatement)conn.prepareCall("{? = call func()}");
ocs.registerOutParameter(1, OracleTypes.CLOB);
ocs.execute();
oracle.sql.CLOB clob = ocs.getCLOB(1);
標準JDBCのプリコンパイルされたSQL文またはコール可能文がある場合は、標準setterメソッドを使用して、LOBロケータを渡せます。メソッドは次のように定義されます。
public void setBlob(int index, Blob value); public void setClob(int index, Clob value); public void setNClob(int index, NClob value);
Oracle固有のOraclePreparedStatementまたはOracleCallableStatementがある場合、次のようにOracle拡張機能を使用できます。
例: BLOBロケータのプリコンパイルされたSQL文への引渡し
OraclePreparedStatementオブジェクトopsと、my_blobという名前のBLOBがある場合は、次のようにBLOBをデータベースに書き込みます。
OraclePreparedStatement ops = (OraclePreparedStatement)conn.prepareStatement
("INSERT INTO blob_table VALUES(?)");
ops.setBLOB(1, my_blob);
ops.execute();
例: CLOBロケータのコール可能文への引渡し
OracleCallableStatementオブジェクトocsと、my_clobという名前のCLOBがある場合は、次のようにCLOBをストアド・プロシージャprocに入力します。
OracleCallableStatement ocs =
(OracleCallableStatement)conn.prepareCall("{call proc(?))}");
ocs.setClob(1, my_clob);
ocs.execute();
例: NCLOBロケータのコール可能文への引渡し
OracleCallableStatementオブジェクトocsと、my_nclobという名前のNCLOBがある場合は、次のようにNCLOBをストアド・プロシージャprocに入力します。
OracleCallableStatement ocs =
(OracleCallableStatement)conn.prepareCall("{call proc(?))}");
ocs.setNClob(1, my_nclob);
ocs.execute();
LOBロケータがあれば、JDBCメソッドを使用して、LOBデータの読取りおよび書込みができます。LOBデータは、Java配列またはストリームとしてインスタンス化されます。LONGおよびLONG RAWデータと異なり、接続されている間、LOBデータにはいつでもアクセスできます。
LOBデータの読取りおよび書込みを行う場合、java.sql.BLOB、java.sql.CLOBまたはjava.sql.NCLOBクラスのメソッドを適宜使用します。これらのクラスは、LOBから入力ストリーム内への読取り、出力ストリームからLOB内への書込み、LOBの長さの決定およびLOBのクローズなどの機能を提供します。
|
注意: LOBデータを書き込むには、アプリケーションでLOBオブジェクトの書込みロックを取得する必要があります。SELECT FOR UPDATEを使用することで取得できます。また、自動コミット・モードを無効化する必要があります。 |
LOBデータの読取りおよび書込みを実行する際、次のメソッドを使用できます。
BLOBから読み取るには、java.sql.BLOBオブジェクトのgetBinaryStreamメソッドを使用して、BLOB全体を入力ストリームとして取り出します。これにより、java.io.InputStreamオブジェクトが戻されます。
InputStreamオブジェクトの場合と同様に、オーバーロードしたreadメソッドの1つを使用してLOBデータを読み取り、完了時にcloseメソッドを使用します。
BLOBに書き込むには、java.sql.BLOBオブジェクトのsetBinaryStreamメソッドを使用して、BLOBを出力ストリームとして取り出します。このメソッドは、BLOBに書き戻されるjava.io.OutputStreamオブジェクトを戻します。
OutputStreamオブジェクトの場合と同様に、オーバーロードしたwriteメソッドの1つを使用して、LOBデータを更新し、完了時にcloseメソッドを使用します。
CLOBから読み取るには、java.sql.CLOBオブジェクトのgetAsciiStreamまたはgetCharacterStreamメソッドを使用して、CLOB全体を入力ストリームとして取り出します。getAsciiStreamメソッドは、java.io.InputStreamオブジェクトのASCII入力ストリームを戻します。getCharacterStreamメソッドは、java.io.ReaderオブジェクトのUnicode入力ストリームを戻します。
InputStreamまたはReaderオブジェクトの場合と同様、オーバーロードしたreadメソッドの1つを使用して、LOBデータを読み取り、完了時にcloseメソッドを使用します。
java.sql.CLOBオブジェクトのgetSubStringメソッドを使用すると、CLOBのサブセットをjava.lang.String型の文字列として取り出せます。
CLOBに書き込むには、java.sql.CLOBオブジェクトのsetAsciiStreamまたはsetCharacterStreamメソッドを使用して、CLOBを出力ストリームとして取り出してから、CLOBに書き戻します。setAsciiStreamメソッドは、java.io.OutputStreamオブジェクトのASCII出力ストリームを戻します。setCharacterStreamメソッドは、java.io.WriterオブジェクトのUnicode出力ストリームを戻します。
StreamまたはWriterオブジェクトの場合と同様、オーバーロードしたwriteメソッドの1つを使用して、LOBデータを更新し、完了時にflushおよびcloseメソッドを使用します。
NCLOBから読み取るには、java.sql.NCLOBオブジェクトのgetAsciiStreamまたはgetCharacterStreamメソッドを使用して、NCLOB全体を入力ストリームとして取り出します。getAsciiStreamメソッドは、java.io.InputStreamオブジェクトのASCII入力ストリームを戻します。getCharacterStreamメソッドは、java.io.ReaderオブジェクトのUnicode入力ストリームを戻します。
InputStreamまたはReaderオブジェクトの場合と同様、オーバーロードしたreadメソッドの1つを使用して、LOBデータを読み取り、完了時にcloseメソッドを使用します。
java.sql.NCLOBオブジェクトのgetSubStringメソッドを使用すると、NCLOBのサブセットをjava.lang.String型の文字列として取り出せます。
NCLOBに書き込むには、oracle.sql.NCLOBオブジェクトのsetAsciiStreamまたはsetCharacterStreamメソッドを使用して、NCLOBを出力ストリームとして取り出してから、NCLOBに書き戻します。setAsciiStreamメソッドは、java.io.OutputStreamオブジェクトのASCII出力ストリームを戻します。setCharacterStreamメソッドは、java.io.WriterオブジェクトのUnicode出力ストリームを戻します。
StreamまたはWriterオブジェクトの場合と同様、オーバーロードしたwriteメソッドの1つを使用して、LOBデータを更新し、完了時にflushおよびcloseメソッドを使用します。
例: BLOBデータの読取り
oracle.sql.BLOBクラスのgetBinaryStreamメソッドを使用して、BLOBデータを読み取ります。getBinaryStreamメソッドは、バイナリ・ストリームを介してBLOBデータにアクセスします。
次の例は、getBinaryStreamメソッドを使用してBLOBデータをバイト・ストリーム内に読み取り、次に、バイト・ストリームを読み取ってバイト配列に格納します。読み取ったバイト数も戻します。
// Read BLOB data from BLOB locator. InputStream byte_stream = my_blob.getBinaryStream(1L); byte [] byte_array = new byte [10]; int bytes_read = byte_stream.read(byte_array); ...
例: CLOBデータの読取り
次の例では、getCharacterStreamメソッドを使用して、CLOBデータをUnicode文字ストリーム内に読み取ります。次に、文字ストリームを読み取って文字配列に格納します。読み取った文字数も戻します。
// Read CLOB data from CLOB locator into Reader char stream. Reader char_stream = my_clob.getCharacterStream(1L); char [] char_array = new char [10]; int chars_read = char_stream.read (char_array, 0, 10); ...
例: NCLOBデータの読取り
次の例では、getCharacterStreamメソッドを使用して、NCLOBデータをUnicode文字ストリーム内に読み取ります。次に、文字ストリームを読み取って文字配列に格納します。読み取った文字数も戻します。
// Read NCLOB data from NCLOB locator into Reader char stream. Reader char_stream = my_nclob.getCharacterStream(1L); char [] char_array = new char [10]; int chars_read = char_stream.read (char_array, 0, 10); ...
次の例では、oracle.sql.NCLOBクラスのgetAsciiStreamメソッドを使用して、NCLOBデータをASCII文字ストリーム内に読み取ります。次に、ASCIIストリームを読み取ってバイト配列に格納します。読み取った文字数も戻します。
// Read NCLOB data from NCLOB locator into Input ASCII character stream Inputstream asciiChar_stream = my_nclob.getAsciiStream(1L); byte[] asciiChar_array = new byte[10]; int asciiChar_read = asciiChar_stream.read(asciiChar_array,0,10);
例: BLOBデータの書込み
oracle.sql.BLOBオブジェクトのsetBinaryOutputStreamメソッドを使用して、BLOBデータを書き込みます。
次の例では、データのベクトルをバイト配列内に読み取り、setBinaryOutputStreamメソッドを使用して、文字データの配列をBLOBに書き込みます。
java.io.OutputStream outstream;
// read data into a byte array
byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
// write the array of binary data to a Blob
outstream = ((BLOB)my_blob).setBinaryOutputStream(1L);
outstream.write(data);
...
例: CLOBデータの書込み
setCharacterStreamメソッドまたはsetAsciiStreamメソッドを使用して、データをCLOBに書き込みます。setCharacterStreamメソッドは、Unicode出力ストリームを戻します。setAsciiStreamメソッドは、ASCII出力ストリームを戻します。
次の例では、データのベクトルを文字配列内に読み取り、setCharacterStreamメソッドを使用して文字データの配列をCLOBに書き込みます。
java.io.Writer writer;
// read data into a character array
char[] data = {'0','1','2','3','4','5','6','7','8','9'};
// write the array of character data to a Clob
writer = ((CLOB)my_clob).setCharacterStream();
writer.write(data);
writer.flush();
writer.close();
...
次の例では、データのベクトルをバイト配列内に読み取り、setAsciiStreamメソッドを使用して、ASCIIデータの配列をCLOBに書き込みます。
java.io.OutputStream out;
// read data into a byte array
byte[] data = {'0','1','2','3','4','5','6','7','8','9'};
// write the array of ascii data to a CLOB
out = clob.setAsciiStream();
out.write(data);
out.flush();
out.close();
例: NCLOBデータの書込み
setCharacterStreamメソッドまたはsetAsciiStreamメソッドを使用して、データをNCLOBに書き込みます。setCharacterStreamメソッドは、Unicode出力ストリームを戻します。setAsciiStreamメソッドは、ASCII出力ストリームを戻します。
次の例では、データのベクトルを文字配列内に読み取り、setCharacterStreamメソッドを使用して文字データの配列をNCLOBに書き込みます。
java.io.Writer writer;
// read data into a character array
char[] data = {'0','1','2','3','4','5','6','7','8','9'};
// write the array of character data to an NClob
writer = ((NCLOB)my_nclob).setCharacterStream();
writer.write(data);
writer.flush();
writer.close();
...
次の例では、データのベクトルをバイト配列内に読み取り、setAsciiStreamメソッドを使用して、ASCIIデータの配列をNCLOBに書き込みます。
java.io.OutputStream out;
// read data into a byte array
byte[] data = {'0','1','2','3','4','5','6','7','8','9'};
// write the array of ascii data to a NClob
out = nclob.setAsciiStream();
out.write(data);
out.flush();
out.close();
表にBLOB列、CLOB列またはNCLOB列を作成し、データを移入するには、SQL文を使用します。
|
注意: Javaのnew文を使用して、アプリケーションで新規のBLOBロケータ、CLOBロケータまたはNCLOBロケータを作成することはできません。SQL操作でロケータを作成した後、そのロケータをアプリケーションで選択するか、oracle.jdbc.OracleConnectionインタフェースからファクトリ・メソッドを使用して選択する必要があります。 |
SQLのCREATE TABLE文を使用して表にBLOB、CLOBまたはNCLOB列を作成し、LOBにデータを移入します。これには、表内でのLOBエントリの作成、LOBロケータの取出し、およびデータのLOBへのコピーが含まれます。
新しい表でのBLOB、CLOBまたはNCLOB列の作成
新しい表にBLOB、CLOBまたはNCLOB列を作成するには、SQLのCREATE TABLE文を使用します。次の例では、新しい表にBLOB列を作成します。この例では、ConnectionオブジェクトconnおよびStatementオブジェクトstmtを作成してあるものとします。
String cmd = "CREATE TABLE my_blob_table (x VARCHAR2 (30), c BLOB)"; stmt.execute (cmd);
この例では、VARCHAR2列は1や2などの行番号を表し、BLOB列にはBLOBデータのロケータが格納されます。
新しい表でのBLOB、CLOBまたはNCLOB列へのデータ移入
この例では、ストリームからデータを読み取って、BLOB、CLOBまたはNCLOB列に移入する方法を示します。ConnectionオブジェクトconnおよびStatementオブジェクトstmtは、すでに作成してあるものとします。表my_blob_tableは、前述の項で作成した表です。
次の例では、john.gifファイルをBLOBに書き込みます。
まず、SQL文を使用してBLOBエントリを表の中に作成します。empty_blob関数を使用して、BLOBロケータを作成します。
stmt.execute ("INSERT INTO my_blob_table VALUES ('row1', empty_blob())");
表からBLOBロケータを取り出します。
BLOB blob; cmd = "SELECT * FROM my_blob_table WHERE X='row1' FOR UPDATE"; ResultSet rset = stmt.executeQuery(cmd); rset.next(); BLOB blob = ((OracleResultSet)rset).getBLOB(2);
|
注意: 自動コミット・モードを無効化する必要があります。 |
john.gifファイル用のファイル・ハンドラを宣言し、ファイルの長さを出力します。この値は、ファイル全体がBLOB内に読み取られたことを保証するために使用されます。次に、FileInputStreamオブジェクトを作成してファイルの内容を読み取り、OutputStreamオブジェクトを作成してBLOBをストリームとして取り出します。
File binaryFile = new File("john.gif");
System.out.println("john.gif length = " + binaryFile.length());
FileInputStream instream = new FileInputStream(binaryFile);
OutputStream outstream = blob.setBinaryStream(1L);
getBufferSizeをコールして、BLOBへの書込みに使用する理想的なバッファ・サイズを取り出し、bufferバイト配列を作成します。
int size = blob.getBufferSize(); byte[] buffer = new byte[size]; int length = -1;
readメソッドを使用してファイルをバイト配列buffer内に読み取り、次にwriteメソッドを使用してそれをBLOBに書き込みます。完了時に、入力および出力ストリームをクローズして、変更をコミットします。
while ((length = instream.read(buffer)) != -1) outstream.write(buffer, 0, length); instream.close(); outstream.close(); conn.commit();
データがBLOB、CLOBまたはNCLOB内に格納されると、データの操作が可能になります。
BLOB、CLOBまたはNCLOBロケータが表内に格納されると、ロケータが指すデータへのアクセスおよび操作が可能になります。データへのアクセスおよび操作を行うには、最初に、結果セットまたはコール可能文からロケータを選択する必要があります。
ロケータの選択後に、BLOB、CLOBまたはNCLOBデータを取り出せます。BLOB、CLOBまたはNCLOBデータの取出し後、任意の方法でデータを操作できます。
この例は、前の項の例からの継続です。SQL SELECT文を使用して、表my_blob_tableでBLOBロケータを選択し、結果セットに格納します。データ操作の結果として、BLOBの長さがバイト単位で出力されます。
// Select the blob - what we are really doing here
// is getting the blob locator into a result set
BLOB blob;
cmd = "SELECT * FROM my_blob_table";
ResultSet rset = stmt.executeQuery (cmd);
// Get the blob data - cast to OracleResult set to
// retrieve the data in oracle.sql format
String index = ((OracleResultSet)rset).getString(1);
blob = ((OracleResultSet)rset).getBLOB(2);
// get the length of the blob
int length = blob.length();
// print the length of the blob
System.out.println("blob length" + length);
// read the blob into a byte array
// then print the blob from the array
byte bytes[] = blob.getBytes(1, length);
blob.printBytes(bytes, length);
LOBのデータ・インタフェースは、LOBの内容全体の読取りと書込みができる効率的なメカニズムを提供しています。多くの場合、これによってコーディングが簡素化され、処理が早くなります。標準のjava.sql.Blobおよびjava.sql.Clobインタフェースと、Oracle拡張機能のoracle.sql.BLOB、oracle.sql.BFILEおよびoracle.sql.CLOBのようなランダム・アクセス機能または2147483648要素を超えるアクセスは提供されていません。
入力
Oracle Database 11gリリース1(11.1)では、PreparedStatementのsetBytes、setBinaryStream、setString、setCharacterStreamおよびsetAsciiStreamメソッドがBLOBおよびCLOBパラメータに関して拡張されています。
JDBC Oracle Call Interface(OCI)およびThinドライバについては、byte配列またはStringのサイズに制限がなく、ストリーム関数に指定される長さにも、Java言語による制限以外はありません。Java言語による制限とは、配列サイズが正のJava intまたは2147483648要素に制限されるというものです。
サーバー側内部ドライバについては、現在INSERT文などのSQL文の操作に4000バイトという制限があります。この制限はPL/SQL文には適用されません。INSERT文については、次のようにPL/SQLブロック内にラップするという簡単な回避方法があります。
BEGIN INSERT id, c INTO clob_tab VALUES(?,?); END;
大量のデータに対する次のような入力モードの自動切替えは留意する必要があります。
SQL文の場合
データが2000バイトを超える場合は、setBytesがsetBinaryStreamに切り替わります。
データが32766文字を超える場合は、setStringがsetCharacterStreamに切り替わります。
PL/SQL文
データが2000バイトを超える場合は、setBytesがsetBinaryStreamに切り替わり、32766バイトを超える場合は、setBytesForBlobに切り替わります。
文字列データがデータベースのキャラクタ・セット32766文字を超える場合は、setStringがsetStringForClobに切り替わります。
setNClobまたはsetObjectは、大きな各国語キャラクタ・セット型データに使用されます。setObjectメソッドが使用される場合、ターゲット・データ型をTypes.NCHAR、Types.NCLOB、Types.NVARCHARまたはTypes.LONGNVARCHARとして設定する必要があります。
これは一部のプログラムに影響を与えます。以前は、それらのプログラムで32766文字を超えるString値にsetStringを使用しようとするとORA-17157エラーが発生していました。現在は、ターゲット・パラメータの型に応じて、アプリケーションの実行中にエラーが発生する場合と、操作が成功する場合があります。
もう1つの影響は、自動切替えの結果、パラメータの型の変更に適合させるために追加のサーバー側解析が実行される場合があることです。その結果、文が繰り返し実行されてデータ・サイズが制限の上下に変動すると、パフォーマンスに影響が生じます。ストリーム・モードへの切替えはバッチ処理にも影響を与えます。
Oracle Database 10gリリース1(10.1)にはSetBigStringTryClob接続プロパティがあります。このプロパティを設定すると、データが大量の場合に、標準setStringメソッドがsetStringForClobメソッドに切り替えられます。このプロパティは使用されなくなり、不要です。setBytesForBlobメソッドとsetStringForClobメソッドはテンポラリLOBを作成します。文が実行されるか、実行前に文を閉じると、テンポラリLOBが自動的に解放されます。
ただし、PL/SQLプロシージャまたはファンクションがSQL文に埋め込まれている場合、4KB未満のデータは標準であるStringとしてバインドされます。4KBより大きいデータの場合は、他のSQL文と同様にドライバがデータをStringとしてバインドします。これによりエラーが発生します。回避策としては、setStringまたはsetStringForClobのかわりに、setClobまたはsetCharacterStreamを使用します。さらに、コール可能文も作成できます。
出力
ResultSetおよびCallableStatementのgetBytes、getBinaryStream、getSting、getCharacterStreamおよびgetAsciiStreamメソッドは、BLOB、CLOBおよびBFILE列またはOUTパラメータと連携するよう拡張されています。これらのメソッドは、2147483648未満の長さの任意のLOBに対して機能します。これは完全にクライアント側で動作し、サポートされる任意のバージョンのデータベース、つまりOracle Database 8.1.7以降と連携します。
BLOB、BFILEまたはCLOBデータの読取りおよび書込みには、LONG RAWおよびLONGデータと同じストリーミング・メカニズムを使用することもできます。読み取るには、列に対してdefineColumnType(nn, Types.LONGVARBINARY)またはdefineColumnType(nn,Types.LONGVARCHAR)を使用します。これによって、ダイレクト・ストリームがデータに対してLONG RAWまたはLONG列であるかのように作成されます。この技術はOracle Database 10gリリース1(10.1)以降に限定されます。
CallableSatementとIN OUTパラメータ
PL/SQLの要件は、IN OUTパラメータの入力と出力に同じJava型を使用する必要があります。この章で説明した拡張機能による型の自動切替えが原因で、これに関して問題が発生する場合があります。
ストアド・プロシージャのIN OUT CLOBパラメータがあり、このパラメータの値を設定するためにsetStringを使用するとします。INおよびOUTパラメータについて、バインドを同じ型にする必要があります。データ・サイズが判明していないかぎり、入力モードの自動切替えによって問題が発生します。たとえば、入力データおよび出力データの両方が32766バイトを超えないことが判明している場合は、入力パラメータにsetStringを使用し、OUTパラメータをTypes.VARCHARとして登録し、出力パラメータにgetStringを使用できます。
よりよい解決方法は、ストアド・プロシージャを変更してINパラメータとOUTパラメータを個別にすることです。たとえば、次のストアド・プロシージャがあったとします。
CREATE PROCEDURE clob_proc( c IN OUT CLOB );
これを次のように変更します。
CREATE PROCEDURE clob_proc( c_in IN CLOB, c_out OUT CLOB );
もう1つの回避方法は、コンテナ・ブロックを使用してコールすることです。次のようにclob_procプロシージャをJava文字列でラップすると、prepareCall文に使用できます。
"DECLARE c_temp; BEGIN c_temp := ?; clob_proc( c_temp); ? := c_temp; END;"
どちらの場合でも、最初のパラメータにsetStringを使用し、2番目のパラメータにregisterOutParameterとTypes.CLOBを使用できます。
サイズの制限
非常に大きなbyte配列またはStringが作成されるために、Javaメモリー管理システムのパフォーマンスに影響が生じることに注意してください。Java Virtual Machine(JVM)ベンダーが提供する情報を参照して、大量のデータ要素がメモリー管理に与える影響を理解し、かわりにストリーム・インタフェースを使用することを検討してください。
テンポラリLOBは、一時データの格納に使用できます。データは、通常の表領域ではなく、一時表領域に格納されます。不要になったテンポラリLOBは解放する必要があります。解放しない場合、LOBによって使用されている一時表領域は再生されません。
テンポラリLOBは、表に挿入できます。挿入すると、LOBの永続的なコピーが作成されて格納されます。テンポラリLOBを挿入する方が便利な場合があります。たとえば、LOBデータが相対的に小さい場合は、空のロケータを取り出すデータベース・ラウンドトリップのコストに比べて、データをコピーするオーバーヘッドが小さくなります。データは、最初はサーバーの一時表領域に格納され、その後永続記憶域に移動されるためです。
テンポラリLOBは、staticメソッドcreateTemporary(Connection, boolean, int)を使用して作成します。このメソッドは、oracle.sql.BLOBクラスとoracle.sql.CLOBクラスの両方に定義されています。テンポラリLOBを解放するには、freeTemporaryメソッドを使用します。
public static BLOB createTemporary(Connection conn, boolean isCached, int duration); public static CLOB createTemporary(Connection conn, boolean isCached, int duration);
引数のdurationには、oracle.sql.BLOBクラスまたはoracle.sql.CLOBクラスで定義されているDURATION_SESSIONまたはDURATION_CALLを使用する必要があります。クライアント・アプリケーションの場合は、DURATION_SESSIONが適しています。Javaストアド・プロシージャの場合は、DURATION_SESSIONまたはDURATION_CALLのいずれか適している方を使用できます。
isTemporaryメソッドをコールすることで、LOBがテンポラリLOBかどうかをテストできます。LOBがcreateTemporaryメソッドをコールして作成された場合、isTemporaryメソッドは、trueを戻し、それ以外の場合はfalseを戻します。
テンポラリLOBを解放するには、freeTemporaryメソッドをコールします。セッションまたはコールを終了する前に、テンポラリLOBを解放してください。そうしないと、テンポラリLOBによって使用されている記憶域は再生されません。
|
注意:
|
JDK 1.5でのテンポラリNCLOBの作成
各国語キャラクタ・ラージ・オブジェクト(NCLOB)はcreateTemporaryメソッドの変種を使用して作成します。
CLOB.createTemporary (Connection conn, boolean cache, int duration, short form);
form引数は、作成されるLOBがCLOBであるかNCLOBであるかを指定します。formがoracle.jdbc.OraclePreparedStatement.FORM_NCHARの場合、メソッドはNCLOBを作成します。formがoracle.jdbc.OraclePreparedStatement.FORM_CHARの場合、メソッドはCLOBを作成します。
JDK 1.6でのテンポラリNCLOBの作成
JDBC 4.0ではNCLOBが直接サポートされています。java.sql.Connectionインタフェースの標準のファクトリ・メソッドを使用してNCLOBを作成することができます。
LOBのオープンとクローズは必須ではありません。パフォーマンス上の理由から、LOBのオープンとクローズを選択できます。
オープンとクローズのコール操作内でLOB操作をラップしない場合、LOBを変更するたびにLOBが暗黙的にオープンおよびクローズされ、その結果、ドメイン索引に関するトリガーが起動します。この場合、LOBを変更するとただちに、LOBのドメイン索引がすべて更新されることに注意してください。したがって、ドメインLOB索引は常に有効となり、いつでも使用できます。
オープンとクローズのコール操作内でLOB操作をラップした場合、LOBを変更するたびにトリガーが起動することはありません。かわりに、クローズがコールされた時点で、ドメイン索引に関するトリガーが起動します。たとえば、closeメソッドをコールするまでドメイン索引が更新されないようにアプリケーションを設計できます。ただし、この場合、オープン・コールからクローズ・コールまでの間、LOBのドメイン索引は有効ではなくなります。
LOBを開くには、openメソッドまたはopen(int)メソッドをコールします。その後、そのLOBに関連付けられたトリガーを起動することなく、LOBの読取りまたは書込み操作を実行できます。LOBへのアクセスが終了後、closeメソッドをコールしてLOBをクローズします。LOBをクローズすると、そのLOBに関連付けられたトリガーが起動します。isOpenメソッドをコールすると、LOBがオープンまたはクローズしているかどうかを確認できます。open(int)メソッドをコールしてLOBをオープンした場合は、引数の値がoracle.sql.BLOBクラスおよびoracle.sql.CLOBクラスで定義されたMODE_READONLYまたはMODE_READWRITEと等しい必要があります。MODE_READONLYを使用してLOBをオープンした場合は、そのLOBに書込みを試みるとSQL例外が発生します。
|
注意: トランザクションでオープンしたすべてのLOBをクローズする前に、そのトランザクションをコミットしようとすると、エラーが発生します。オープンしているLOBのオープン状態は破棄され、トランザクションは正常にコミットされます。このため、トランザクション内でLOBおよび非LOBデータに対して行われた変更はすべてコミットされますが、ドメイン索引に関するトリガーは確実には実行されません。 |
この項では、ファイル・ロケータを使用してBFILEにデータを読み取る方法を説明します。この項には、次の項目が含まれます。
データベースからBFILEロケータを取り出すとき、またはデータベースにBFILEロケータを渡すときには、getterおよびsetterメソッドを使用します。
BFILEロケータの取出し
標準JDBC結果セットまたはBFILEロケータを含むコール可能文オブジェクトがある場合は、標準結果セットのgetObjectメソッドを使用して、ロケータにアクセスできます。このメソッドは、oracle.sql.BFILEオブジェクトを返します。
また、結果セットをOracleResultSetにキャストするか、コール可能文をOracleCallableStatementにキャストし、getOracleObjectまたはgetBFILEメソッドを使用することにより、ロケータにアクセスすることもできます。
|
注意:
|
例: 結果セットからのBFILEロケータの取出し
BFILEロケータbfile_col用の列を1つ備えたbfile_tableという表がデータベースにあるものとします。この例では、Statementオブジェクトstmtをすでに作成してあるものとします。
BFILEロケータを選択し、標準結果セット内にselectします。次のように、結果セットをOracleResultSetにキャストすると、getBFILEを使用してBFILEロケータを取り出せます。
// Select the BFILE locator into a result set
ResultSet rs = stmt.executeQuery("SELECT bfile_col FROM bfile_table");
while (rs.next())
{
oracle.sql.BFILE my_bfile = ((OracleResultSet)rs).getBFILE(1);
}
別の方法として、getObjectを使用してBFILEロケータを戻すこともできます。この場合、getObjectはjava.lang.Objectを戻すため、結果をBFILEにキャストしてください。たとえば、次のようになります。
oracle.sql.BFILE my_bfile = (BFILE)rs.getObject(1);
例: コール可能文からのBFILEロケータの取出し
BFILE出力パラメータを持つ関数funcをコールする、OracleCallableStatementオブジェクトocsがあるとします。次のコード例は、コール可能文を設定し、出力パラメータをOracleTypes.BFILEとして登録し、文を実行し、BFILEロケータを取り出します。
OracleCallableStatement ocs =
(OracleCallableStatement)conn.prepareCall("{? = call func()}");
ocs.registerOutParameter(1, OracleTypes.BFILE);
ocs.execute();
oracle.sql.BFILE bfile = ocs.getBFILE(1);
BFILEロケータの引渡し
BFILEロケータをプリコンパイルされたSQL文またはコール可能文に渡すには、次のどちらかの方法を使用します。
これらのメソッドは、パラメータ索引およびoracle.sql.BFILEオブジェクトを入力として取ります。
例: BFILEロケータのプリコンパイルされたSQL文への引渡し
BFILEロケータを表に挿入するとき、表にデータを挿入するOraclePreparedStatementオブジェクトopsがあるとします。最初の列は文字列で、2番目の列はBFILEです。また、有効なoracle.sql.BFILEオブジェクトbfileがあります。この場合は、次のように、BFILEをデータベースに書き込みます。
OraclePreparedStatement ops = (OraclePreparedStatement)conn.prepareStatement
("INSERT INTO my_bfile_table VALUES (?,?)");
ops.setString(1,"one");
ops.setBFILE(2, bfile);
ops.execute();
例: BFILEロケータのコール可能文への引渡し
BFILEロケータをコール可能文に渡す方法は、プリコンパイルされたSQL文に渡す方法と同じです。この例では、BFILEロケータはmyGetFileLengthプロシージャに渡され、このプロシージャがBFILEの長さを数値で戻します。
OracleCallableStatement cstmt = (OracleCallableStatement)conn.prepareCall
("begin ? := myGetFileLength (?); end;");
try
{
cstmt.registerOutParameter (1, Types.NUMERIC);
cstmt.setBFILE (2, bfile);
cstmt.execute ();
return cstmt.getLong (1);
}
BFILEデータを読み取るには、最初に、BFILEロケータを取り出す必要があります。ロケータは、コール可能文からも結果セットからも取出し可能です。ロケータを取り出すと、BFILEをオープンせずに、BFILEに対して多数のメソッドをコールできます。たとえば、oracle.sql.BFILEメソッドfileExists()やisFileOpen()を使用して、BFILEがあるかどうか、オープンしているかどうかを判断できます。ただし、データの読取りおよび操作を行うには、次のようにBFILEをオープンし、クローズする必要があります。
oracle.sql.BFILEクラスのopenFileメソッドを使用して、BFILEをオープンします。
完了後、BFILEクラスのcloseFileメソッドを使用します。
BFILEデータは、Javaストリームを使用して読み取られます。BFILEから読み取るには、oracle.sql.BFILEオブジェクトのgetBinaryStreamメソッドを使用し、入力ストリームとしてファイル全体にアクセスします。これにより、java.io.InputStreamオブジェクトが戻されます。
InputStreamオブジェクトの場合と同様に、オーバーロードされたreadメソッドの1つを使用してファイル・データを読み取り、完了時にcloseメソッドを使用します。
|
注意:
|
例: BFILEデータの読取り
次の例では、oracle.sql.BFILEオブジェクトのgetBinaryStreamメソッドを使用して、BFILEデータをバイト・ストリームに読み取り、その後バイト・ストリームをバイト配列に読み取ります。この例では、BFILEがすでにオープンされているものとします。
// Read BFILE data from a BFILE locator
Inputstream in = bfile.getBinaryStream();
byte[] byte_array = new byte{10};
int byte_read = in.read(byte_array);
この項では、SQL操作を使用して表にBFILE列を作成し、BFILEの格納場所を指定する方法を説明します。この項で示す例では、ConnectionオブジェクトconnおよびStatementオブジェクトstmtは、すでに作成してあるものとします。
新しい表でのBFILE列の作成
BFILEデータを操作するには、表内にBFILE列を作成してから、BFILEの格納場所を指定します。BFILEの格納場所を指定するには、SQL CREATE DIRECTORY...AS文を使用してBFILEの存在するディレクトリの別名を指定します。この例では、ディレクトリの別名はtest_dirで、BFILEの格納場所は/home/workディレクトリです。
String cmd; cmd = "CREATE DIRECTORY test_dir AS '/home/work'"; stmt.execute (cmd);
SQL CREATE TABLE文を使用して、BFILE列を含む表を作成します。この例の場合、表の名前はmy_bfile_tableです。
// Create a table containing a BFILE field cmd = "CREATE TABLE my_bfile_table (x varchar2 (30), b bfile)"; stmt.execute (cmd);
この例では、VARCHAR2列は行番号を示し、BFILE列にはBFILEデータのロケータが格納されます。
BFILE列のデータ移入
SQL INSERT INTO...VALUES文を使用してVARCHAR2およびBFILEフィールドにデータを移入します。BFILE列には、BFILEデータを指すロケータが移入されます。BFILE列にデータを移入するには、bfilename関数を使用してディレクトリの別名およびBFILEのファイル名を指定します。
cmd ="INSERT INTO my_bfile_table VALUES ('one', bfilename(test_dir,
'file1.data'))";
stmt.execute (cmd);
cmd ="INSERT INTO my_bfile_table VALUES ('two', bfilename(test_dir,
'jdbcTest.data'))";
stmt.execute (cmd);
この例では、ディレクトリの別名はtest_dirです。BFILE file1.data のロケータは、行oneのBFILE列にロードされます。BFILE jdbcTest.dataのロケータは、行twoのbfile列にロードされます。
別の方法として、この時点で行番号用の行およびBFILEロケータを作成し、ロケータの挿入は後で行うこともできます。この場合は、行番号を表に挿入し、BFILEロケータのプレースホルダとしてnullを挿入します。
cmd ="INSERT INTO my_bfile_table VALUES ('three', null)";
stmt.execute(cmd);
この例では、threeが行番号列に挿入され、プレースホルダとしてnullが挿入されます。プログラムの後半で、プリコンパイルされたSQL文を使用してBFILEロケータを表に挿入します。
まず、有効なBFILEロケータを取出して、bfileオブジェクトに挿入します。
rs = stmt.executeQuery("SELECT b FROM my_bfile_table WHERE x='two'");
rs.next();
oracle.sql.BFILE bfile = ((OracleResultSet)rs).getBFILE(1);
その後、プリコンパイルされたSQL文を作成します。この例ではsetBFILEメソッドを使用してBFILEを識別しているため、次のようにプリコンパイルされたSQL文をOraclePreparedStatementにキャストする必要があります。
OraclePreparedStatement ops = (OraclePreparedStatement)conn.prepareStatement
(UPDATE my_bfile_table SET b=? WHERE x = 'three');
ops.setBFILE(1, bfile);
ops.execute();
これで、行twoと行threeには、同じBFILEが格納されます。
表の中で使用可能なBFILEロケータの準備が完了すると、BFILEデータへのアクセスおよび操作が可能になります。
表の中にBFILEロケータを配置すると、ロケータの指すデータへのアクセスと操作が可能になります。データへのアクセスおよび操作を行うには、まず結果セットまたはコール可能文からロケータを選択する必要があります。
次のコードは前項の例から続いています。表の行twoからBFILEのロケータを取り出し、結果セットに挿入します。結果セットをOracleResultSetにキャストして、結果セットに対してoracle.sql.*メソッドを使用可能にします。BFILEに適用されるメソッドには、getDirAliasおよびgetNameのように、BFILEのオープンが不要なものもあります。読取り、長さの取出し、表示など、BFILEデータを操作するメソッドでは、BFILEをオープンする必要があります。
BFILEデータの操作完了時には、BFILEをクローズする必要があります。
// select the bfile locator
cmd = "SELECT * FROM my_bfile_table WHERE x = 'two'";
rset = stmt.executeQuery (cmd);
if (rset.next ())
BFILE bfile = ((OracleResultSet)rset).getBFILE (2);
// for these methods, you do not have to open the bfile
println("getDirAlias() = " + bfile.getDirAlias());
println("getName() = " + bfile.getName());
println("fileExists() = " + bfile.fileExists());
println("isFileOpen() = " + bfile.isFileOpen());
// now open the bfile to get the data
bfile.openFile();
// get the BFILE data as a binary stream
InputStream in = bfile.getBinaryStream();
int length ;
// read the bfile data in 6-byte chunks
byte[] buf = new byte[6];
while ((length = in.read(buf)) != -1)
{
// append and display the bfile data in 6-byte chunks
StringBuffer sb = new StringBuffer(length);
for (int i=0; i<length; i++)
sb.append( (char)buf[i] );
System.out.println(sb.toString());
}
// we are done working with the input stream. Close it.
in.close();
// we are done working with the BFILE. Close it.
bfile.closeFile();
Oracle Database 11gリリース1(11.1)では、LOB用に完全に設計し直された記憶域であるOracle SecureFileにより、次の機能が提供されています。
SecureFile圧縮により、データを圧縮してディスク容量を節約できます。
SecureFile暗号化では、暗号化されたデータのランダムな読取りおよび書込みを可能にする新しい暗号化ツールが導入されました。
重複では、Oracle Databaseの重複LOBデータが自動検出され、データを1部のみ保存することで容量が節約されます。
LOBデータ・パス最適化には、記憶域層上での論理キャッシュ、読取りプリフェッチ、新しいキャッシュ・モード、ベクトルIOなどが含まれます。
高いパフォーマンスの容量管理。
これらの機能はデータベースに実装されており、既存のAPIを使用してJDBCプログラムで透過的に使用できます。
新しいsetLobOptionsおよびgetLobOptions APIについては『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。コール可能文を使用してJDBCからアクセスすることもできます。
|
関連項目: 『Oracle Database SecureFilesおよびラージ・オブジェクト開発者ガイド』 |