ヘッダーをスキップ
Oracle® Database JDBC開発者ガイド
12cリリース1 (12.1)
B71308-02
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

12 JDBC内のJavaストリーム

この章では、Oracle Java Database Connectivity(JDBC)ドライバで様々なデータ型のJavaストリームを処理する方法を説明します。データ・ストリームを使用することにより、最大2GBのLONG型列データを読み取れます。

この章の内容は次のとおりです。

Javaストリームの概要

Oracle JDBCドライバは、サーバーとクライアント間の双方向のデータ・ストリーム操作をサポートします。ドライバは、すべてのストリーム変換、つまりバイナリ、ASCIIおよびUnicodeをサポートします。次に、ストリームの各タイプを簡単に説明します。

  • バイナリ

    データのRAWバイトのために使用されます。getBinaryStreamメソッドに対応します。

  • ASCII

    ISO-Latin-1エンコーディングでASCIIバイトのために使用されます。getAsciiStreamメソッドに対応します。

  • Unicode

    UTF-16エンコーディングでUnicodeバイトのために使用されます。getUnicodeStreamメソッドに対応します。

getBinaryStreamgetAsciiStreamおよびgetUnicodeStreamの各メソッドは、InputStreamオブジェクト内のデータ・バイトを戻します。


注意:

Oracle Database 12cリリース1 (12.1)以降、CONNECTION_PROPERTY_STREAM_CHUNK_SIZEは非推奨になり、ドライバは内部でこれを使用してストリーム・チャンク・サイズを設定しません。

LONGまたはLONG RAW列のストリーム

問合せがLONGまたはLONG RAW列を1つ以上選択すると、JDBCドライバは、ストリーム・モードでこれらの列をクライアントに転送します。ストリーム・モードでは、JDBCドライバは、必要になるまでLONGまたはLONG RAW列のためにネットワークから列データを読み取りません。コードでgetXXXメソッドを呼び出して列データを読み取るまで、列データはネットワーク通信チャネルに残っています。コールの後でも、列データは、getXXXコールから戻り値にデータを移入する場合にのみ読み取られます。列データが通信チャネルに残っているため、ストリーム・モードは他のすべての接続の使用を妨げます。列データの読取り以外で接続を使用すると、チャネルから列データが廃棄されます。ストリーム・モードはメモリーを効率よく使用し、ネットワークのラウンドトリップを最小限に抑えますが、他の多くのデータベース操作の妨げになります。


注意:

LONGおよびLONG RAW列は使用しないことをお薦めします。かわりにLOBを使用してください。

LONG列のデータにアクセスするには、この列をJava InputStreamオブジェクトとして取得し、InputStreamオブジェクトのreadメソッドを使用します。また、データをStringまたはbyte配列として取得することもできます。この場合、ドライバによってストリーム処理が実行されます。

3種類のストリームのうち、任意のものを使用して、LONGまたはLONG RAWデータを取得できます。ドライバは、データベースおよびドライバのキャラクタ・セットに応じて、変換を行います。


注意:

LONG列を使用した表を作成しないでください。かわりに、ラージ・オブジェクト(LOB)列CLOBNCLOBおよびBLOBを使用してください。LONG列は、下位互換性のみのためにサポートされています。また、既存のLONG列をLOB列に変換することをお薦めします。LOB列に対する制約は、LONG列に比べてはるかに少なくなります。

この項の内容は次のとおりです。

LONG RAWデータの変換

getBinaryStreamへのコールにより、RAWデータが戻されます。getAsciiStreamへのコールにより、RAWデータが16進データに変換され、ASCIIコードが戻されます。getUnicodeStreamへのコールにより、RAWデータが16進データに変換され、Unicode文字が戻されます。

LONGデータの変換

getAsciiStreamを使用してLONGデータを取得した場合、ドライバは、データベースに格納されている基礎となるデータにUS7ASCIIまたはWE8ISO8859P1キャラクタ・セットが使用されているものとみなします。実際にそうである場合、ドライバはASCII文字に対応するバイトを戻します。データベースでUS7ASCIIWE8ISO8859P1のいずれのキャラクタ・セットも使用されていない場合、getAsciiStreamへのコールを実行すると、意味のないコードが戻されます。

getUnicodeStreamを使用してLONGデータを取得すると、Unicode文字のストリームがUTF-16エンコーディングで戻されます。これは、Oracleがサポートする、データベースの基礎となるキャラクタ・セットすべてに当てはまります。

