6 型のサポート
この章では、Oracle SQLJ実装でサポートされているデータ型について説明し、サポートされているSQL型とそれに対応するJava型を示します。さらに、ストリームとOracle拡張型のサポートについても詳しく説明します。SQLJでサポートされるJava型とは、ホスト式で使用できる型のことです。
この章の構成は、次のとおりです。
関連項目:
6.1 ホスト式での型サポート
ここでは、Oracle SQLJ実装でサポートされている型について概説します。具体的には、Java Database Connectivity(JDBC)2.0の型に関して新規にサポートされた点について説明します。
関連項目:
各Oracle SQL型ごとの正当なJavaマッピングの詳細リストは、『Oracle Database JDBC開発者ガイド』を参照してください。
注意:
SQLJでは、SQL型とJava型との変換が暗黙的に実行されるようになっています。この型変換は、一般的には便利ですが、予想と異なる結果を招く場合もあります。コードが正確かどうかの確認は、変換時の型チェックのみに頼らないでください。
この項の内容は次のとおりです。
6.1.1 サポートされている型の概要
表6-1に、Oracle JDBCドライバを使用した場合にホスト式で使用できるJava型を示します。また、Java型、SQL型(oracle.jdbc.OracleTypes
クラスに型コードが定義されている)およびOracle Database 12c リリース2 (12.2)のデータ型との相関関係もこの表からわかります。
注意:
OracleTypes
クラスは、各Oracleデータ型の型コード(整数の定数)のみを定義します。標準JDBC型のOracleTypes
の値は、標準java.sql.Types
の値と同じです。
Java変数に出力されるSQLデータは、該当するJava型に変換されます。SQLに入力されるJava変数は、該当するOracleデータ型に変換されます。
表6-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 12c リリース2 (12.2)では、すべての数値型が
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
などのパラメータ設定が必要になることがあります。 -
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 SQLJ実装では、文字列を
WHERE
句のCHAR
列値と比較するときに、自動的に空白埋めを考慮する機能があります。この機能がない場合は、データベース列の文字数と一致させるために文字列に対する埋込みが必要です。この機能は、Oracle固有コード生成ではSQLJトランスレータ・オプション、ISO標準コード生成ではOracleカスタマイザ・オプションとして使用できます。 -
弱い型指定は、
OUT
パラメータまたはINOUT
パラメータには使用できません。OracleOPAQUE
型と同様に、これはSTRUCT
、REF
およびARRAY
のOracle拡張型および対応する標準的なJDBC 2.0型に適用されます。 -
次に、Oracle拡張機能の使用に必要な項目を示します。
-
Oracle JDBCドライバ
-
変換時のOracle固有コード生成またはOracleのカスタマイズ
-
アプリケーション実行時のOracle SQLJランタイム
-
6.1.2 JDBC 2.0でサポートされている型と要件
表6-1に示したとおり、Oracle JDBCおよびSQLJの実装で標準java.sql
パッケージのJDBC 2.0型がサポートされています。ここでは、JDBC 2.0でサポートされている型および関連するOracleの拡張機能について説明します。
表6-2に、Oracle SQLJ実装でサポートされているJDBC 2.0型を示します。このJDBC 2.0型とそれに対応するOracle拡張型については、この表を参照してください。
Oracle拡張型は、以前のリリースから提供されていたもので、引き続き現行のリリースでも利用できます。これらのoracle.sql.*
クラスでは、SQLのRAWデータをラップする機能が用意されています。
表6-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 Database 11g からは、Oracle SQLJ実装でイテレータ列に配列型を使用できるISO SQLJの機能がサポートされています。java.sql.Array
またはoracle.sql.ARRAY
列を使用するイテレータを宣言できます。たとえば、次のデータベース表が定義されているとします。
CREATE OR REPLACE TYPE arr_type IS VARRAY(20) OF NUMBER; CREATE TABLE arr_type (arr_col1 arr_type, arr_col2 arr_type);
次のように対応するイテレータ型を定義できます。
#sql static iterator MyIter (oracle.sql.ARRAY arr_col1, java.sql.Array arr_col2);
6.1.3 PL/SQLのBOOLEAN型、RECORD型およびTABLE型の使用
Oracle SQLJおよびJDBCの実装では、コール用引数または戻り値として、PL/SQLのBOOLEAN
型またはRECORD型をサポートしていません。
TABLE型のサポート
Oracle JDBCドライバでは、PL/SQLのスカラー索引付き表がサポートされています。
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レコードを使用するストアド・プロシージャをラッピングするには、レコードをその個々のコンポーネント(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;
6.1.4 Oracle JDBCの旧リリースとの下位互換性
ここでは、Oracle SQLJ実装をOracle JDBCの旧リリースとともに使用する際の下位互換性の問題について説明します。
Oracle Database 11g リリース1 (11.1)のSQLJでは、Oracle9i DatabaseおよびOracle Database 10g リリース1 (10.1)で開発したアプリケーションが完全にサポートされます。ただし、Oracle Database 11g リリース1 (11.1)では、JDBCリソースがSQLJランタイム・リソース・ファイナライザによってクローズされません。そのため、Oracle Database 11g リリース1 (11.1)より前のリリースで開発した一部のアプリケーションでは、JDBC接続およびJDBC文がリークされることがあります。このようなリークを回避するには、SQLJアプリケーションで、接続コンテキスト、実行コンテキスト、イテレータなど、すべてのSQLJランタイム・リソースを適切にクローズする必要があります。
注意:
Oracle9i リリース2では、最初にOPAQUE型およびTIMESTAMP型のサポートが追加されました。
Oracle8iデータベースとの下位互換性
次のOracle Database 11gの機能(Oracle9iデータベースでも使用可能)は、Oracle8iのJDBCドライバではサポートされていないか、異なる形でサポートされています。
-
ユーザー定義SQL型のJavaマッピング用の
oracle.sql.ORAData
およびORADataFactory
インタフェースかわりに、
oracle.sql.CustomDatum
およびCustomDatumFactory
インタフェースを使用します。 -
グローバリゼーション・サポートのための文字型に対するOracle拡張型:
NCHAR
、NCLOB
、NString
およびNcharCharacterStream
(または旧リリースでのNcharAsciiStream
およびNcharUnicodeStream
)
6.2 ストリームのサポート
標準SQLJには、ロング・データをストリームとして処理する次の2つの専用クラスがあります。
-
sqlj.runtime.BinaryStream
-
sqlj.runtime.CharacterStream
イテレータ列でデータベースからデータを取り出すとき、または入力ホスト変数を使用してデータをデータベースに送信するときに、これらのストリーム型を使用します。一般のJavaストリームと同じように、これらのクラスでは、大きなデータ項目を扱いやすい大きさの塊に分割して処理し、転送できます。
ここでは、これらのクラス、Oracle固有のSQLJ拡張機能およびストリーム・クラスのメソッドの一般的な使用方法について説明します。内容は次のとおりです。
注意:
JDBC 2.0では、AsciiStream
クラスおよびUnicodeStream
クラスは、CharacterStream
クラスに置換されています。CharacterStream
によって、ユーザーはエンコーディングに関する不要なロジスティクスから解放されます。
6.2.1 SQLJストリームの一般的な使用方法
通常、表6-1のデータ型をこれらのストリーム・クラスで処理します。つまり、
-
BinaryStream
は通常、LONG RAW
(Types.LONGVARBINARY
)で使用されますが、RAW
(Types.BINARY
またはTypes.VARBINARY
)でも使用されます。 -
CharacterStream
は通常、LONG
(java.sql.Types.LONGVARCHAR
)で使用されますが、VARCHAR2
(Types.VARCHAR
)でも使用されます。
ストリームの使用方法は自由です。ホスト変数にSQLJストリーム型を使用して、データの送信または取出しが実行できます。
表6-1に示したように、LONG
データとVARCHAR2
データはJavaのString
でも使用可能であり、RAW
データとLONGRAW
データはJavaのbyte[]
配列でも使用可能です。また、データベースでBLOB
やCLOB
などのラージ・オブジェクト型がサポートされる場合は、これらを使用する方がLONG
やLONG RAW
などの型よりも便利な場合もあります。ラージ・オブジェクトからのデータの抽出には、ストリームも使用できます。Oracle SQLJおよびJDBC実装では、ラージ・オブジェクト型がサポートされます。
関連項目:
SQLJストリーム・クラスは両方ともに標準Javaクラスのサブクラス(BinaryStream
のjava.io.InputStream
およびCharacterStream
のjava.io.Reader
)で、SQLJに必要な機能を提供するラッパーとして動作します。つまり、基になるデータが適切に処理および変換されるように、データのデータ型とデータ長をSQLJに通知します。
6.2.2 ストリームをサポートするクラスの主な特長
次の略記されたコードは、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); }
注意:
-
コンストラクタの
int
パラメータは、データ長をバイト数または文字数の適切な値で指定します。 -
java.io.InputStream
オブジェクトを入力に使用するメソッドの場合は、かわりにBinaryStream
オブジェクトを使用できます。同様に、java.io.Reader
オブジェクトを入力に使用するメソッドの場合は、かわりにCharacterStream
オブジェクトを使用できます。 -
現在推奨されていない
AsciiStream
クラスおよびUnicodeStream
クラスにも、BinaryStream
と同じ主な特長とシグネチャがあります。
6.2.3 データ送信時のSQLJストリームの使用
標準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の配列はすべて、データ長を戻す機能を備えているため、配列の方がファイルの場合より簡単です。
次の手順で、バイト配列からデータベースを更新します。
次に、バイト配列から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(); ...
注意:
この例に示したように、ストリームは必ずしも必要ではありません。かわりに、バイト配列から直接データベースを更新することも可能です。
6.2.4 ストリームへのデータの取出し: 注意
データの取出しにSQLJストリーム・クラスも使用できますが、一部のデータベース製品に関しては、ストリーム使用時に注意事項があります。ロング・データの読取りおよびストリームへの書込みにOracle Database 12c リリース2 (12.2)とOracle JDBCドライバを使用する場合は、ストリーム・データへのアクセス方法およびデータの処理方法に注意する必要があります。
Oracle JDBCドライバはイテレータ行からデータにアクセスするため、通信パイプからストリーム項目をフラッシュした後で次のデータ項目にアクセスするようになっています。イテレータ行の処理時にストリーム・データがローカル・ストリームに書き込まれる場合でも、JDBCドライバが次のデータ項目にアクセスする前にローカル・ストリームからデータを読み取らないと、ストリーム・データが失われます。このようなストリーム処理は、ストリームの特性である大型化と長さが不明な点を考慮したものです。
したがって、Oracle JDBCドライバでストリーム項目にアクセスし、ローカル・ストリーム変数への書込みが完了した時点で、このローカル・ストリームの読取りと処理を行い、その後で、イテレータから別のアクセスを行う必要があります。
特に、位置イテレータの場合は必須のFETCH INTO
構文があるため、注意が必要です。各回のフェッチでは、すべての列の読取りが完了した後で、処理が開始されます。したがって、ストリーム項目は1つのみ、つまり最後にアクセスした項目のみとなります。
次に、注意点をまとめます。
-
位置イテレータを使用する場合、使用できるのは1つのストリーム列のみで、最後の列である必要があります。イテレータの各行をフェッチしたらすぐに、ストリーム項目をプロセスのローカル入力ストリーム変数に書き込み、イテレータの次の行に進む前にそのローカル・ストリーム変数を読み取って処理する必要があります。
-
名前付きイテレータを使用すると、複数のストリーム列を操作できます。ただし、各イテレータ行を処理するときは、ストリーム・フィールドにアクセスするたびに、そのデータをプロセスのローカル・ストリーム変数に書き込み、ただちにローカル・ストリームを読み取って処理してから、イテレータの他のデータを読み取る必要があります。
また、名前付きイテレータの各行を処理するときは、イテレータ設定時の問合せでデータベース列を選択したときと同じ順番で、列のアクセッサ・メソッドをコールする必要があります。前述のように、問合せ後の通信パイプにストリーム・データが残っているためです。列へのアクセス順が異なると、ストリーム・データがスキップされ、他の列へのアクセス時に失われることがあります。
注意:
-
Oracle Database 12c リリース2 (12.2)およびOracle JDBCドライバの場合は、
SELECT INTO
文でストリームを使用できません。 -
デフォルトで、
mark
メソッドおよびreset
メソッドは、入力ストリームでサポートされていません。任意の入力ストリームをコンストラクタに渡すと、InputStream
クラスのreset
メソッドがIOException
をスローします。このため、NcharAsciiStream
コンストラクタに渡す場合は常に、入力ストリームが適切な状態であることを確認します。たとえば、ストリームにデータが存在しない場合やストリームが終了している場合は、NcharAsciiStream
に渡す前にストリームをリセットします。
6.2.5 データ取得時のSQLJストリームの使用
データをストリームとして取り出すとき、標準SQLJでは、名前付きまたは位置イテレータのSQLJストリーム型列へのデータの読込みができます。
ここでは、位置イテレータまたは名前付きイテレータを使用してデータをSQLJストリームに取り出す基本的な手順を示します。前の項で述べた注意事項に留意してください。
位置イテレータのSQLJストリーム列の使用方法
次の手順で、位置イテレータを使用して、データをSQLJストリームに取り出します。
-
位置イテレータ・クラスを宣言します。最終列を該当するSQLJストリーム型として指定します。
-
イテレータ型のローカル変数を宣言します。
-
該当するSQLJストリーム型のローカル変数を宣言します。これをホスト変数として、イテレータのSQLJストリーム列の各行からデータを取得します。
-
問合せを実行して、手順2で宣言したイテレータにデータを設定します。
-
通常どおり、イテレータを処理します。
FETCH INTO
文のINTO
リスト内のホスト変数は、位置イテレータの列順に記述する必要があるので、ローカル入力ストリーム変数をリストの最後のホスト変数にします。関連項目:
-
イテレータの処理ループでは、各イテレータ行にアクセスした時点でローカル入力ストリームの読取りと処理を行い、必要に応じてストリーム・データの格納や出力を行います。
-
イテレータ処理ループが一巡するたびに、ローカル入力ストリームを終了します。
-
イテレータを終了します。
注意:
必須ではありませんが、イテレータ処理ループが一巡するたびにローカル入力ストリームを終了することをお薦めします。
<<<[11g用?] コードの例を使用してください。>>>
名前付きイテレータのSQLJストリーム列の使用方法
次の手順で、名前付きイテレータを使用して、データを1つ以上のSQLJストリームに取り出します。
<<<[11g用?] コードの例を使用してください。>>>
注意:
-
SQLJストリーム・オブジェクトにデータを移入した場合は、ストリームのデータ長属性は意味を持ちません。この属性が意味を持つのは、データ長を明示的に設定した場合、つまり各SQLJストリーム・クラスの
setLength()
メソッドを使用したか、またはコンストラクタにデータ長を指定した場合です。 -
必須ではありませんが、イテレータ処理ループが一巡するたびにローカル入力ストリームを終了することをお薦めします。
6.2.6 ストリーム・クラスのメソッド
名前付きまたは位置イテレータの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[]
文字配列に書き込みます。戻り値として、読み取った文字数を示すint
値が戻されますが、データの終端に達した場合は-1
が戻されます。入力が用意されるまで、このメソッドはブロックします。 -
long skip (long n)
: 入力のn
文字のデータをスキップし、破棄します。実際にスキップする文字数が、指定値より少ない場合もあります。実際にスキップした文字数は、long
値で戻されます。 -
void close()
: ストリームを終了し、関連リソースを解放します。
6.2.7 ストリーム・データの取出しおよび処理例
ここでは、ストリーム・データを取り出す例を示します。
-
例6-1では、
SELECT
文でLONG
列からデータを選択し、名前付きイテレータのSQLJCharacterStream
列に設定します。 -
例6-2では、
SELECT
文でLONG RAW
列からデータを選択し、位置イテレータのSQLJBinaryStream
列に設定します。
例6-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文字ストリームを渡すことが可能です。
例6-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();
...
6.2.8 出力パラメータおよびファンクションの戻り値としてのSQLJストリーム・オブジェクト
前述のように、標準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(); ...
注意:
ストリームを必ずしも終了する必要はありませんが、終了することをお薦めします。
6.3 JDBC 2.0 LOB型とOracle拡張型のサポート
Oracle SQLJ実装では、次に示したようにJDBC 2.0のデータ型とOracle固有のデータ型に対して拡張機能が用意されています。
-
JDBC 2.0ラージ・オブジェクト(LOB)型(
BLOB
およびCLOB
) -
Oracle
BFILE
型 -
Oracle
ROWID
型 -
Oracle REF CURSOR型
-
その他のOracle Database 12c リリース2 (12.2)データ型(
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固有のチェッカが起動されます。
この項の内容は次のとおりです。
6.3.1 パッケージoracle.sql
oracle.sql
パッケージは、SQLJユーザーにもJDBCユーザーにも重要であり、このパッケージ内のクラスによって、Oracle Database 12c リリース2 (12.2)のすべてのデータ型(oracle.sql.ROWID
、oracle.sql.CLOB
およびoracle.sql.NUMBER
など)がサポートされます。oracle.sql
クラスは、SQLのrawデータ用のラッパーであり、Java形式への適切なマッピングと変換メソッドを提供します。oracle.sql.*
オブジェクトは、対応するSQLデータのバイナリ表現をバイト配列の形式で保持しています。各oracle.sql.*
データ型クラスは、oracle.sql.Datum
クラスのサブクラスです。
Oracle固有のセマンティクス・チェックを行うには、適切なチェッカを使用する必要があります。デフォルトのチェッカoracle.sqlj.checker.OracleChecker
は、フロントエンドとして機能し、環境に応じたチェッカを起動します。JDBCドライバを使用すると、Oracle固有のチェッカが起動されます。
6.3.2 BLOB、CLOBおよびBFILEのサポート
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機能を使用した場合の比較
LOBでBLOBおよびCLOBクラスを使用した場合とDBMS_LOB機能を使用した場合の比較
例6-5および例6-6では、BLOBでoracle.sql
メソッドを使用した場合とDBMS_LOB
パッケージを使用した場合を比較します。また、例6-7および例6-8では、CLOBでoracle.sql
メソッドを使用した場合とDBMS_LOB
パッケージを使用した場合を比較します。
LOBおよびBFILE型でのストアド・ファンクションの結果
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番目の行を表に挿入します。
LOBとBFILEを使用したイテレータの宣言
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バイトずつデータが書き込まれます。
例6-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は、あらかじめオープンしておく必要があります。
例6-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
クラスの該当メソッドをコールし、ファイルをオープンにしておく必要があります。
例6-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全体を読み取るまで、ループが繰り返されます。
例6-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
です。
例6-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バイトずつ書き込むループを繰り返します。
例6-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[]
型です。
6.3.3 Oracle ROWIDのサポート
Oracle固有のROWID
型は、データベース表の各行の一意のアドレスを格納するための型です。oracle.sql.ROWID
クラスはROWID情報をラッピングするもので、ROWID
型の変数をバインドおよび定義するために使用します。
oracle.sql.ROWID
型の変数は、Oracle Database 12c リリース2 (12.2)に接続する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 first_name, rowid FROM employees }; while (iter.next()) { if (iter.first_name().equals("Peter Hall")) { rowid = iter.rowid(); #sql { UPDATE employees SET salary = salary + 500 WHERE rowid = :rowid }; } } iter.close(); ...
この例では、ROWIDに従って、従業員Peter Hallの給与が$500増えます。
ストアド・ファンクションの結果としてのROWID
次のファンクションについて考えてみます。
CREATE OR REPLACE FUNCTION get_rowid (name VARCHAR2) RETURN ROWID IS rid ROWID; BEGIN SELECT rowid INTO rid FROM employees WHERE first_name = name; RETURN rid; END get_rowid;
このストアド・ファンクションの場合は、このファンクションの戻り値の代入型として、ROWID
オブジェクトが次のように使用されます。
ROWID rowid; #sql rowid = { values(get_rowid('AMY FEINER')) }; #sql { UPDATE employees SET salary = salary + 500 WHERE rowid = :rowid };
この例では、ROWID
に従って、従業員Amy Feinerの給与が$500増えます。
SELECT INTOのターゲットとしてのROWID
ROWID
型のホスト変数は、SELECT INTO
文のINTO
リストに記述できます。
ROWID rowid; #sql { SELECT rowid INTO :rowid FROM employees WHERE first_name='PETER HALL' }; #sql { UPDATE employees SET salary = salary + 500 WHERE rowid = :rowid };
この例では、ROWID
に従って、従業員Peter Hallの給与が$500増えます。
ROWIDホスト変数と位置イテレータのFETCH INTOターゲット
ROWID
型のホスト変数は、FETCH INTO
文のINTO
リストに記述できます(イテレータ内の該当列の属性が同じ型の場合)。
#sql iterator PositionedRowidIter (String, ROWID); ... PositionedRowidIter iter; ROWID rowid = null; String ename = null; #sql iter = { SELECT first_name, rowid FROM employees }; while (true) { #sql { FETCH :iter INTO :ename, :rowid }; if (iter.endFetch()) break; if (ename.equals("PETER HALL")) { #sql { UPDATE employees SET salary = salary + 500 WHERE rowid = :rowid }; } } iter.close(); ...
この例は、前述の名前付きイテレータの例に似ていますが、位置イテレータの必須FETCH INTO
構文を使用します。
位置指定更新および位置指定削除
Oracle Database 11g リリース1以降、SQLJで位置指定更新および位置指定削除の操作がサポートされています。位置指定更新または位置指定削除の操作は、イテレータを使用して実行できます。位置指定更新または位置指定削除に使用されるイテレータは、sqlj.runtime.ForUpdate
インタフェースを実装している必要があります。名前付きイテレータ、位置イテレータまたはスクロール可能なイテレータを使用できます。
次のコードは、位置指定更新を示しています。
... #sql iterator iter implements sqlj.runtime.ForUpdate(String str) ... #sql iter = {SELECT first_name FROM employees WHERE department_id=10}; ... while(iter.next()) { #sql {UPDATE employees SET salary=salary+5000 WHERE CURRENT OF :iter}; } ...
前述のコードでは、イテレータiter
が作成され、employees
表を更新する際に使用されます。
注意:
同期の問題を回避する場合は、SELECT ... FOR UPDATE
文を発行します。
同様に位置指定削除を実行できます。次に例を示します。
... #sql {DELETE FROM employees 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 salary, first_name from employees }; } ….. }
ここで、SELECT文にFOR UPDATE
が追加され、次のようにForUpdate
イテレータsalbn
が戻されます。
……… String theSqlTS = “SELECT rowid sjT_rowid,first_name, salary FROM employees WHERE first_name = :1 FOR UPDATE"; ………
表6-3に、for_update
オプションに使用可能な値と前述の例の対応するSQL文を示しています。
表6-3 for_updateオプションの値と対応するSQL文
for_updateオプション | SQLJ文 | SQL文 |
---|---|---|
なし |
|
|
|
|
|
|
|
|
注意:
アプリケーションの選択問合せにFOR UPDATE
が含まれる場合、新しい変換オプションを使用すると、変換時のオンライン・チェック中に警告がスローされます。変換中にオフライン解析を選択すると、変換時にエラーは検出されません。
6.3.4 OracleのREF CURSOR型のサポート
Oracle PL/SQLとOracle SQLJ実装では、データベースのカーソルを表すカーソル変数を使用できます。
REF CURSOR型の概要
カーソル変数は、機能的には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変数に戻り値としての結果を代入したりできます。
SQLJのREF CURSOR型
Oracle SQLJ実装では、REF CURSOR型を任意のイテレータ・クラス型またはjava.sql.ResultSet
型のイテレータ列またはホスト変数にマッピングできます(ただし、ホスト変数はOUT
のみにできます)。次に、REF CURSOR型の用途をまとめます。
-
ストアド・ファンクションの戻り値に使用する結果式として
-
ストアド・プロシージャまたはストアド・ファンクションの出力パラメータの出力ホスト式として
-
INTO
リスト中の出力ホスト式として -
イテレータ列として
SQLのCURSOR
演算子は、外部のSELECT
文の内側にネストされているSELECT
に対して使用できます。これによって、REF CURSORオブジェクトをイテレータ列またはイテレータのResultSet
列に書き込むか、REF CURSORオブジェクトをイテレータ・ホスト変数またはINTO
リストのResultSet
ホスト変数に書き込むことが可能になります。
関連項目:
暗黙的なREF CURSOR変数の使用例(たとえば、CURSOR
演算子の例など)は、「ホスト変数としてのイテレータおよび結果セットの使用」を参照してください。
注意:
-
REF CURSOR型には、型コード
OracleTypes.CURSOR
を使用する必要があります。 -
REF CURSOR型は、
oracle.sql
クラスではサポートされていません。java.sql.ResultSet
クラスまたはイテレータ・クラスを使用する必要があります。リソースを解放するために、結果セットまたはイテレータは、処理完了時点で終了してください。
REF CURSORの例
無名ブロックから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 employees (first_name, employee_id) VALUES (:name, :no); OPEN :out emps FOR SELECT first_name, employee_id FROM employees ORDER BY employee_id; end }; return emps; }
6.3.5 その他のOracle Database 11g データ型のサポート
oracle.sql
のあらゆるクラスは、イテレータ列にも、入力、出力または入出力のホスト変数にも、標準Java型と同じように使用できます。たとえば、前述したクラスでは、oracle.sql.NUMBER
、oracle.sql.CHAR
、oracle.sql.RAW
などのクラスがサポートされています。
oracle.sql.*
クラスはJava型の形式に変換する必要がないため、同等のJava型より効率および精度面で優れています。ただし、標準Javaプログラムで使用する場合、またはエンド・ユーザーで表示する場合は、データを標準Java型に変換する必要があります。
6.3.6 BigDecimalのサポート強化
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
パッケージにあります。