getBinaryStreamを使用してLONGデータを取得する場合、次の2つのケースが考えられます。

  • ドライバがJDBC OCIで、クライアントのキャラクタ・セットがUS7ASCIIまたはWE8ISO8859P1ない場合、getBinaryStreamをコールするとUTF-8が戻されます。クライアントのキャラクタ・セットがUS7ASCIIまたはWE8ISO8859P1に設定されている場合、US7ASCIIバイト・ストリームが戻されます。

  • ドライバがJDBC Thinで、データベースのキャラクタ・セットがUS7ASCIIまたはWE8ISO8859P1ない場合、getBinaryStreamをコールするとUTF-8が戻されます。サーバー側キャラクタ・セットがUS7ASCIIまたはWE8ISO8859P1に設定されている場合、US7ASCIIバイト・ストリームが戻されます。


注意:

LONGまたはLONG RAW列をストリームとして受信するには、データベースから取り出す列の順序に注意する必要があります。詳細は、「データ・ストリームと複数列」を参照してください。

表12-1に、LONGおよびLONG RAWデータ変換の概要をストリーム・タイプ別に示します。

表12-1 LONGおよびLONG RAWデータの変換

データ型 BinaryStream AsciiStream UnicodeStream

LONG

Unicode UTF-8の文字を表すバイト。データベース・キャラクタ・セットがUS7ASCIIまたはWE8ISO8859P1の場合、バイトはUS7ASCIIまたはWE8ISO8859P1のキャラクタを表します。

ISO-Latin-1(WE8ISO8859P1)エンコーディングの文字を表すバイト。

Unicode UTF-16エンコーディングの文字を表すバイト。

LONG RAW

データの変換なし。

16進バイトのASCII表現。

16進バイトのUnicode表現。


LONG RAWデータのストリーム例

getXXXStreamメソッドには、データの増分的取得を可能にする機能があります。一方、getBytesは、すべてのデータを一度のコールでフェッチします。この項には、バイナリ・データ・ストリームの取得方法を示す2つの例が含まれています。LONG RAWデータを取得するのに、最初のバージョンではgetBinaryStreamメソッドを使用し、2番目のバージョンではgetBytesメソッドを使用します。

getBinaryStreamを使用したLONG RAWデータ列の取得

この例では、ローカル・ファイルシステム上のファイルにLONG RAW列の内容を書き込みます。この場合、ドライバはデータを増分的にフェッチします。

次のコードは、名前LESLIEに関連付けられたLONG RAWデータ列を格納する表を作成します。

-- SQL code:
create table streamexample (NAME varchar2 (256), GIFDATA long raw);
insert into streamexample values ('LESLIE', '00010203040506070809');

次のJavaコードの一部は、LONG RAW列のデータを、leslie.gifというファイルに書き込みます。

ResultSet rset = stmt.executeQuery 
                 ("select GIFDATA from streamexample where NAME='LESLIE'");

// get first row
if (rset.next())
{
    // Get the GIF data as a stream from Oracle to the client
    InputStream gif_data = rset.getBinaryStream (1);
   try
   {
      FileOutputStream file = null;
      file = new FileOutputStream ("leslie.gif");
      int chunk;
      while ((chunk = gif_data.read()) != -1)
         file.write(chunk);
   }
   catch (Exception e)
   {
      String err = e.toString();
      System.out.println(err);
   }
   finally
   {
      if file != null()
         file.close();
   }
} 

この例では、getBinaryStreamへのコールにより戻されるInputStreamオブジェクトは、データベース接続から直接データを読み取ります。

getBytesを使用したLONG RAWデータ列の取得

この例では、getBinaryStreamのかわりにgetBytesを使用して、GIFDATA列の内容を取得します。この場合、ドライバは一度のコールですべてのデータをフェッチして、バイト配列に格納します。コードは次のようになります。

ResultSet rset2 = stmt.executeQuery 
                  ("select GIFDATA from streamexample where NAME='LESLIE'"); 

// get first row
if (rset2.next())
{
   // Get the GIF data as a stream from Oracle to the client
   byte[] bytes = rset2.getBytes(1);
   try
   {
      FileOutputStream file = null;
      file = new FileOutputStream ("leslie2.gif");
      file.write(bytes);
   }
   catch (Exception e)
   {
      String err = e.toString();
      System.out.println(err);
   }
   finally
   {
      if file != null()
         file.close();
   }
}

LONG RAW列では最大2GBのデータを格納できるため、getBytesの使用例では、getBinaryStreamの使用例に比べて多量のメモリーを使用できます。LONGまたはLONG RAW列のデータの最大サイズが不明な場合は、ストリームを使用してください。

LONGまたはLONG RAWのストリーム回避


注意:

Oracle Database 12cリリース1 (12.1)以降、このメソッドは非推奨になりました。詳細は、「非推奨となった機能」を参照してください。

JDBCドライバは、任意のLONGおよびLONG RAW列を自動的にストリーム処理します。ただし、データ・ストリームを避ける状況が生じる場合もあります。たとえば、LONG列のサイズが非常に小さい場合、データが増分的ではなく、一度のコールで戻される方が望ましい場合があります。

ストリームを回避するには、defineColumnTypeメソッドを使用してLONG列の型を再定義します。たとえば、LONGまたはLONG RAW列をVARCHAR型またはVARBINARY型として再定義すると、ドライバがデータを自動的にストリーム処理することはなくなります。

defineColumnTypeを使用して列の型を再定義する場合、問合せの中で列の型を宣言する必要があります。列の型を宣言しないと、executeQueryが失敗します。また、Statementオブジェクトをoracle.jdbc.OracleStatementオブジェクトにキャストする必要があります。

さらに、defineColumnTypeを使用すると、問合せの実行時にOCIドライバがデータベースへラウンドトリップを行わずにすみます。defineColumnTypeを使用しない場合、これらのJDBCドライバは列タイプのデータ型を要求する必要があります。JDBC Thinドライバでは、ラウンドトリップの回数が常に最小であるため、defineColumnTypeを使用する利点はありません。

前の項の例を使用して、StatementオブジェクトstmtOracleStatementへキャストされ、LONG RAWデータを含む列はVARBINARAY型として再定義されます。このデータはストリーム化されません。かわりに、バイト配列で戻されます。コードは次のようになります。

//cast the statement stmt to an OracleStatement
oracle.jdbc.OracleStatement ostmt = 
   (oracle.jdbc.OracleStatement)stmt;

//redefine the LONG column at index position 1 to VARBINARY
ostmt.defineColumnType(1, Types.VARBINARY);

// Do a query to get the images named 'LESLIE'
ResultSet rset = ostmt.executeQuery
         ("select GIFDATA from streamexample where NAME='LESLIE'");

// The data is not streamed here
rset.next();
byte [] bytes = rset.getBytes(1);

CHAR、VARCHARまたはRAW列のストリーム


注意:

Oracle Database 12cリリース1 (12.1)以降、このメソッドは非推奨になりました。詳細は、「非推奨となった機能」を参照してください。

defineColumnType Oracle拡張機能を使用してCHARVARCHARまたはRAW列をLONGVARCHARまたはLONGVARBINARYとして再定義する場合は、列をストリームとして取得できます。このプログラムは、列が実際にLONGまたはLONG RAW型であるかのように動作します。通常これらの列は短いため、これが問題になることはほとんどありません。

CHARVARCHARまたはRAW列を、列タイプを再定義することなくデータ・ストリームとして取得しようとすると、JDBCドライバはJava InputStreamを戻しますが、実際のストリームは発生しません。これらのデータ型の場合、JDBCドライバは、executeQueryメソッドまたはnextメソッドへのコール中に、データをインメモリーのバッファに完全にフェッチします。getXXXStreamエントリ・ポイントは、このバッファからデータを読み取るストリームを戻します。

LOBおよび外部ファイルのストリーム

ラージ・オブジェクト(LOB)という用語は、データベース表に直接格納するには大きすぎるデータ項目を指します。一方、ロケータはデータベース表に格納されて、実際のデータの場所を指します。外部ファイルも同様に管理されます。JDBCドライバは、ストリームの使用によって次の型をサポートできます。

  • バイナリ・ラージ・オブジェクト(BLOB)

    非構造化バイナリ・データ用

  • キャラクタ・ラージ・オブジェクト(CLOB)

    文字データ用

  • 各国語キャラクタ・ラージ・オブジェクト(NCLOB)

    各国語キャラクタ・データ用

  • バイナリ・ファイル(BFILE)

    外部ファイル用

LOBとBFILEは、この章で説明した他のストリーム・データとは動作が異なります。表に実際のデータが格納されるかわりに、ロケータが格納されます。ストリームとしてのデータの読取りおよび書込みなど、実際のデータを操作するには、このロケータを使用します。ストリーム処理するときでも、一部のデータ(サイズで定義済)のみがネットワーク間を移動します。これに対して、LONGまたはLONG RAWをストリーム処理するときは、すべてのデータがネットワーク間を移動します。

BLOB、CLOBおよびNCLOBのストリーム

問合せにより、1つ以上のBLOBCLOBまたはNCLOB列をフェッチすると、JDBCドライバはデータをクライアントに転送します。このデータはストリームとしてアクセスできます。JDBCより取得したBLOBCLOBまたはNCLOBデータを操作する場合、Oracleの拡張機能クラスoracle.sql.BLOBoracle.sql.CLOBおよびoracle.sql.NCLOB内のメソッドを使用します。これらのクラスは、BLOBCLOBまたはNCLOBから入力ストリーム内への読取り、出力ストリームからBLOBCLOBまたはNCLOB内への書込み、BLOBCLOBまたはNCLOBの長さの決定およびBLOBCLOBまたはNCLOBのクローズなど、固有の機能を提供します。


注意:

Oracle Database 12cリリース1 (12.1)以降、oracle.sqlパッケージの具象クラスは非推奨となり、oracle.jdbcパッケージのインタフェースに置き換えられています。標準互換性には(可能であれば)java.sqlパッケージの使用可能なメソッドを使用し、Oracle固有の拡張機能にはoracle.jdbcパッケージの使用可能なメソッドを使用することをお薦めします。これらのインタフェースの詳細は、My Oracle Supportノート1364193.1を参照してください。

BFILEのストリーム

外部ファイルであるBFILEは、データベースの外部にあるファイルへのロケータを格納するために使用します。ファイルは、データ・サーバーのファイルシステム上のいずれかの場所に格納されます。ロケータは、ファイルが実際に格納された場所を指します。

問合せにより1つ以上のBFILE列をフェッチすると、JDBCドライバは必要に応じてファイルをクライアントに転送します。データにはストリームとしてアクセスできます。JDBCからBFILEデータを操作するには、Oracle拡張クラスoracle.sql.BFILEのメソッドを使用します。このクラスは、BFILEから入力ストリームへの読取り、出力ストリームからBFILEへの書込み、BFILEの長さの判定、BFILEのクローズなど、特定の機能を提供します。

データ・ストリームと複数列

1つの問合せが複数の列をフェッチする場合、その列の1つにデータ・ストリームが含まれていると、ストリーム列に後続する列の内容はストリームが読み取られるまで利用できず、後続する列が読み取られると、ストリーム列はそれ以降利用できません。ストリーミング列の先にある列を読み取ろうとすると、ストリーミング列はクローズされます。

複数列によるストリーミングの例

次のコードについて検討します。

ResultSet rset = stmt.executeQuery
        ("select DATECOL, LONGCOL, NUMBERCOL from TABLE");
while rset.next()
{
   //get the date data
   java.sql.Date date = rset.getDate(1);

   // get the streaming data
   InputStream is = rset.getAsciiStream(2); 

   // Open a file to store the gif data
   FileOutputStream file = new FileOutputStream ("ascii.dat");

   // Loop, reading from the ascii stream and 
   // write to the file
   int chunk;
   while ((chunk = is.read ()) != -1)
      file.write(chunk);
   // Close the file
   file.close();

   //get the number column data
   int n = rset.getInt(3);  
}

各行の受信データは、次の形式になります。

<a date><the characters of the long column><a number>

結果セットの各行を処理するときには、数値列を読み取る前にストリーム列の処理を完了する必要があります。

ストリーム・データ列のバイパス

ストリーム・データを含む列の読取りを回避することが望ましい場合があります。このようなデータの読取りを回避するには、ストリーム・オブジェクトのcloseメソッドをコールします。このメソッドを使用すると、ストリーム・データが廃棄され、ドライバが、ストリーム・データを含んだ列以降の非ストリーム・データを含んだ列すべてからデータを継続して読み取ることが可能になります。意図的にストリームを廃棄する場合でも、SELECT文で指定した順序で列を取り出すことは、よいプログラミング手法です。

次の例では、LONG列のストリーム・データを廃棄し、DATEおよびNUMBER列のデータのみをリカバリします。

ResultSet rset = stmt.executeQuery
        ("select DATECOL, LONGCOL, NUMBERCOL from TABLE");

while rset.next()
{
   //get the date
   java.sql.Date date = rset.getDate(1);
   
   // access the stream data and discard it with close()
   InputStream is = rset.getAsciiStream(2);
   is.close();   
   
   // get the number column data
   int n = rset.getInt(3); 
}

ストリームのクローズ

ストリームから取得したデータは、closeメソッドをコールすることによりいつでも廃棄できます。不要になったストリームはクローズするのが、望ましいプログラミング手法です。たとえば、次のようになります。

   ...
   InputStream is = rset.getAsciiStream(2);
   is.close();   

注意:

ストリームのクローズは、LONGまたはLONG RAW列のパフォーマンスにはほとんど影響しません。データはすべて、そのままネットワーク経由で移動し、ドライバはネットワークからビットを読み取ることが必要です。

ストリームに関する注意

この項では、ストリームの使用に関して、次に示すいくつかの注意事項を説明します。

ストリーム・データを使用する際の注意

この項では、誤ってストリーム・データを廃棄または喪失することがないよう、事前に注意する必要がある点を説明します。現行ストリームの読取り以外の、データベースと通信する任意のJDBC操作を実行すると、ドライバは自動的にストリーム・データを廃棄します。2つの共通する注意事項について説明します。

  • ストリーム・データはアクセス後に使用します。

    データ・ストリームを含む列からデータを取得するには、列のフェッチのみでは十分ではありません。列の内容をただちに処理する必要があります。そうしないと、次の列のフェッチ時にその内容は廃棄されます。

  • SELECT文で指定した順序でストリーム列をコールします。

    問合せを使用して複数列をフェッチすると、データベースは各行を、列を表すバイト・セットとして、SELECTで指定された順序で送信します。列の1つにストリーム・データが含まれる場合は、データベースは、次の列を処理する前にデータ・ストリーム全体を送信します。

    SELECT文で指定した順序を使用せずにデータへアクセスする場合は、ストリーム・データを失う可能性があります。つまり、ストリーム・データ列をバイパスして次の列のデータにアクセスすると、ストリーム・データは失われます。たとえば、ストリーム・データ列からデータを読み取るNUMBER列のデータにアクセスしようとすると、JDBCドライバは自動的に、まずストリーミング・データを読み取り、続いて廃棄します。LONG列に大量のデータが格納されている場合、これは非常に非効率的です。

    LONG列に後でアクセスしようとしても、データは使用できず、ドライバは「ストリームがクローズされています。」エラーを戻します。

次の例では、2番目の点について例証します。

ResultSet rset = stmt.executeQuery
        ("select DATECOL, LONGCOL, NUMBERCOL from TABLE");
while rset.next()
{
   int n = rset.getInt(3);  // This discards the streaming data
   InputStream is = rset.getAsciiStream(2);
                            // Raises an error: stream closed.
}

ストリームを取得して、それを使用するNUMBER列を取得した場合、ストリーム列はやはり自動的にクローズされます。

ResultSet rset = stmt.executeQuery
                 ("select DATECOL, LONGCOL, NUMBERCOL from TABLE");
while rset.next()
{
   InputStream is = rset.getAsciiStream(2); // Get the stream
   int n = rset.getInt(3);
   // Discards streaming data and closes the stream
}
int c = is.read(); // c is -1: no more characters to read-stream closed

setBytesとsetStringの制限を回避するためのストリームの使用方法

Oracle Database 12c以降、setBytesメソッドおよびsetStringメソッドで使用されるデータ・サイズの上限は大幅に増加されました。どのJava byte配列もsetBytesに渡すことができ、どのJava StringsetStringに渡すことができます。データのサイズ、SQL文またはPL/SQL文および使用するドライバによって、JDBCドライバは自動的に、setBinaryStreamsetCharacterStreamの使用、またはsetBytesForBlobsetStringForClobの使用に切り替わります。

以前のバージョンのOracle Database、およびサーバー側内部ドライバでは一部、制限があります。


関連項目:

詳細は、「LOBのデータ・インタフェース」とリリース・ノートを参照してください。

ストリームと行のプリフェッチ

JDBCドライバがデータ・ストリームを含む列に遭遇すると、行のフェッチ・サイズは1に再設定されます。行のフェッチ・サイズは、データベースにアクセスするたびに複数行のデータを取り出すことができるOracleのパフォーマンス強化点です。