この章では、Oracle SQLJ実装におけるユーザー定義SQL型のサポート方法について説明します。 OracleのJPublisherユーティリティについても説明します。ユーザー定義SQL型に対応するJavaクラスをこのユーティリティで生成できます。この章の最後にはOracle OPAQUE型に関する項目もあります。
この章では、次の項目について説明します。
ここでは、Oracle Database 11g のオブジェクトとコレクションの基本的な概念について説明します。
|
参照: 『Oracle Database SQL言語リファレンス』および『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』 |
ここでは、次の項目について説明します。
Oracle SQLJ実装では、ユーザー定義のSQLオブジェクト型(複合データ構造体)、関連するSQLオブジェクト参照型およびユーザー定義のSQLコレクション型がサポートされています。Oracleのオブジェクトとコレクションは、複合データ構造体であり、複数のデータ要素で構成されます。
Oracle SQLJ実装では、強い型指定または弱い型指定の、Java表現のオブジェクト型、参照型およびコレクション型を、イテレータまたはホスト式で使用できます。強い型指定の表現では、オブジェクト型、参照型またはコレクション型が、Java Database Connectivity(JDBC)2.0標準のjava.sql.SQLDataインタフェース(オブジェクト型の場合のみ)またはOracleのoracle.sql.ORADataインタフェースを実装したカスタムJavaクラスにマップされます。カスタムJavaクラスはOracle Database 11g JPublisherユーティリティを使用して自動的に生成することが可能です。このユーティリティは前述の2つのインタフェースをサポートしています。
「強い型指定」という用語は、特定のJava型がSQL名前指定型またはユーザー定義型に対応付けられている場合に使用されます。たとえば、PERSON型がある場合、対応するPerson Javaクラスが対応付けられます。
弱い型指定の表現では、oracle.sql.STRUCT(オブジェクトの場合)、oracle.sql.REF(オブジェクト参照の場合)またはoracle.sql.ARRAY(コレクションの場合)を使用します。弱い型指定の表現では、標準java.sql.Struct、java.sql.Refまたはjava.sql.Arrayオブジェクトもかわりに使用できます。
「弱い型指定」という用語は、Java型が汎用的な方法で使用され、複数のSQL名前指定型にマップできる場合に使用されます。Javaクラス(インタフェース)には、SQL型に特有の情報はありません。これは、oracle.sql.STRUCT、oracle.sql.REFおよびoracle.sql.ARRAY型とjava.sql.Struct、java.sql.Refおよびjava.sql.Array型の場合です。
コード中にOracle拡張型を使用する場合は、次の要件が伴うので注意してください。
Oracle JDBCドライバを使用する必要があります。
デフォルトのOracle固有コード生成を使用するか、またはISOコード生成の場合は必要に応じてプロファイルをカスタマイズします。Oracle固有生成コードの場合、プロファイルが生成されないため、カスタマイズは適用されません。Oracle JDBC Application Program Interface(API)は、生成されたJavaコードによって直接コールされます。
|
注意: デフォルトのカスタマイザoracle.sqlj.runtime.util.OraCustomizerを使用することをお薦めします。 |
アプリケーションの実行時にOracle SQLJランタイムを使用します。 Oracle SQLJランタイムとOracle JDBCドライバは、Oracle拡張型をコード中に使用しない場合であっても、Oracleカスタマイザを使用する場合は常に必要です。
Oracle固有のセマンティクス・チェックを行うには、適切なチェッカを使用する必要があります。デフォルトのチェッカoracle.sqlj.checker.OracleCheckerは、フロントエンドとして機能し、環境に応じたチェッカを起動します。 JDBCドライバを使用すると、Oracle固有のチェッカが起動されます。
|
注意: Oracleオブジェクトとコレクション用のOracle固有の型は、oracle.sqlパッケージにあります。 |
カスタムJavaクラスの使用方法
この章では、主に、ユーザー定義型に対するカスタムJavaクラスの使用方法について説明しますが、ORADataを実装したクラスは、他のOracle SQL型に対しても使用できます。ORADataを実装したクラスを使用すると、SQLとJava間のデータ転送時に任意の処理または変換を行えます。
SQLDataインタフェースは、カスタム・オブジェクト・クラス専用です。これに対し、ORADataインタフェースは、どのカスタムJavaクラスにも使用できます。
用語上の注意
ユーザー定義のSQLオブジェクト型とSQLコレクション型は、ユーザー定義型(UDT)と呼ばれます。
オブジェクト、参照およびコレクションのカスタムJavaクラスは、それぞれカスタム・オブジェクト・クラス、カスタム参照クラスおよびカスタム・コレクション・クラスと呼ばれます。
|
参照: Oracleオブジェクトの特長と機能の概要は、『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』を参照してください。 |
Oracle SQLオブジェクトは複合データ構造体であり、関連するデータ項目(各従業員に関する事実など)を1つのデータ単位にまとめたものです。オブジェクト型は、機能的にはJavaのクラスに相当します。Java型をインスタンス化してオブジェクトをいくつでも使用できるように、特定のオブジェクト型のオブジェクトをいくつでも設定して使用できます。
たとえば、CHAR型の属性name、CHAR型のaddress、CHAR型のphonenumberおよびNUMBER型のemployeenumberを持つオブジェクト型EMPLOYEEを定義できます。
Oracleのオブジェクトのメソッド(ストアド・プロシージャ)は、オブジェクト型への対応付けができます。これらのメソッドは、staticメソッドまたはインスタンス・メソッドとして、PL/SQLまたはJavaで実装できます。メソッドのシグネチャとして、任意の数の入力、出力または入出力パラメータを使用できます。すべては初期定義によって決まります。
Oracle SQLコレクションは、次の2つのカテゴリに分類されます。
両カテゴリのコレクションは1次元ですが、複合オブジェクト型の要素を収容できます。VARRAY型は、1次元配列に対して使用します。一方、NESTED TABLE型は、表の中に別の表を入れ子にする場合に使用します。VARRAY型の変数を可変長配列と呼びます。NESTED TABLE型の変数をネストした表と呼びます。
VARRAYは、配列と同じように、データ要素の順序付けられた集合です。各要素に索引があり、すべての要素が同じデータ型です。VARRAYのサイズは、要素の最大数を示します。OracleのVARRAYは、名前が示すように可変サイズです。各VARRAY型の宣言時に、VARRAY型の最大サイズを指定する必要があります。
ネストした表は、順序付けされていない要素の集合です。表内のネストした表要素は、それ自体SQLで問合せを実行します。表と同じように、ネストした表の行数は、作成時に指定する必要はありません。行数は動的に算出されます。
|
注意: VARRAYの各要素またはネストした表の各行は、ユーザー定義のオブジェクト型にできます。したがって、ユーザー定義オブジェクト型の属性に対して、VARRAY型およびNESTED TABLE型を使用できます。Oracle Database 11g では、コレクション型のネストをサポートしています。VARRAYの要素またはネストした表の行は、別のVARRAY型またはNESTED TABLE型にしたり、VARRAY属性またはネストした表の属性を持つユーザー定義のオブジェクト型にできます。 |
Oracle Database 11g では、ユーザー定義のオブジェクトおよびコレクション定義は、SQLのデータ型定義として機能します。これらのデータ型は、他のデータ型と同じように、表の列定義、SQLオブジェクトの属性定義およびストアド・プロシージャまたはストアド・ファンクションのパラメータの定義に使用できます。また、オブジェクト型を定義すると、そのオブジェクト参照型を他のSQL参照型のように使用できます。
たとえば、前述のEMPLOYEE Oracleオブジェクトを考えます。このオブジェクトを定義すると、Oracleのデータ型になります。EMPLOYEE型の列は、NUMBER型の列と同じように、表に取り込むことができます。EMPLOYEE列の各行に、EMPLOYEEオブジェクト全体を格納できます。REF EMPLOYEE型の列も表に取り込めます。この列の内容は、EMPLOYEEオブジェクトへの参照です。
同様に、NUMBERのVARRAY(10)としてVARRAY型MYVARRを定義したり、CHAR(20)のNESTED TABLE型NTBLを定義できます。コレクション型MYVARRとNTBLはOracleのデータ型になるので、これらの型の列を表に定義できます。MYVARR列の各行は、最大10数字を収容する配列です。NTBL列の各行は20文字で構成されます。
カスタムJavaクラスはファーストクラスの型です。ユーザー定義SQL型に対する読込み/書込みは、ユーザーが意識することなく行われます。カスタムJavaクラスの用途は、SQLとJava間のデータの変換手段を提供し、データへのアクセスを可能にすることです。この機能は、特に、オブジェクトとコレクションのサポートにおいて重要です。データのカスタム変換を行う場合も、この機能が必要です。
SQLJアプリケーションで使用する各ユーザー定義型に対して、カスタムJavaクラスを用意することをお薦めします。 Oracle JDBCドライバでのデータ変換には、これらのカスタムJavaクラスのインスタンスを使用します。カスタムJavaクラスを使用した方が、弱い型指定のoracle.sql.STRUCT、oracle.sql.REFおよびoracle.sql.ARRAYクラスを使用するより便利であり、エラーも発生しにくくなるためです。
SQLJイテレータまたはホスト式で使用するために、カスタムJavaクラスではoracle.sql.ORADataおよびoracle.sql.ORADataFactoryインタフェースまたは標準java.sql.SQLDataインタフェースを実装する必要があります。ここでは、これらのインタフェースとカスタムJavaクラス機能の概要を説明します。次の項目について説明します。
ここでは、ORADataおよびORADataFactoryインタフェースと標準SQLDataインタフェースの指定について説明します。
Oracle Database 11g には、ユーザー定義型(oracle.sql.ORADataおよびoracle.sql.ORADataFactory)に対するOracle固有のカスタムJavaクラス機能をサポートするための一連のAPIが含まれています。
以前この機能のために使用されていたoracle.sql.CustomDatumおよびoracle.sql.CustomDatumFactoryインタフェースは廃止されました。
Oracleには、oracle.sql.ORADataインタフェースと関連するoracle.sql.ORADataFactoryインタフェースが用意されています。これらのインタフェースを使用して、Oracleのオブジェクト型、参照型およびコレクション型をカスタムJavaクラスにマッピングできます。
データの送信や取出しには、oracle.sql.Datumオブジェクトの形式を使用します。元のデータは、oracle.sql.Datumのサブクラス(oracle.sql.STRUCTなど)の形式です。このデータはSQL形式のままになっており、oracle.sql.Datumオブジェクトはラッパーとしてのみ機能します。
|
参照: 『Oracle Database JDBC開発者ガイドおよびリファレンス』 |
Java形式からSQL形式へのデータ変換に使用するtoDatum()メソッドは、ORADataインタフェースで指定します。このメソッドでは、接続オブジェクトを入力として見なし、データをoracle.sql.*表現に変換します。JDBCドライバは、実行時に接続オブジェクトを使用して、型チェックと型変換を行います。ORADataとtoDatum()は、次のように指定します。
interface oracle.sql.ORAData
{
oracle.sql.Datum toDatum(java.sql.Connection c) throws SQLException;
}
カスタムJavaクラスのインスタンスを作成し、SQL形式からJava形式に変換するcreate()メソッドは、ORADataFactoryインタフェースで指定します。このメソッドは、入力としてDatumオブジェクトを取ります。このオブジェクトには、データおよび型コード(基になるデータがSQL型であることを示すOracleTypes.RAWなど)があります。このメソッドで戻されるカスタムJavaクラスのオブジェクトには、ORADataインタフェースが実装されています。このオブジェクトは、入力されたDatumオブジェクトからデータを取得します。ORADataFactoryとcreate()は、次のように指定します。
interface oracle.sql.ORADataFactory
{
oracle.sql.ORAData create(oracle.sql.Datum d, int sqlType)
throws SQLException;
}
ORADataインタフェースとORADataFactoryインタフェース間の関係を確立するには、ORADataインタフェースを実装したカスタムJavaクラスに、static getORADataFactory()メソッドを実装する必要があります。このメソッドで戻されるオブジェクトは、ORADataFactoryインタフェースが実装されているので、カスタムJavaクラスのインスタンスを作成できます。 戻されたオブジェクト自体が、カスタムJavaクラスのインスタンスの場合もあります。この場合、Oracle JDBCドライバは、そのcreate()メソッドを使用して、カスタムJavaクラスのインスタンスを生成します。
|
注意: JPublisherの出力は、ORADataインタフェースおよびそのtoDatum()メソッドと、ORADataFactoryインタフェースおよびそのcreate()メソッドとを、1つのカスタムJavaクラスに実装できます。ただし、toDatum()とcreate()を別々のインタフェースに指定した場合は、それぞれを別個のクラスに実装することも可能になります。ORADataおよびそのtoDatum()メソッドと、getORADataFactory()メソッドを1つのカスタムJavaクラスに実装する一方で、ORADataFactoryおよびそのcreate()メソッドを別個のファクトリ・クラスに実装することも可能です。ここでは、両方のインタフェースを1つのクラスに実装する場合について説明します。 |
JPublisherを使用して-usertypes=oracleを指定すると、JPublisherのカスタムJavaクラスが生成されます。このクラスには、ORADataインタフェース、ORADataFactoryインタフェースおよびgetORADataFactory()メソッドが実装されます。下位互換性を保つために、JPublisherの-compatibleオプションとともに-usertypes=oracleを指定して、CustomDatumおよびCustomDatumFactoryインタフェースを使用することもできます。
|
参照: 『Oracle Database JPublisherユーザーズ・ガイド』 |
Oracle9i データベースでは、oracle.jdbc.driverクラスにかわってoracle.jdbcインタフェースが導入されています。そのため、カスタマイズされたオブジェクトの利用に以前使用されていたoracle.sql.CustomDatumおよびoracle.sql.CustomDatumFactoryインタフェースは廃止されました。かわりに、oracle.sql.ORADataおよびoracle.sql.ORADataFactoryインタフェースが使用されます。CustomDatumインタフェースのように、これらを標準SQLDataインタフェースのOracle固有の代替インタフェースとして使用できます。CustomDatumインタフェースは、下位互換性を保つために現在もサポートされています。
CustomDatumおよびCustomDatumFactoryは、次のように定義します。
public interface CustomDatum
{
oracle.sql.Datum toDatum(
oracle.jdbc.driver.OracleConnection conn
) throws SQLException;
public interface CustomDatumFactory
{
oracle.sql.CustomDatum create(
oracle.sql.Datum d, int sqlType
) throws SQLException;
}
接続connおよび型コードsqlTypeは、ORADataおよびORADataFactoryで説明されているように使用されます。 ただし、CustomDatumでは、標準Connection型ではなく、必ずOracle固有のOracleConnection型を使用してください。
標準JDBC 2.0には、構造化オブジェクト型をJavaクラスにマッピングし、変換するためのjava.sql.SQLDataインタフェースが提供されています。このインタフェースは、構造化オブジェクト型のみをマッピングの対象としているため、コレクションや配列などのSQL型はマッピングできません。
SQLDataインタフェースは、JDBC 2.0標準であり、readSQL()メソッドを指定してデータをJavaオブジェクトに読み込む一方で、writeSQL()メソッドを指定してJavaオブジェクトからデータベースに書き込みます。JPublisherを使用して-usertypes=jdbcを指定すると、JPublisherのカスタムJavaクラスが生成されます。このクラスには、SQLDataインタフェースが実装されています。
標準SQLData機能の追加詳細は、Sun社のJDBC 2.0以上のAPI仕様を参照してください。
Oracleオブジェクト・メソッドは、カスタムJavaクラスのラッパーから起動できます。基になるストアド・プロシージャの記述言語がPL/SQL、Javaのどちらであるか、またSQLに公開されているかどうかは、ユーザーには表示されません。
Javaのラッパー・メソッドでサーバー側メソッドを起動するには、サーバーと通信するための接続が必要です。接続オブジェクトは、パラメータとして明示的に指定することも、別の方法で対応付けることも可能です(カスタムJavaクラスの属性など)。ラッパー・メソッドで使用する接続オブジェクトを非静的属性とした場合、このラッパー・メソッドをカスタムJavaクラスのインスタンス・メソッドとすると、この接続にアクセスできます。JPublisherで生成したカスタムJavaクラスでは、この手法が採用されています。
Oracleオブジェクト・メソッドの出力および入出力パラメータに関しては、次のことに留意してください。ストアド・プロシージャ(SQLオブジェクト・メソッド)によって、ある引数の内部状態が変更された場合、ストアド・プロシージャに渡された実引数も変更されます。Javaで記述した場合、こうした現象は起こりません。JDBC出力パラメータがストアド・プロシージャ・コールから戻る場合、新規に作成されたオブジェクトに格納する必要があります。元のオブジェクト識別性は失われます。
出力または入出力パラメータをコール元に戻す方法としては、パラメータを配列要素として渡す方法もあります。入出力パラメータの場合は、ラッパー・メソッドが入力として配列要素をとります。処理後、ラッパーによって出力が配列要素に代入されます。JPublisherで生成したカスタムJavaクラスでは、この手法が使用されます。つまり、出力または入出力パラメータをそれぞれ単一要素配列で渡します。
JPublisherを使用すると、デフォルトではラッパー・メソッドが実装されます。生成されたクラスには、SQLDataインタフェースまたはORADataインタフェースが実装されており、このことが当てはまります。このデフォルトの機能を無効化するには、JPublisherの-methodsフラグをfalseに設定します。
|
参照: 『Oracle Database JPublisherユーザーズ・ガイド』 |
|
注意: カスタムJavaクラスを実装する場合、ラッパー・メソッドを実装する方法としていくつかの選択肢があります。サーバーでのデータ処理には、SQLオブジェクト・メソッドによって直接実行する場合と、クライアントからサーバーへオブジェクト値を転送して、サーバー上でメソッドを実行する場合があります。JPublisherでのラッパー・メソッドの実装および実装による効果については、「JPublisherで生成されるラッパー・メソッド」を参照してください。 |
カスタムJavaクラスに求められる要件としては、有効なホスト変数型としてOracle SQLJトランスレータで認識できることおよびトランスレータで型チェックできることが挙げられます。
|
注意: ユーザー定義型用のカスタムJavaクラスは、このマニュアルで「ラッパー・クラス」と呼ばれている場合が多くあります。 |
ORADataを実装したクラスに対するOracleの要件
ORADataを実装するためのOracleでの要件は、どのカスタムJavaクラスの場合にも基本的には同じです。ただし、クラスによるマッピング先が、オブジェクト、オブジェクト参照、コレクションまたはその他のSQL型のどれに該当するかによって、この要件は若干異なってきます。
この要件を次に示します。
oracle.sql.ORADataインタフェースを実装したクラスであること。
oracle.sql.ORADataFactoryオブジェクトを戻り値とするgetORADataFactory()メソッドを実装したクラスであること。メソッドのシグネチャは、次のように指定します。
public static oracle.sql.ORADataFactory getORADataFactory();
現在推奨されていないCustomDatumインタフェースを使用する場合、クラスでgetFactory()メソッドを実装します。このメソッドはoracle.sql.CustomDatumFactoryオブジェクトを戻します。メソッドのシグネチャは、次のように指定します。
public static oracle.sql.CustomDatumFactory getFactory();
クラスがString定数_SQL_TYPECODEを持つこと。この定数は、toDatum()が戻すDatumサブクラス・インスタンスのoracle.jdbc.OracleTypes型コードに初期化される必要があります。型コードを次に示します。
カスタム・オブジェクト・クラスの場合
public static final int _SQL_TYPECODE = OracleTypes.STRUCT;
カスタム参照クラスの場合
public static final int _SQL_TYPECODE = OracleTypes.REF;
カスタム・コレクション・クラスの場合
public static final int _SQL_TYPECODE = OracleTypes.ARRAY;
これ以外のクラスの場合は、それぞれ適切な型コードで初期化します。たとえば、カスタムJavaクラスで、RAWフィールドに対してJavaオブジェクトをシリアライズおよびシリアライズ解除するには、_SQL_TYPECODEをOracleTypes.RAWに初期化します。
|
注意: OracleTypesクラスは、各Oracleデータ型の型コード(整数の定数)のみを定義します。標準SQL型のOracleTypesエントリは、標準java.sql.Types型定義クラスのエントリと同じです。 |
_SQL_TYPECODEがSTRUCT、REFまたはARRAYのいずれかに該当する(つまり、オブジェクト、オブジェクト参照またはコレクションを表す)カスタムJavaクラスの場合、該当のユーザー定義型の名前を示す定数を保持していること。次に例を示します。
カスタム・オブジェクト・クラスおよびカスタム・コレクション・クラスのString定数_SQL_NAMEは、ユーザー定義型に対して宣言したSQL名に初期化する必要があります。次に例を示します。
public static final String _SQL_NAME = UDT name;
たとえば、ユーザー定義のPERSONオブジェクトのカスタム・オブジェクト・クラスの定数は次のようになります。
public static final String _SQL_NAME = "PERSON";
次のように、必要に応じて、スキーマで同様に指定できます。
public static final String _SQL_NAME = "SCOTT.PERSON";
PERSONオブジェクトのコレクションをPERSON_ARRAYと宣言した場合のカスタム・コレクション・クラスの定数は、次のようになります。
public static final String _SQL_NAME = "PERSON_ARRAY";
カスタム参照クラスのString定数_SQL_BASETYPEは、参照先のユーザー定義型に対して宣言したSQL名に初期化する必要があります。
public static final String _SQL_BASETYPE = UDT name;
PERSON参照のカスタム参照クラスの定数は、次のようになります。
public static final String _SQL_BASETYPE = "PERSON";
前述以外のORADataで使用する場合、UDT名の指定はありません。
使用時には次の事項に注意してください。
コレクション型の名前は、ベース型ではなく、コレクション型を表す名前にします。たとえば、PERSONオブジェクトに対し、VARRAYまたはNESTED TABLE型のPERSON_ARRAYを宣言した場合は、_SQL_NAMEエントリに指定するコレクション型の名前は、PERSONではなく、PERSON_ARRAYになります。
SQL型を_SQL_NAMEフィールドに指定するときに、SQL型が大/小文字を区別して(引用符で囲んで)宣言されている場合、CaseSensitiveやSCOTT.CaseSensitiveなど、宣言されているとおりにSQL名を指定する必要があります。これは、引用符内で同様に名前の大/小文字を区別するJPublisher入力ファイルでの使用方法と異なります。SQL型を大/小文字を区別して宣言していない場合(引用符なし)、ADDRESSやSCOTT.ADDRESSのように、すべて大文字でSQL名を指定する必要があります。
JPublisherでは、大/小文字の区別とJPublisherの-omit_schema_names設定がある場合はそれに従って、自動的にこのフィールドの値を生成します。
SQLDataを実装したクラスに対する要件
ISO SQLJ規格では、SQLDataインタフェースを実装するクラスへの型マップ定義の要件を概説しています。一方、SQLDataラッパー・クラスは、public static finalフィールドでSQL関連オブジェクト型を識別できます。
次の点は重要なので、十分に注意してください。
マッピングの指定に型マップを使用する場合でも、標準でないpublic static finalフィールドを使用する場合でも、一貫して指定する必要があります。public static finalフィールドを不要にする目的ですべての関連マッピングを指定する型マップを使用するか、または型マップをまったく使用せずにすべてのマッピングをpublic static finalフィールドで指定します。
SQLDataは、ORADataとは異なり、構造化オブジェクト型のマッピング専用です。オブジェクト参照やコレクションや配列などのSQL型のマッピングには、SQLDataを使用できません。ORADataを使用しない場合、オブジェクト参照とコレクションをマッピングする方法は、それぞれ弱い型指定のjava.sql.Refとjava.sql.Array、またはoracle.sql.REFとoracle.sql.ARRAYを使用する方法のみです。
SQL型からJava型へのマッピングを指定するとき、SQL型が大/小文字を区別する方法で宣言されている場合、CaseSensitiveまたはSCOTT.CaseSensitiveなど宣言されているとおりにSQL名を指定する必要があります。これは、引用符内で同様に名前の大/小文字を区別するJPublisher入力ファイルでの使用方法と異なります。SQL型を大/小文字を区別して宣言していない場合、ADDRESSやSCOTT.ADDRESSのように、すべて大文字でSQL名を指定する必要があります。
型マップ・リソースで指定したマッピング
まず、ISO SQLJ規格に従ったマッピング表現を想定します。Address、pack.Personおよびpack.Manager.InnerPM(InnerPMはManagerの内部クラスです)がjava.sql.SQLDataを実装している3つのラッパー・クラスであるとします。
次の点を考慮する必要があります。
これらのクラスは、宣言済の接続コンテキスト型の明示的接続コンテキスト・インスタンスを使用する文でのみ採用する必要があります。たとえば、この型をSDContextと呼ぶとします。
Address a =...;
pack.Person p =...;
pack.Manager.InnerPM pm =...;
SDContext ctx = new SDContext(url,user,pwd,false);
#sql [ctx] { ... :a ... :p ... :pm ... };
接続コンテキスト型は、java.util.PropertyResourceBundleを実装している関連クラスを指定するwith属性のtypeMapで宣言されている必要があります。前述の例では、SDContextが次のように宣言されています。
#sql public static context SDContext with (typeMap="SDMap");
型マップのリソースは、SQLオブジェクト型からjava.sql.SQLDataインタフェースを実装するJavaクラスにマッピングしている必要があります。このマッピングは、次の形式のエントリで指定します。
class.java_class_name=STRUCT sql_type_name
STRUCTキーワードは省略可能です。例では、SDMap.propertiesリソース・ファイルに次のエントリがあります。
class.Address=STRUCT SCOTT.ADDRESS class.pack.Person=PERSON class.pack.Manager$InnerPM=STRUCT PRODUCT_MANAGER
パッケージとクラス名の分割にはピリオド(.)を使用しますが、内部クラス名を分割するにはドル記号($)を使用する必要があります。
|
重要: デフォルトのOracle固有コード生成を使用している場合、コンテキスト型がSDContextの文に使用されるイテレータにも同じ型マップSDMapが宣言されていることが必要です。次に例を示します。
#sql public static iterator SDIter with (typeMap="SDMap");
...
SDContext sdctx = ...
SDIter sditer;
#sql [sdctx] sditer = { SELECT ...};
これにより、イテレータ・クラスに正しいコードが生成されたことを確認します。 |
マッピングを型マップ・リソースに指定するこのメカニズムは、非標準の方法よりも複雑になっています。さらに、型マップ・リソースをデフォルトの接続コンテキストと関連付けることはできません。すべてのマッピング情報が1つの場所、型マップ・リソースに置かれるというメリットがあります。つまり、コンパイル済アプリケーション上の型マッピングは、後で簡単に調整できます。たとえば、新規SQL型およびJavaラッパーを拡張SQL-Java型階層に含めることができます。
次の点に注意してください。
この機能を使用するには、SQLJ runtime12またはruntime12eeライブラリを採用する必要があります。型マップはjava.util.Mapオブジェクトで表されます。これらは、SQLJランタイムAPIで公開されていますが、汎用ランタイム・ライブラリではサポートされていません。
SQLDataラッパー・クラスがSQLJ文のOUTまたはINOUTパラメータに現れた場合、Oracle SQLJランタイムおよびOracle固有コード生成またはプロファイルのカスタマイズを使用する必要があります。 これは、Oracle JDBCがそのようなパラメータのSQL型をregisterOutParameter()で必要とするためです。さらに、OUTパラメータ型を登録するため、SQL型は変換中に有力な型マップにより凍結されます。
SQLJ型マップは基礎となる接続で使用しているJDBC型マップとは独立しています。このため、SQLDataラッパーを使用しているSQLJコードおよびJDBCコードを混在させている場合は、注意が必要です。しかし、指定されたSQLJ接続コンテキスト上の有効な型マップは、簡単に抽出できます。
ctx.getTypeMap();
ラッパー・クラスの静的フィールドで指定されたマッピング
SQLDataを実装しているクラスは、次の非標準要件を満たすことができます。
JavaクラスはString定数_SQL_NAMEを宣言します。これには、JavaクラスによってラップされるSQL型名を定義します。例として、Addressクラスには次のようなフィールド定義があります。
public static final String _SQL_NAME="SCOTT.ADDRESS";
次の宣言はpack.Person内にあります。
public static final String _SQL_NAME="PERSON";
また、pack.Manager.InnerPMクラスには次の要素があります。
public static final String _SQL_NAME="PRODUCT_MANAGER";
JPublisherでは、常に_SQL_NAMEフィールドとともにSQLDataラッパー・クラスが生成されます。ただし、このフィールドは型マップを参照するSQLJ文では無視されます。
|
注意:
|
SQLJのコマンドラインには、アプリケーションの.sqljファイルと一緒に、カスタムJavaクラス(ORADataまたはSQLDataを実装したクラス)の.javaファイルを指定できます。ただし、この指定が必要ではない場合もあります。それは、SQLJの-checksourceフラグをtrue(デフォルト)に設定し、CLASSPATHにカスタムJavaソースの保存ディレクトリを指定した場合です。
|
注意: ここでは、カスタム・オブジェクトとカスタム・コレクション用に、.sqljファイルではなく.javaファイルを作成していることを想定しています。.sqljファイルは、SQLJコマンドラインで指定される必要があります。 |
たとえば、ObjectDemo.sqljでOracleのオブジェクト型ADDRESSおよびPERSONを使用し、これらのオブジェクトのカスタムJavaクラスが生成されている場合は、次のようにSQLJを実行できます。
-checksource=trueを指定し、CLASSPATHにカスタムJavaソースの保存ディレクトリを指定した場合は、次のようにします。
% sqlj ObjectDemo.sqlj
-checksource=falseを指定した場合は、次のようにします(この行は、全体を1行で入力します)。
% sqlj ObjectDemo.sqlj Address.java AddressRef.java Person.java PersonRef.java
また、Javaコンパイラを使用してカスタム.javaソース・ファイルを直接コンパイルすることもできます。この場合は、.sqljファイルの変換前に、コンパイルする必要があります。
|
注意: ORAData実装はOracle固有の機能を必要とするので、トランスレータの移植設定を-warn=noportable(デフォルト)に設定する必要があります。この設定にしない場合、移植性に関する警告がいくつか通知されます。-warnのフラグの詳細は、「トランスレータからの警告(-warn)」を参照してください。 |
カスタムJavaクラス・インスタンスを使用すると、Oracle SQLJおよびJDBC実装では、ユーザー定義型の読込みと書込みが可能になります(ただし、これらは組込み型です)。この仕組みをユーザーが意識することはありません。
ORAData実装およびSQLData実装でのデータの読込み/書込みの仕組みは、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。
ここまでは、カスタムJavaクラスについて、次のような使用例を取り上げてきました。
SQLオブジェクトのラッパー: このカスタム・オブジェクト・クラスは、oracle.sql.STRUCTインスタンスに対して使用します。
SQL参照のラッパー: このカスタム参照クラスは、oracle.sql.REFインスタンスに対して使用します。
SQLコレクションのラッパー: このカスタム・コレクション・クラスは、oracle.sql.ARRAYインスタンスに対して使用します。
これ以外のoracle.sql.*型をラッピングする場合でも、カスタムJavaクラスを用意すると独自の変換または処理を行うのに便利です。このためには、ORADataを実装したクラスを使用してください(SQLDataは不使用)。次に例を示します。
データの暗号化と復号化、またはデータの妥当性チェックを行う場合
読込み後または書込み前の値をロギングする場合
CHAR列(URL情報を格納した文字フィールドなど)をより小さいコンポーネントに解析する場合
文字列を数値定数にマッピングする場合
データをより適切なJava形式にマッピングする場合(DATEフィールドをjava.util.Date形式にマッピングする場合など)
データ表現をカスタマイズする場合(選択した表のフィート単位データをメートル単位で表現する場合など)
Javaオブジェクトをシリアライズおよびシリアライズ解除する場合(RAWフィールドなどに対して)
|
注意: このような機能は、SQLDataインタフェースを使用しても実現されません。SQLData実装でラッピング対象となるのは構造化オブジェクト型のみに限られているためです。 |
ORADataの一般的な使用方法: BetterDate.java
ここでは、ORADataインタフェースを実装したクラスを使用して、Javaの日付をカスタマイズする方法を示します。これは、java.sql.Dateのかわりに使用できます。
|
注意: これは、完全なアプリケーションではありません。main()メソッドはありません。 |
import java.util.Date;
import oracle.sql.ORAData;
import oracle.sql.DATE;
import oracle.sql.ORADataFactory;
import oracle.jdbc.OracleTypes;
// a Date class customized for user's preferences:
// - months are numbers 1..12, not 0..11
// - years are referred to through four-digit numbers, not two.
public class BetterDate extends java.util.Date
implements ORAData, ORADataFactory {
public static final int _SQL_TYPECODE = OracleTypes.DATE;
String[]monthNames={"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
String[]toDigit={"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
static final BetterDate _BetterDateFactory = new BetterDate();
public static ORADataFactory getORADataFactory() { return _BetterDateFactory;}
// the current time...
public BetterDate() {
super();
}
public oracle.sql.Datum toDatum(java.sql.Connection conn) {
return new DATE(toSQLDate());
}
public oracle.sql.ORAData create(oracle.sql.Datum dat, int intx) {
if (dat==null) return null;
DATE DAT = ((DATE)dat);
java.sql.Date jsd = DAT.dateValue();
return new BetterDate(jsd);
}
public java.sql.Date toSQLDate() {
java.sql.Date retval;
retval = new java.sql.Date(this.getYear()-1900, this.getMonth()-1,
this.getDate());
return retval;
}
public BetterDate(java.sql.Date d) {
this(d.getYear()+1900, d.getMonth()+1, d.getDate());
}
private static int [] deconstructString(String s) {
int [] retval = new int[3];
int y,m,d; char temp; int offset;
StringBuffer sb = new StringBuffer(s);
temp=sb.charAt(1);
// figure the day of month
if (temp < '0' || temp > '9') {
m = sb.charAt(0)-'0';
offset=2;
} else {
m = (sb.charAt(0)-'0')*10 + (temp-'0');
offset=3;
}
// figure the month
temp = sb.charAt(offset+1);
if (temp < '0' || temp > '9') {
d = sb.charAt(offset)-'0';
offset+=2;
} else {
d = (sb.charAt(offset)-'0')*10 + (temp-'0');
offset+=3;
}
// figure the year, which is either in the format "yy" or "yyyy"
// (the former assumes the current century)
if (sb.length() <= (offset+2)) {
y = (((new BetterDate()).getYear())/100)*100 +
(sb.charAt(offset)- '0') * 10 +
(sb.charAt(offset+1)- '0');
} else {
y = (sb.charAt(offset)- '0') * 1000 +
(sb.charAt(offset+1)- '0') * 100 +
(sb.charAt(offset+2)- '0') * 10 +
(sb.charAt(offset+3)- '0');
}
retval[0]=y;
retval[1]=m;
retval[2]=d;
// System.out.println("Constructing date from string as: "+d+"/"+m+"/"+y);
return retval;
}
private BetterDate(int [] stuff) {
this(stuff[0], stuff[1], stuff[2]);
}
// takes a string in the format: "mm-dd-yyyy" or "mm/dd/yyyy" or
// "mm-dd-yy" or "mm/dd/yy" (which assumes the current century)
public BetterDate(String s) {
this(BetterDate.deconstructString(s));
}
// years are as '1990', months from 1..12 (unlike java.util.Date!), date
// as '1' to '31'
public BetterDate(int year, int months, int date) {
super(year-1900,months-1,date);
}
// returns "Date: dd-mon-yyyy"
public String toString() {
int yr = getYear();
return getDate()+"-"+monthNames[getMonth()-1]+"-"+
toDigit[(yr/1000)%10] +
toDigit[(yr/100)%10] +
toDigit[(yr/10)%10] +
toDigit[yr%10];
// return "Date: " + getDate() + "-"+getMonth()+"-"+(getYear()%100);
}
public BetterDate addDays(int i) {
if (i==0) return this;
return new BetterDate(getYear(), getMonth(), getDate()+i);
}
public BetterDate addMonths(int i) {
if (i==0) return this;
int yr=getYear();
int mon=getMonth()+i;
int dat=getDate();
while(mon<1) {
--yr;mon+=12;
}
return new BetterDate(yr, mon,dat);
}
// returns year as in 1996, 2007
public int getYear() {
return super.getYear()+1900;
}
// returns month as 1..12
public int getMonth() {
return super.getMonth()+1;
}
public boolean equals(BetterDate sd) {
return (sd.getDate() == this.getDate() &&
sd.getMonth() == this.getMonth() &&
sd.getYear() == this.getYear());
}
// subtract the two dates; return the answer in whole years
// uses the average length of a year, which is 365 days plus
// a leap year every 4, except 100, except 400 years =
// = 365 97/400 = 365.2425 days = 31,556,952 seconds
public double minusInYears(BetterDate sd) {
// the year (as defined in the preceding text) in milliseconds
long yearInMillis = 31556952L;
long diff = myUTC()-sd.myUTC();
return (((double)diff/(double)yearInMillis)/1000.0);
}
public long myUTC() {
return Date.UTC(getYear()-1900, getMonth()-1, getDate(),0,0,0);
}
// returns <0 if this is earlier than sd
// returns = if this == sd
// else returns >0
public int compare(BetterDate sd) {
if (getYear()!=sd.getYear()) {return getYear()-sd.getYear();}
if (getMonth()!=sd.getMonth()) {return getMonth()-sd.getMonth();}
return getDate()-sd.getDate();
}
}
ここでは、Oracle Database 11g のユーザー定義オブジェクト型とユーザー定義コレクション型の作成例および使用例を示します。
オブジェクト型を作成するSQLコマンドは、次の形式で指定します。
CREATE TYPE typename AS OBJECT ( attrname1 datatype1, attrname2 datatype2, ... ... attrnameN datatypeN );
typename で、オブジェクト型の名前を指定します。attrname1 からattrnameN で、属性名を指定します。datatype1 からdatatypeN で、属性のデータ型を指定します。
次に、Oracle Database 11g におけるユーザー定義オブジェクト型の作成例を示します。
この例では、SQLを使用して次の項目を作成します。
2つのオブジェクト型、PERSONとADDRESS。
PERSONオブジェクト用の型付けされた表。
EMPLOYEES表。ADDRESS列が1列とPERSON参照列が2列あります。
これらの項目を作成するスクリプトを次に示します。
/*** Using user-defined types (UDTs) in SQLJ ***/
/
/*** Create ADDRESS UDT ***/
CREATE TYPE ADDRESS AS OBJECT
(
street VARCHAR(60),
city VARCHAR(30),
state CHAR(2),
zip_code CHAR(5)
)
/
/*** Create PERSON UDT containing an embedded ADDRESS UDT ***/
CREATE TYPE PERSON AS OBJECT
(
name VARCHAR(30),
ssn NUMBER,
addr ADDRESS
)
/
/*** Create a typed table for PERSON objects ***/
CREATE TABLE persons OF PERSON
/
/*** Create a relational table with two columns that are REFs
to PERSON objects, as well as a column which is an Address ADT. ***/
CREATE TABLE employees
(
empnumber INTEGER PRIMARY KEY,
person_data REF PERSON,
manager REF PERSON,
office_addr ADDRESS,
salary NUMBER
)
/*** Insert some data--2 objects into the persons typed table ***/
INSERT INTO persons VALUES (
PERSON('Wolfgang Amadeus Mozart', 123456,
ADDRESS('Am Berg 100', 'Salzburg', 'AT','10424')))
/
INSERT INTO persons VALUES (
PERSON('Ludwig van Beethoven', 234567,
ADDRESS('Rheinallee', 'Bonn', 'DE', '69234')))
/
/** Put a row in the employees table **/
INSERT INTO employees (empnumber, office_addr, salary) VALUES (
1001,
ADDRESS('500 Oracle Parkway', 'Redwood Shores', 'CA', '94065'),
50000)
/
/** Set the manager and PERSON REFs for the employee **/
UPDATE employees
SET manager =
(SELECT REF(p) FROM persons p WHERE p.name = 'Wolfgang Amadeus Mozart')
/
UPDATE employees
SET person_data =
(SELECT REF(p) FROM persons p WHERE p.name = 'Ludwig van Beethoven')
|
注意: Oracle SQL実装では、特にユーザー定義型の表にアクセスする場合に、表の別名(この例のpなど)の使用を習慣付けることをお薦めします。オブジェクトの属性にアクセスする場合は、この構文を使用する必要があります。別名を使用する必要のない場合でも、別名の使用によって明確化できます。表の別名の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。 |
コレクションは、次の2つの種類があります。
可変長配列(VARRAY)
ネストした表(NESTED TABLE)
VARRAY型を作成するSQLコマンドは、次の形式で指定します。
CREATE TYPE typename IS VARRAY(n) OF datatype;
typename でVARRAY型の名前を、n で配列内の最大要素数を、またdatatype で配列要素のデータ型を指定します。次にその例を示します。
CREATE TYPE myvarr IS VARRAY(10) OF INTEGER;
NESTED TABLE型を作成するSQLコマンドは、次の形式で指定します。
CREATE TYPE typename AS TABLE OF datatype;
typename でネストした表の型の名前を、datatype で表要素のデータ型を指定します。データ型は標準データ型またはユーザー定義型を指定できます。ネストした表は1列のみに限られています。ただし、1つの列型を、複数の属性を持つ複合オブジェクトにすることも可能です。ネストした表には、データベース表と同様に、行数制限がありません。次にその例を示します。
CREATE TYPE person_array AS TABLE OF person;
このCREATE TYPEコマンドを指定すると、PERSONオブジェクトから構成された各行にNESTED TABLE型が作成されます。
次に、Oracle Database 11g におけるユーザー定義のコレクション型およびオブジェクト型の作成例を示します。
SQLを使用して次の項目が作成および設定されます。
2つのオブジェクト型PARTICIPANT_TおよびMODULE_T
コレクション型MODULETBL_T(MODULE_Tオブジェクトのネストした表)
PARTICIPANT_T参照の列およびネストした表のMODULETBL_T列を含むPROJECTS表
コレクション型PHONE_ARRAY(VARCHAR2(30)のVARRAY)
PERSONとADDRESSオブジェクト(「オブジェクト型の作成」に同じ定義が示されています)
EMPLOYEES表(PHONE_ARRAY列を含む)
これらの項目を作成するスクリプトを次に示します。
Rem This is a SQL*Plus script used to create schema to demonstrate collection Rem manipulation in SQLJ CREATE TYPE PARTICIPANT_T AS OBJECT ( empno NUMBER(4), ename VARCHAR2(20), job VARCHAR2(12), mgr NUMBER(4), hiredate DATE, sal NUMBER(7,2), deptno NUMBER(2)) / SHOW ERRORS CREATE TYPE MODULE_T AS OBJECT ( module_id NUMBER(4), module_name VARCHAR2(20), module_owner REF PARTICIPANT_T, module_start_date DATE, module_duration NUMBER ) / SHOW ERRORS CREATE TYPE MODULETBL_T AS TABLE OF MODULE_T; / SHOW ERRORS CREATE TABLE projects ( id NUMBER(4), name VARCHAR(30), owner REF PARTICIPANT_T, start_date DATE, duration NUMBER(3), modules MODULETBL_T ) NESTED TABLE modules STORE AS modules_tab; SHOW ERRORS CREATE TYPE PHONE_ARRAY IS VARRAY (10) OF varchar2(30) / /*** Create ADDRESS UDT ***/ CREATE TYPE ADDRESS AS OBJECT ( street VARCHAR(60), city VARCHAR(30), state CHAR(2), zip_code CHAR(5) ) / /*** Create PERSON UDT containing an embedded ADDRESS UDT ***/ CREATE TYPE PERSON AS OBJECT ( name VARCHAR(30), ssn NUMBER, addr ADDRESS ) / CREATE TABLE employees ( empnumber INTEGER PRIMARY KEY, person_data REF person, manager REF person, office_addr address, salary NUMBER, phone_nums phone_array ) /
Oracleでは、Oracleのオブジェクト型、参照型およびコレクション型を強い型指定のパラダイムのJavaクラスに柔軟にマッピングできます。開発者は、これらのカスタムJavaクラスを次の方法で作成できます。
OracleのJPublisherユーティリティでカスタムJavaクラスを自動生成し、このクラスを変更せずにそのまま使用する方法
JPublisherでカスタムJavaクラスと対応するサブクラス(後でユーザーが機能を変更できる)を自動生成する方法
JPublisherを使用せずに、手動でカスタムJavaクラスをコーディングする方法(「カスタムJavaクラスの要件」の要件を満たすクラスの場合のみ)
カスタムJavaクラスは手動でもコーディングできますが、JPublisherで生成されたクラスを直接使用すること、またはJPublisherで生成されたサブクラスを変更することをお薦めします。
JPublisherでは、Oracle oracle.sql.ORADataまたは標準java.sql.SQLDataインタフェースを実装したカスタム・オブジェクト・クラスを生成できます。このうちのORAData実装をJPublisherで生成した場合は、カスタム参照クラスも一緒に生成されます。現行より古いJDBCバージョンとの互換性のために、JPublisherで、現在は使用されていないoracle.sql.CustomDatumインタフェースを実装するクラスを生成することもできます。
SQLDataインタフェースは、カスタム参照クラスやカスタム・コレクション・クラスには実装できません。これから開発するコードを移植可能にする方法は、弱い型指定の標準java.sql.Refオブジェクトとjava.sql.Arrayオブジェクトを、それぞれ参照とコレクションにマッピングする方法のみです。
ここでは、次の項目について説明します。
|
参照:
|
JPublisherを使用してカスタムJavaクラスを生成する場合は、ORAData(カスタム・オブジェクト・クラス、カスタム参照クラスまたはカスタム・コレクション・クラス用)またはSQLData(カスタム・オブジェクト・クラス専用)実装を使用できます。ORAData実装には、ORADataFactoryインタフェース(カスタムJavaクラスのインスタンス生成に必要なインタフェース)も実装できます。
JPublisherの-usertypesオプションの設定内容に応じて、実装の対象が決まります。-usertypes=oracle設定ではORAData実装が指定され、-usertypes=jdbc設定ではSQLData実装が指定されます。
ORAData実装
ユーザー定義オブジェクト型を処理対象としてJPublisherを実行し、カスタム・オブジェクト・クラスに対してORAData実装を使用すると、次のようなクラスが自動的に生成されます。
カスタム・オブジェクト・クラス(通常は.sqljソース・ファイル内)。Oracleのオブジェクト型に対応する型定義です。
このクラスには、各属性のアクセッサ・メソッドがあります。たとえば、getFoo()およびsetFoo()は、属性fooのアクセッサ・メソッドです。JPublisherのデフォルトでは、サーバー側Oracleオブジェクトのメソッドを起動するラッパー・メソッドもクラスに生成されます。ただし、-methods=falseを設定すると、このラッパー・メソッドを生成しないように指定できます。この場合、JPublisherは、ラッパー・メソッドを生成せずに、カスタム・オブジェクト用に.sqljファイルではなく.javaファイルを作成します。
Oracleオブジェクト型へのオブジェクト参照にアクセスするためのカスタム参照クラス。
このカスタム参照クラスのgetValue()メソッドは、カスタム・オブジェクト・クラスのインスタンスを戻り値としており、もう1つのsetValue()メソッドは、カスタム・オブジェクト・クラスのインスタンスを入力として取り、データベース内のオブジェクトの値を更新します。
SQLオブジェクト型で参照を使用しているかどうかにかかわらず、強い型指定の参照クラスが常に生成されます。
最上位オブジェクトのオブジェクト属性またはコレクション属性にアクセスするためのカスタム・クラス。
Javaでは、最上位クラスのインスタンスを作成するとき、このクラスを使用して属性を作成します。
ユーザー定義コレクション型を処理対象としてJPublisherを実行し、ORADataを実装するように指定すると、次のようなクラスが自動的に生成されます。
カスタム・コレクション・クラス。指定したOracleコレクション型に対応する型定義として機能します。
このカスタム・コレクション・クラスのオーバーロード・メソッドgetArray()およびsetArray()は、コレクション単位で取得や更新を行うメソッドであり、getElement()メソッドおよびsetElement()メソッドは、コレクションの要素単位で取得や更新を行うメソッドです。このクラスには、これ以外のユーティリティ・メソッドもあります。
要素のカスタム・オブジェクト・クラス(コレクションの要素がオブジェクトの場合)。
Javaでは、コレクションのインスタンスを作成するときに、このクラスを使用してオブジェクト要素を作成します。
JPublisherで生成したこの2種類のカスタムJavaクラスには、ORADataインタフェース、ORADataFactoryインタフェースおよびgetORADataFactory()メソッドが実装されます。
|
注意:
|
ORAData実装のための強い型指定のオブジェクト参照
OracleのORADataを実装する場合、JPublisherでは、弱い型指定のoracle.sql.REFクラスを使用するのではなく、常に強い型指定のオブジェクト参照クラスを生成します。これによって、オブジェクト参照が強い型指定されている場合、型の安全性が向上し、SQLの動作がミラー化されます。強い型指定のクラス(PERSONオブジェクトを参照するPersonRefなどの名前とともに使用)は、基本的にはREFクラス用のラッパーです。
これらの強い型指定のREFラッパーには、参照されるSQLオブジェクトのインスタンスを該当するJavaクラスのインスタンスの形式で生成するgetValue()メソッドがあります。(継承する場合は、該当するJavaクラスのサブクラスのインスタンスとして生成します。)たとえば、PERSON SQLオブジェクト型とPerson Javaクラスがある場合、PersonRef Javaクラスもあります。PersonRefクラスのgetValue() メソッドによって、データベース内にPERSONオブジェクト用のデータを含むPersonインスタンスが戻されます。
SQLオブジェクト型にオブジェクト参照の属性がある場合、そのオブジェクト型に対応するJavaクラスには、該当の参照型に対応するJavaクラスのインスタンスの属性があります。たとえば、MANAGER REF属性のPERSONオブジェクトがある場合、対応するPerson JavaクラスはManagerRef属性を持ちます。
SQLData実装
ユーザー定義型のオブジェクト型を処理対象としてJPublisherを実行し、カスタム・オブジェクト・クラスに対してSQLData実装を選択する場合、Oracleオブジェクト型に対応する型定義として機能するカスタム・オブジェクト・クラスが生成されます。このカスタム・コレクション・クラスに属するものを次に示します。
各属性のアクセッサ・メソッド
標準SQLDataインタフェースのreadSQL()メソッドとwriteSQL()メソッドの実装
サーバー側で実行されるOracleオブジェクト・メソッドを起動するラッパー・メソッド(JPublisher実行時の設定が-methods=false以外の場合)
ただし、SQLDataインタフェースはオブジェクトのみを対象とし、参照やコレクションには使用できないため、Oracleオブジェクト型を参照するためのカスタム参照クラスについては、JPublisherでは生成の対象とはしていません。弱い型指定の標準java.sql.Refインスタンスまたはoracle.sql.REFインスタンスを使用する場合、移植性を気にする必要はありません。REFインスタンスは、カスタム参照クラスのインスタンスと同様に、Oracle拡張メソッドgetValue()およびsetValue()を使用して、参照するオブジェクトのインスタンスに対して読込みと書込みを行います。標準Refインスタンスには、こうした読込みと書込みの機能はありません。
カスタム・コレクション・クラスに対してはSQLDataを実装できないため、弱い型指定の標準java.sql.Arrayインスタンスを使用するか、または移植性が必要でなければoracle.sql.ARRAYインスタンスを使用してください。ArrayインスタンスとARRAYインスタンスは、カスタム・コレクション・クラスのインスタンスと同様に、getArray()機能を備えているため、コレクション単位での読込みはできますが、要素レベルでのアクセスや書込みはできません。このアクセスや書込みには、カスタム・コレクション・クラスのgetElement()メソッドとsetElement()メソッドを使用する必要があります。
|
注意: JDBCの仕様では、SQLDataインタフェースが移植可能であると定義されています。 ただし、JPublisherで生成されるSQLData実装を移植可能とするには、Oracle固有の機能とOracle型のマッピングを使用しないようにします(これらを使用した場合は、Oracle固有のoracle.sql.*クラスが使用されるためです)。 |
ここでは、JPublisherのコマンドラインの主要機能について説明します。主要機能として、Javaにマッピングするユーザー定義型を指定する機能、およびオブジェクト・クラス名、コレクション・クラス名、属性型のマッピングおよびラッパー・メソッドを指定する機能があります。次に、要点をまとめます。
JPublisherの-usertypesオプションを介して、使用する実装を指定します。
Javaにマッピングするユーザー定義型の指定。使用するJPublisherのカスタム・オブジェクト・クラスとカスタム・コレクション・クラスの名前を指定することも、デフォルトの名前を使用することも可能です。必要に応じてJPublisherの-sql、-userおよび-caseオプションを使用します。
オプションとして、-numbertypes、-builtintypesおよび-lobtypesなどのJPublisherの-xxx typesオプションを介して属性型マッピングを指定します。
ラッパー・メソッド(Oracleオブジェクト・メソッドなど)の作成要否も指定できます。デフォルトでは作成されますが、JPublisherの-methodsフラグでの指定も可能です。
|
注意: これ以降は、カスタム参照クラスやカスタム・コレクション・クラスを簡潔に説明するために、ORAData実装についてのみ記述します。 |
生成されたクラスへの実装の選択
JPublisherを実行する前に、OracleのORADataインタフェースと標準SQLDataインタフェースのどちらを生成されたクラスに実装するかを検討します。SQLDataを使用すると、コードの移植性が高くなりますが、ORADataを使用すると、型マップが不要になるなどいくつかのメリットがあります。
次の点に注意してください。
カスタム・コレクション・クラスには、ORAData実装を使用する必要があります。SQLDataインタフェースではコレクションをサポートしていません。
強い型指定の参照クラスでは、常にORADataカスタム・オブジェクト・クラスの実装が生成され、SQLDataカスタム・オブジェクト・クラスの実装は生成されません。SQLDataインタフェースは、強い型指定のオブジェクト参照をサポートしていません。かわりに、弱い型指定のjava.sql.Ref型またはoracle.sql.REF型を使用してください。
JPublisher -usertypesオプションを使用して、クラスに実装するインタフェースを指定します。-usertypes=oracle設定(デフォルト)ではORADataインタフェースが指定され、-usertypes=jdbc設定ではSQLDataインタフェースが指定されます。
次に示すJPublisherのコマンドラインは、それぞれORAData、CustomDatumおよびSQLDataを実装する例です(%はシステム・プロンプトです)。
% jpub -usertypes=oracle ... <other option settings> % jpub -usertypes=oracle -compatible=customdatum ... <other option settings> % jpub -usertypes=jdbc ... <other option settings>
-usertypes=jdbcの場合、-compatible=customdatumまたは-compatible=oradata設定はJPublisherでは無視されます。
JPublisherでカスタムJavaクラスを作成する場合は、Javaにマッピングするユーザー定義SQL型を-sqlオプションで指定します。カスタム・オブジェクト・クラスとカスタム・コレクション・クラスの名前を指定することも、デフォルト名を使用することも可能です。
最上位カスタム・クラス(-sqlオプションで名前を指定したユーザー定義型に対応するクラス)のデフォルト名には、JPublisherコマンドラインで入力したユーザー定義型の名前が使用されます。データベース内のSQL名はデフォルトでは大/小文字が区別されないため、SQL名を大文字で開始することによって、Java規則に従ってクラス名を大文字で開始できます。たとえば、employeeオブジェクトのカスタム・クラスを作成するには、次のようにJPublisherを実行します。
% jpub -sql=Employee ...
employeeオブジェクトの属性であるhome_addressオブジェクトなど、他のクラスのデフォルト名は、JPublisherの-caseオプションで指定します。-caseオプションを指定しないと、mixedに設定されます。つまり、カスタム・クラスのデフォルト名は、ユーザー定義型名の頭文字と以降の各ワードの頭文字が大文字になります。JPublisherでは、アンダースコア(_)、ドル記号($)、およびJavaの識別子で無効な文字は、ワード間のセパレータとしてみなされます。これらの文字は処理時に破棄されます。
たとえば、Oracleのオブジェクト型home_addressの場合は、クラスHomeAddressがHomeAddress.sqljまたは.javaのソース・ファイルに生成されます。
JPublisherのコマンドラインでは、-sqlオプションに次の構文を使用します。
-sql=udt1<:mapclass1><,udt2<:mapclass2>>,...,<udtN<:mapclassN>> ...
単一のオプション設定で複数のアクションを指定できることに注意してください。
データベース・スキーマを指定するには、-userオプションを使用します。次に例を示します。
% jpub -sql=Myobj,mycoll:MyCollClass -user=scott
Enter scott password: password
|
注意: カンマの前後に空白を挿入しないでください。 |
このように入力すると、OracleのオブジェクトはMYOBJという名前になり、Myobjクラスを定義したMyobj.sqljソース・ファイルが生成されます。また、MYCOLL Oracleコレクションに対して、MyCollClassクラスを定義したソース・ファイルMyCollClass.javaが生成されます。
-sqlにはスキーマ名(次の例のscottスキーマなど)も指定できます。
% jpub -sql=scott.Myobj,scott.mycoll:MyCollClass -user=scott
Enter scott password: password
カスタム参照クラスの名前は指定できません。JPublisherでは、カスタム・オブジェクト・クラス名の後ろにRefが追加されて、自動的に生成されます。これは、ORAData実装の場合のみです。たとえば、Myobjカスタム・オブジェクト・クラスを定義するMyobj.sqlj Javaソース・ファイルをJPublisherで作成すると、MyobjRefカスタム参照クラスを定義するMyobjRef.java Javaソース・ファイルも生成されます。
|
注意: スキーマ(例のようにscottなど)を指定しても、カスタムJavaクラスの名前には取り込まれません。 |
「ユーザー定義データ型」で定義したオブジェクト型とコレクション型のカスタムJavaクラスを作成するには、次のようにJPublisherを実行します。
% jpub -user=scott -sql=Address,Person,Phone_array,Participant_t,Module_t,Moduletbl_t
Enter scott password: password
または、カスタム・オブジェクト・クラスとカスタム・コレクション・クラスの名前を明示的に指定し、次のように実行します。
% jpub -user=scott -sql=Address,Person,phone_array:PhoneArray,
participant_t:ParticipantT,module_t:ModuleT,moduletbl_t:ModuletblT
Enter scott password: password
前述の2つの例はそれぞれ折り返されて表示されていますが、1行で入力されたコマンドラインです。
2番目の例では、Javaソース・ファイルAddress.sqlj、AddressRef.java、Person.sqlj、PersonRef.java、PhoneArray.java、ParticipantT.sqlj、ParticipantTRef.java、ModuleT.sqlj、ModuleTRef.javaおよびModuletblT.javaが生成されます。ソース・ファイルの例は、「JPublisherのカスタムJavaクラスの例」を参照してください。
カスタムJavaクラスの設定方法を特定するために、JPublisherは指定されたスキーマに接続し、指定されたオブジェクト型の属性または指定されたコレクション型の要素を調べます。
|
注意: 単一の-sql設定に複数のマッピングを指定するかわりに、同一のコマンドラインに複数の-sqlオプションを使用できます。複数の-sqlオプションの効果は累積されます。 |
JPublisherで生成するメソッドおよび属性のデフォルト名の大/小文字の使用方法を変更するには、-caseオプションを使用します。オブジェクトまたはコレクションが属性の場合、つまり下位カスタムJavaクラスの場合も、このオプションを使用します。次の4通りの設定があります。
-case=mixed(デフォルト)
大文字になる文字は、クラス名を構成する各ワードの頭文字、属性名を構成する各ワードの頭文字およびメソッド名の2番目以降のワードの頭文字です。これ以外の文字はすべて小文字になります。JPublisherでは、アンダースコア(_)、ドル記号($)、およびJavaの識別子で無効な文字は、ワード間のセパレータとしてみなされます。これらの文字は処理時に破棄されます。
-case=same
大/小文字区別は、データベース内で表現された場合と同じになります。アンダースコアとドル記号は保持されます。Javaの識別子以外の文字は破棄されます。
-case=upper
小文字が大文字に変換されます。アンダースコアとドル記号は保持されます。Javaの識別子以外の文字は破棄されます。
-case=lower
大文字が小文字に変換されます。アンダースコアとドル記号は保持されます。Javaの識別子以外の文字は破棄されます。
|
注意: JPublisherの実行時にJavaにマッピングするユーザー定義型を指定しないと、スキーマ内のすべてのユーザー定義型が処理されます。最上位カスタム・クラスとその他のクラス(オブジェクトの属性またはコレクションの要素)のクラス名は、-caseオプションに基づき生成されます。 |
JPublisherには、ユーザー定義型およびその属性型や要素型をSQLとJavaとの間でマッピングする方法が数通りあります。
JPublisherではSQL型が次のようなグループに分類されています。各グループに対応するJPublisherオプションは、各項に示したとおりです。
数値型: SQL型NUMBERとして格納されるすべての型
ラージ・オブジェクト(LOB)型: SQL型のBLOBおよびCLOB
組込み型: データベース内にSQL型として格納されている型のうち、前に示した分類のどれにも該当しない型(たとえば、CHAR、VARCHAR2、LONG、RAWなど)
JPublisherでは次の型マッピング・モードを定義します。
JDBCマッピング(設定はjdbc): SQL型とネイティブなJava型とのマッピングに標準デフォルト・マッピング型を使用します。この設定は、-numbertypes、-lobtypesおよび-builtintypesオプションに有効です。
Oracleマッピング(設定はoracle): SQL型へのマッピングの際に、対応するoracle.sql型を使用します。この設定は、-numbertypes、-lobtypesおよび-builtintypesオプションに有効です。
Object JDBCマッピング(設定はobjectjdbc): JDBCマッピングをさらに拡張したマッピングです。必要に応じて、Object JDBCマッピングでは、標準java.langパッケージの数値オブジェクト型を使用します(具体的にはjava.lang.Integer、FloatまたはDoubleなど)。この数値オブジェクト型は、Javaの基本型(具体的にはint、floatまたはdoubleなど)のかわりに使用されます。java.lang型ではNULL値が許容されていますが、基本型では許容されていません。この設定は、-numbertypesオプションにのみ有効です。
BigDecimalマッピング(設定はbigdecimal): 数値属性へのマッピングの際にjava.math.BigDecimalを使用します。これは、桁数の多い数値を使用してもoracle.sql.NUMBER型は使用しない場合に適したマッピング方法です。この設定は、-numbertypesオプションにのみ有効です。
|
注意: BigDecimalマッピングを使用すると、パフォーマンスが著しく劣化する場合があります。 |
SQLオブジェクト型や要素型の属性型に対してマッピング指定を省略すると、JPublisherでは次のデフォルトが使用されます。
数値型のデフォルトは、Object JDBCマッピングです。
LOB型のデフォルトは、Oracleマッピングです。
組込みタイプ型のデフォルトは、JDBCマッピングです。
これ以外に代替のマッピングを使用する場合は、-numbertypes、-lobtypesおよび-builtintypesオプションを使用します。どのオプションを使用するかは、処理対象とする属性型およびマッピングに応じて決めてください。
属性型自体がSQLオブジェクト型である場合には、-usertypes設定値に基づいてマッピングされます。
|
注意: カスタム・オブジェクト・クラスに対してSQLDataが実装されるように指定し、コードが移植可能であるようにする場合は、属性型に対して移植可能なマッピングを使用する必要があります。数値型または組込み型をマッピング対象とする場合はデフォルトで移植可能になりますが、LOB型の場合は-lobtypes=jdbcを指定する必要があります。 |
表6-1に、SQL型、マッピング設定およびデフォルト設定に関してJPublisherでの分類をカテゴリ別にまとめます。
表6-1 JPublisherでのSQL型の分類、サポートされている設定およびデフォルト
| SQL型の分類 | JPublisherマッピング・オプション | マッピング設定 | デフォルト |
|---|---|---|---|
|
UDT型 |
|
|
|
|
数値型 |
|
|
|
|
LOB型 |
|
|
|
|
組込み型 |
|
|
|
|
注意: 以前のリリースで使用された-mappingオプションは、現在のJPublisherでは使用されていませんが、サポートはされています。JPublisherの-mappingオプション設定値を新しいマッピング・オプション設定値に変換する方法の詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。 |
OracleオブジェクトをJavaにマッピングするためのカスタム・オブジェクト・クラスを作成する際に、Oracleオブジェクトのメソッドで使用するJavaラッパーについて作成要否を指定するには、JPublisherで-methodsオプションを使用します。デフォルトの-methods=true設定では、ラッパーが生成されます。また、基となるSQLオブジェクトに実際にメソッドがないかぎり、JPublisherでは、カスタム・オブジェクト・クラス用に.javaファイルではなく.sqljファイルが生成されます。
JPublisherでラッパー・メソッドを生成すると、元のオブジェクト・メソッドがstaticでも、必ずインスタンス・メソッドになります。次に、-methodsオプションの設定例を示します。
% jpub -sql=Myobj,mycoll:MyCollClass -user=scott -methods=true
Enter scott password: password
この例では、デフォルトのネーミング・メソッドが使用されます。Javaのメソッド名は、最初の文字が小文字になる他は、カスタムJavaクラス名と同じ方法でネーミングされます。たとえば、オブジェクトのメソッド名CALC_SALは、デフォルトでJavaのラッパー・メソッド名calcSal()になります。かわりにJavaのメソッド名を指定することも可能ですが、この場合は、JPublisherのINPUTファイルを使用する必要があります。
|
注意: -methodsオプションには、他の用途もあります。たとえば、パッケージのラッパー・クラスやパッケージのメソッドのラッパー・メソッドの生成時に使用できます。この内容はこのマニュアルの対象外です。詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。 |
オーバーロード・メソッドについて
JPublisherの実行対象のOracleオブジェクトにオーバーロード・メソッドがあり、複数のシグネチャが同一のJavaシグネチャに対応する場合は、シグネチャごとに一意なメソッド名が生成されます。つまり、ファンクション名の後ろに_n が付けられます(n は番号です)。生成されたカスタムJavaクラスの2つのメソッドは、それぞれ一意な名前とシグネチャになります。一例として、MY_TYPEオブジェクト型の生成時に定義されるSQLファンクションについて考えます。
CREATE OR REPLACE TYPE my_type AS OBJECT
(
...
MEMBER FUNCTION myfunc(x INTEGER)
RETURN my_return IS
BEGIN
...
END;
MEMBER FUNCTION myfunc(y SMALLINT)
RETURN my_return IS
BEGIN
...
END;
...
);
このままでは、myfuncの定義は両方とも、Java形式の名前とシグネチャになります。
myfunc(Integer)
その理由は、SQL形式のINTEGERおよびSMALLINTが、Java Integer型にマッピングされているためです。
かわりに、JPublisherでは、myfunc_1ともう1つのmyfunc_2をコールします。_n はそれぞれ一意です。簡単な例では、_1、_2のようになりますが、各ファンクションに一意の値ではなく、任意の値でもかまいません。
|
注意: オーバーロードしたラッパー・メソッドに対するJPublisherでの処理は、オブジェクトやパッケージ内で生成されたSQLファンクションに対しても実行されます。ただし、最上位ファンクションに対してはJPublisherでの処理が実行されません。最上位ファンクションではオーバーロードが許容されません。 |
JPublisherでは、カスタムJavaクラスを生成できる他、オブジェクト型またはコレクション型のマッピング先として、生成したクラスでなく代替クラスが指定できます。
通常は、JPublisherで生成したクラスをスーパークラスにし、これを拡張して機能を追加し、オブジェクト型をサブクラスにマッピングします。たとえば、Oracleオブジェクト型ADDRESSのカスタムJavaクラスを作成し、JPublisherで生成されなかった機能を追加するとします。この場合は、JPublisherでJAddressカスタムJavaクラスを生成してから、これを拡張してMyAddressクラスを作成します。専用機能をMyAddressに追加してから、JPublisherでADDRESSオブジェクトを、JAddressクラスではなく、MyAddressにマッピングします。JPublisherでは、JAddressではなく、MyAddressの参照クラスも生成できます。
JPublisherでは、代替クラスへのマッピングを容易に行えます。-sqlオプションを次の構文で指定します。
-sql=object_type:generated_class:map_class
前の例の場合は、次のように設定します。
-sql=ADDRESS:JAddress:MyAddress
この設定では、クラスJAddressがソース・ファイルJAddress.sqlj(またはJAddress.java)に作成され、次の処理が行われます。
ADDRESSオブジェクト型のマッピング先が、JAddressクラスではなく、MyAddressクラスになります。したがって、データベースから取得したオブジェクトにADDRESS属性があると、Javaでこの属性がMyAddressのインスタンスとして生成されます。ADDRESSオブジェクトを直接取得する場合は、MyAddressインスタンスに取り込まれます。
JAddressRefクラスではなく、MyAddressRefクラスがMyAddressRef.javaに生成されます。
ファイルがすでに存在していないかぎり、MyAddress.sqlj(またはMyAddress.java)ソース・ファイルにMyAddressクラスの初期バージョンが生成されます。存在している場合、ファイルは変更されません。
MyAddressはJAddressにサブクラス化されます。MyAddressに対して拡張機能を実装するためには、JPublisherで生成されたMyAddressソース・ファイルを使用し、必要に応じてそのファイルを編集できます。
JPublisher生成クラスを拡張する方法(前例の続き)は、「JPublisher生成クラスの拡張」を参照してください。
JPublisherでは、専用のINPUTファイルおよび標準のプロパティ・ファイルを使用して、型のマッピングなどのオプション設定を指定できます。
JPublisherのコマンドライン・オプション-inputで、型マッピングを追加するためのINPUTファイルを指定できます。INPUTファイルのSQLは、コマンドラインの-sqlに相当します。ASまたはGENERATE...AS構文は、コマンドラインのコロン区切り構文に相当します。次の構文でマッピングを指定します。SQLコマンドで一度に指定できるマッピングは1つのみです。
SQL udt1 <GENERATE GeneratedClass1> <AS MapClass1> SQL udt2 <GENERATE GeneratedClass2> <AS MapClass2> ...
GeneratedClass1 とGeneratedClass2 が生成され、udt1 がMapClass1 に、udt2 がMapClass2 にマッピングされます。
INPUTファイルの例
次の例では、JPublisherは、コマンドラインの-userオプションに基づき、INPUTファイルmyinput.inで型マッピングを調べます。
コマンドラインで次のように指定します。
% jpub -input=myinput.in -user=scott
Enter scott password: password
INPUTファイルmyinput.inの内容は次のようになります。
SQL Myobj SQL mycoll AS MyCollClass SQL employee GENERATE Employee AS MyEmployee
次の処理が行われます。
ユーザー定義型MYOBJの名前は、カスタム・オブジェクト・クラスの名前として入力したMyobjになります。JPublisherでは、ソース・ファイルMyobj.sqlj(またはMyobjにメソッドがない場合はMyobj.java)およびMyobjRef.javaが作成されます。
ユーザー定義型MYCOLLが、MyCollClassにマッピングされます。JPublisherでは、MyCollClass.javaソース・ファイルが生成されます。
ユーザー定義型EMPLOYEEがMyEmployeeクラスにマッピングされます。JPublisherでは、ファイルがすでに存在していないかぎり、初期バージョンのMyEmployee.sqlj(または.java)に加え、ソース・ファイルEmployee.sqlj(またはEmployee.java)およびMyEmployeeRef.javaが作成されます。データベースから取得したオブジェクトにEMPLOYEE属性があると、Javaでこの属性がMyEmployeeのインスタンスとして生成されます。EMPLOYEEオブジェクトを直接取得する場合は、MyEmployeeのインスタンスに取り込まれます。MyEmployeeコードに関する責任は開発者にありますが、EMPLOYEEオブジェクトに対して特別な機能をJavaで実装するためには、JPublisherで生成したMyEmployeeソース・ファイルを利用して、そのファイルを編集できます。MyEmployeeはEmployeeクラスにサブクラス化されます。
JPublisherのコマンドライン・オプション-propsで、型マッピングなどのオプション設定に使用するプロパティ・ファイルを指定できます。
プロパティ・ファイルのjpub.(ピリオドを含む)は、コマンドラインの-(シングル・ダッシュ)に相当します。その他の構文は同じです。1行に1つのオプションを指定してください。
型マッピングの場合、jpub.sqlは-sqlに相当します。単一のjpub.sql設定で複数のマッピングを指定できます。また、複数のjpub.sqlオプションを使用できます。複数の-sqlオプションが1つのコマンドラインにある場合、オプションの効果は累積されます。
|
注意: jpub.または--jpub.(2つのダッシュの後にjpub.が続く)で開始しないプロパティ・ファイルの行は無視されます。これによって、型を作成するSQLスクリプトとJPublisher用のプロパティ・ファイルに同じファイルを使用できます。JPublisherの各文を「--」(SQLのコメントを示す)で開始すると、SQL*Plusでは無視されます。また、SQL文はJPublisherによって無視されます。 |
プロパティ・ファイルの例
次の例では、JPublisherはコマンドラインの-userオプションに基づき、プロパティ・ファイルjpub.propertiesで型マッピングと属性マッピング・オプションを調べます。
コマンドラインで次のように指定します。
% jpub -props=jpub.properties -user=scott
Enter scott password: password
プロパティ・ファイルjpub.propertiesの内容は次のとおりです。
jpub.sql=Myobj,mycoll:MyCollClass,employee:Employee:MyEmployee jpub.usertypes=oracle
このように指定すると、oracleマッピングの設定が明示的に指定され、前の入力ファイルの例と同じ結果になります。
|
注意: SQLJと異なり、JPublisherにはデフォルトのプロパティ・ファイルがありません。プロパティ・ファイルを使用するには、-propsオプションを使用します。 |
カスタムJavaクラスの作成時に、カスタム・クラスの属性またはメソッドの名前を指定できます。ただし、この属性やメソッド名は、JPublisherコマンドラインでの指定はできません。次のようにTRANSLATE構文を使用して、JPublisherのINPUTファイルで指定する必要があります。
SQL udt <GENERATE GeneratedClass> <AS MapClass> <TRANSLATE membername1 AS Javaname1> <, membername2 AS Javaname2> ...
TRANSLATEペア(membernameN AS JavanameN )はカンマで区切ります。
たとえば、EMPLOYEE Oracleオブジェクト型のADDRESS属性の名前をHomeAddressにし、GIVE_RAISEメソッドの名前をgiveRaise()にするとします。また、Employeeクラスを作成し、EMPLOYEEオブジェクトのマッピング先をこれから作成するMyEmployeeクラスにするとします。(この例はINPUTファイル構文の全体を示すものであり、メンバー名の指定には関係ありません。)
SQL employee GENERATE Employee AS MyEmployee TRANSLATE address AS HomeAddress, GIVE_RAISE AS giveRaise
|
注意:
|
ここでは、JPublisherでのラッパー・メソッドの生成方法と、ランタイムのラッパー・メソッド・コールの処理方法を説明します。
ラッパー・メソッドの生成
次に、JPublisherでのラッパー・メソッドの生成方法について説明します。
JPublisherで生成したラッパー・メソッドは、SQLJで実装されます。したがって、-methods=trueに設定すると、オブジェクト型でメソッドを定義している場合は、カスタム・オブジェクト・クラスが、.javaファイルではなく、.sqljファイルに定義されます。.sqljファイルを変換するには、SQLJを実行します。
|
注意: オブジェクト型でメソッドを定義していない場合でも、-methods=alwaysの設定で.sqljファイルが生成されることを確認できます。詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。 |
JPublisherで生成したラッパー・メソッドは、すべてインスタンス・メソッドとして実装されます。サーバー側メソッドの起動にデータベース接続を必要とするからです。JPublisher生成のカスタムJavaクラスの各インスタンスに接続が対応付けられます。
ラッパー・メソッド・コールのランタイム実行
JPublisherで生成したJavaラッパー・メソッドをランタイムに実行する方法を説明します。ここで取り上げるJavaラッパー・メソッドとは、カスタムJavaオブジェクトのメソッドのことを指していますが、SQLメソッドのラッピングとは、SQLオブジェクトのメソッドをラッパー・メソッドでラッピングすることを指します。
カスタムJavaオブジェクトがSQLオブジェクトに変換され、データベースに転送されると、ラッピングされたSQLメソッドが起動されます。このSQLメソッドの起動後、SQLオブジェクトの新しい値が戻り値として新規のカスタムJavaオブジェクトの形でJavaに戻されます。この戻り値は、ラッピングされたSQLメソッドからのファンクション戻り値(SQLメソッドがストアド・プロシージャである場合)、または追加の出力パラメータの配列要素(SQLメソッドがストアド・ファンクションで、ファンクション戻り値がすでに存在している場合)のどちらかになります。
出力または入出力パラメータが単一要素配列の要素として渡されます。入出力パラメータの場合は、ラッパー・メソッドが入力として配列要素をとります。処理後、ラッパーによって出力が配列要素に代入されます。
ここでは、次のユーザー定義型に対して、JPublisherで生成されるORAData実装の例を示します。
カスタム・オブジェクト・クラス(Oracleのオブジェクト型 ADDRESSに対応するAddress)とそのカスタム参照クラス(AddressRef)
カスタム・コレクション・クラス(Oracleのコレクション型MODULETBL_Tに対応するMODULETBL_T)
-methodsオプションがデフォルトのtrueに設定されており、ADDRESS型にメソッドがあるとします。この結果、.sqljファイルがAddressクラスに対して生成されます。
|
参照: JPublisherで生成されるSQLDataおよびORAData実装の各例については、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。 |
カスタム・オブジェクト・クラス: Address.sqlj
次に、JPublisherで生成されるカスタム・オブジェクト・クラスのソース・コードの例を示します。実装の詳細は省略されています。
この例では、「オブジェクト型の作成」と異なり、OracleオブジェクトADDRESSの属性はstreetとzip_codeのみです。
package bar;
import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.STRUCT;
import oracle.jpub.MutableStruct;
public class Address implements ORAData, ORADataFactory
{
public static final String _SQL_NAME = "SCOTT.ADDRESS";
public static final int _SQL_TYPECODE = OracleTypes.STRUCT;
public static ORADataFactory getORADataFactory()
{ ... }
/* constructors */
public Address()
{ ... }
public Address(String street, java.math.BigDecimal zip_code)
throws SQLException
{ ... }
/* ORAData interface */
public Datum toDatum(Connection c) throws SQLException
{ ... }
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException
{ ... }
/* accessor methods */
public String getStreet() throws SQLException
{ ... }
public void setStreet(String street) throws SQLException
{ ... }
public java.math.BigDecimal getZipCode() throws SQLException
{ ... }
public void setZipCode(java.math.BigDecimal zip_code) throws SQLException
{ ... }
}
カスタム参照クラス: AddressRef.java
次に、ADDRESSオブジェクトへの参照としてJPublisherで生成されるカスタム参照クラスのソース・コードの例を示します。実装の詳細は省略されています。
package bar;
import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.REF;
import oracle.sql.STRUCT;
public class AddressRef implements ORAData, ORADataFactory
{
public static final String _SQL_BASETYPE = "SCOTT.ADDRESS";
public static final int _SQL_TYPECODE = OracleTypes.REF;
public static ORADataFactory getORADataFactory()
{ ... }
/* constructors */
public AddressRef()
{ ... }
public static AddressRef(ORAData o) throws SQLException
{ ... }
/* ORAData interface */
public Datum toDatum(Connection c) throws SQLException
{ ... }
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException
{ ... }
public static AddressRef cast(ORAData o) throws SQLException
{ ... }
public Address getValue() throws SQLException
{ ... }
public void setValue(Address c) throws SQLException
{ ... }
}
カスタム・コレクション・クラス: ModuletblT.java
次に、JPublisherで生成されるカスタム・コレクション・クラスのソース・コードの例を示します。実装の詳細は省略されています。
import java.sql.SQLException;
import java.sql.Connection;
import oracle.jdbc.OracleTypes;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.jpub.runtime.MutableArray;
public class ModuletblT implements ORAData, ORADataFactory
{
public static final String _SQL_NAME = "SCOTT.MODULETBL_T";
public static final int _SQL_TYPECODE = OracleTypes.ARRAY;
public static ORADataFactory getORADataFactory()
{ ... }
/* constructors */
public ModuletblT()
{ ... }
public ModuletblT(ModuleT[] a)
{ ... }
/* ORAData interface */
public Datum toDatum(Connection c) throws SQLException
{ ... }
/* ORADataFactory interface */
public ORAData create(Datum d, int sqlType) throws SQLException
{ ... }
public String getBaseTypeName() throws SQLException
{ ... }
public int getBaseType() throws SQLException
{ ... }
public ArrayDescriptor getDescriptor() throws SQLException
{ ... }
/* array accessor methods */
public ModuleT[] getArray() throws SQLException
{ ... }
public void setArray(ModuleT[] a) throws SQLException
{ ... }
public ModuleT[] getArray(long index, int count) throws SQLException
{ ... }
public void setArray(ModuleT[] a, long index) throws SQLException
{ ... }
public ModuleT getObjectElement(long index) throws SQLException
{ ... }
public void setElement(ModuleT a, long index) throws SQLException
{ ... }
}
JPublisherで生成したカスタムJavaクラスは、メソッドと一時フィールドを追加して、機能を拡張できます。このためには、JPublisher生成クラスを拡張します。
たとえば、JPublisherで、ADDRESS SQLのオブジェクト型からJAddressクラスを生成するとします。また、ADDRESSオブジェクトを表すMyAddressクラスを使用し、特別な機能を実装します。このMyAddressクラスは、JAddressの拡張クラスとする必要があります。
JPublisher生成クラスの機能を拡張する場合は、単にメソッドを追加する方法もあります。ただし、クラスをJPublisherで再生成する予定がある場合は、JPublisher生成クラスへのメソッドの追加は避けてください。この方法で変更したクラスをJPublisherで再生成する場合は、コピーを保存しておき、変更内容を手動でマージする必要があります。
生成クラスを拡張するためのJPublisherの機能
次のJPublisherの構文でJAddressを生成し、MyAddressにマッピングできます。
-sql=ADDRESS:JAddress:MyAddress
または、INPUTファイルで次のように指定します。
SQL ADDRESS GENERATE JAddress AS MyAddress
このように指定した結果、JPublisherで生成される参照クラスは、MyAddressRef.javaのMyAddressRefであって、JAddressRefが生成されることはありません。
また、JPublisher生成コードが変更され、次の機能が実装されます。
SQL型ADDRESSの属性は、JAddressクラスではなく、MyAddressクラスで表されます。
ADDRESS型のメソッド引数およびファンクション結果は、JAddressクラスではなく、MyAddressクラスで表されます。
SQL型ADDRESSのJavaオブジェクトは、JAddressファクトリではなく、MyAddressファクトリで構築されます。
追加コードを作成する場合も、おそらくMyAddressを使用することになります。
実行時にOracle JDBCドライバによって、データベース内のADDRESSデータのオカレンスが、JAddressのインスタンスではなく、MyAddressのインスタンスにマッピングされます。
拡張クラスの要件
JPublisherではデフォルトの場合、作成されるファイルがすでに存在していないかぎり、MyAddress.sqljファイル(元のクラスがメソッドを使用し、これらのメソッドを公開している場合)またはMyAddress.javaファイルに、初期バージョンのMyAddressユーザー・サブクラスが作成されます(ファイルがすでに存在している場合、ファイルは変更されません)。このファイルは、必要な機能を追加するために編集できます。
MyAddressには、引数を持たないコンストラクタを用意する必要があります。適切に初期化したオブジェクトを容易に構築するには、スーパークラスのコンストラクタを明示的にまたは暗黙的に起動します。
JPublisher生成クラスを拡張した場合、_SQL_NAMEフィールド(必須)の定義と_SQL_TYPECODEフィールドの定義がサブクラスに継承されます。
また、次のどちらかが当てはまります。
JPublisherで生成したクラスには、ORADataおよびORADataFactoryインタフェースが実装されているため、必要とされるtoDatum()およびJPublisherで生成されたクラスのcreate()機能がサブクラスに継承されます。ただし、サブクラスには、自分でマッピングしたクラスのインスタンス(MyAddressオブジェクトなど)を戻り値とするgetORADataFactory()メソッドを実装します。
JPublisherで生成したクラスにSQLDataインタフェースが実装されている場合、この実装と生成クラスのreadSQL()およびwriteSQL()機能とがサブクラスに継承されます。
JPublisher生成のカスタム・オブジェクト・クラス: JAddress.sqlj
JPublisherで生成されたJAddressクラスのコード(ORADataおよびORADataFactoryを実装する)は、AddressがJAddressに置換されることを除き、前述のAddressクラスのコードとほぼ同じです。
JPublisher生成のその他の参照クラス: MyAddressRef.java
これまでの項の続きとして、JPublisher生成の参照クラスMyAddressRefのコードを示します(ここで取り上げるクラスは、JAddressRefではありません。MyAddressクラスがADDRESSオブジェクトのマッピング先となるためです)。また、このクラスにはORADataとORADataFactoryが実装されます。この実装は、クラス名が異なることと、アクセッサ・メソッドがAddressインスタンスではなくMyAddressインスタンスを使用することを除いて、AddressRef.javaの実装とほぼ同じです。
拡張カスタム・オブジェクト・クラス: MyAddress.sqlj
これまでの例の続きとして、JPublisherで生成されるJAddressクラスのサブクラスであるMyAddressのサンプル・コードを示します。JAddressから継承された内容は、コード中のコメントに記述してあります。実装の詳細は省略されています。
import java.sql.SQLException;
import oracle.sql.ORAData;
import oracle.sql.ORADataFactory;
import oracle.sql.Datum;
import oracle.sql.STRUCT;
import oracle.jpub.runtime.MutableStruct;
public class MyAddress extends JAddress
{
/* _SQL_NAME inherited from MyAddress */
/* _SQL_TYPECODE inherited from MyAddress */
static _myAddressFactory = new MyAddress();
public static ORADataFactory getORADataFactory()
{
return _myAddressFactory;
}
/* constructor */
public MyAddress()
{ super(); }
/* ORAData interface */
/* toDatum() inherited from JAddress */
/* ORADataFactory interface */
public ORAData create(oracle.sql.Datum d, int sqlType) throws SQLException
{ ... }
/* accessor methods inherited from JAddress */
/* Additional methods go here. These additional methods (not shown)
are the reason that JAddress was extended.
*/
}
Oracle SQLJ実装では、強い型指定のオブジェクトまたは参照をホスト式およびイテレータで使用して、オブジェクト・データの読込みおよび書込みを柔軟に行えます。
イテレータの場合は、カスタム・オブジェクト・クラスをイテレータの列型として使用できます。または、属性のSQLデータ型にマップしている列型を使用して、イテレータ列をオブジェクトの各属性に対応付けることができます(エクステント表と同様)。
ホスト式の場合は、カスタム・オブジェクト・クラス型またはカスタム参照クラス型のホスト変数を使用できます。または、属性のSQLデータ型にマップしている変数型を使用して、ホスト変数をオブジェクトの属性に対応付けることができます。
次に、Oracleオブジェクトの操作例を示します。SQLJ実行文のホスト変数とイテレータ列に対して、カスタム・オブジェクト・クラス、カスタム・オブジェクト・クラスの属性およびカスタム参照クラスを使用します。
次の2つの例は、オブジェクト・レベルでの操作です。
「各オブジェクト属性から作成したオブジェクトの挿入」の例は、スカラー属性レベルでの操作です。
「オブジェクト参照の更新」の例は、参照による操作です。
Oracleのオブジェクト型ADDRESSおよびPERSONは、「オブジェクト型の作成」を参照してください。
この例では、カスタムJavaクラスとカスタム参照クラスをイテレータの列型として使用します。ADDRESS Oracleオブジェクト型が次のように定義されているものとします。
CREATE TYPE ADDRESS AS OBJECT ( street VARCHAR(40), zip NUMBER );
また、EMPADDRS表が次のように定義されているものとします。この表には、ADDRESS列とADDRESS参照列があります。
CREATE TABLE empaddrs ( name VARCHAR(60), home ADDRESS, loc REF ADDRESS );
ADDRESS Oracleオブジェクト型に対応するカスタムJavaクラスAddressとカスタム参照クラスAddressRefをJPublisherで生成するか、または手動で作成すると、次のようにAddressとAddressRefとを名前付きイテレータで使用できます。
#sql iterator EmpIter (String name, Address home, AddressRef loc);
...
EmpIter ecur;
#sql ecur = { SELECT name, home, loc FROM empaddrs };
while (ecur.next()) {
Address homeAddr = ecur.home();
// Print out the home address.
System.out.println ("Name: " + ecur.name() + "\n" +
"Home address: " + homeAddr.getStreet() + " " +
homeAddr.getZip());
// Now update the loc address zip code through the address reference.
AddressRef homeRef = ecur.loc();
Address location = homeRef.getValue();
location.setZip(new BigDecimal(98765));
homeRef.setValue(location);
}
...
ecur.home()メソッド・コールは、イテレータのhome列からAddressオブジェクトを抽出し、homeAddrローカル変数に代入します(効率化)。このオブジェクトの属性にアクセスするには、次に示したJavaの標準ドット区切り構文を使用します。
homeAddr.getStreet()
ロケーション・アドレス(この例ではzipコード)を操作するには、getValue()およびsetValue()メソッドを使用します。これらのメソッドは、JPublisher生成のカスタム参照クラスの標準機能です。
この例では、Address Java型の入力ホスト変数を宣言し、設定します。この変数によって、employees表の列にあるADDRESSオブジェクトを更新します。更新前と更新後のアドレスがAddress型の出力ホスト変数に取り出され、検証用に出力されます。
...
// Updating an object
static void updateObject()
{
Address addr;
Address new_addr;
int empnum = 1001;
try {
#sql {
SELECT office_addr
INTO :addr
FROM employees
WHERE empnumber = :empnum };
System.out.println("Current office address of employee 1001:");
printAddressDetails(addr);
/* Now update the street of address */
String street ="100 Oracle Parkway";
addr.setStreet(street);
/* Put updated object back into the database */
try {
#sql {
UPDATE employees
SET office_addr = :addr
WHERE empnumber = :empnum };
System.out.println
("Updated employee 1001 to new address at Oracle Parkway.");
/* Select new address to verify update */
try {
#sql {
SELECT office_addr
INTO :new_addr
FROM employees
WHERE empnumber = :empnum };
System.out.println("New office address of employee 1001:");
printAddressDetails(new_addr);
} catch (SQLException exn) {
System.out.println("Verification SELECT failed with "+exn); }
} catch (SQLException exn) {
System.out.println("UPDATE failed with "+exn); }
} catch (SQLException exn) {
System.out.println("SELECT failed with "+exn); }
}
...
AddressオブジェクトのsetStreet()アクセッサ・メソッドが使用されています。JPublisherで生成したカスタムJavaクラスには、属性ごとにアクセッサ・メソッドが用意されています。
この例では、printAddressDetails()ユーティリティを使用しています。このメソッドのソース・コードを次に示します。
static void printAddressDetails(Address a) throws SQLException
{
if (a == null) {
System.out.println("No Address available.");
return;
}
String street = ((a.getStreet()==null) ? "NULL street" : a.getStreet()) ;
String city = (a.getCity()==null) ? "NULL city" : a.getCity();
String state = (a.getState()==null) ? "NULL state" : a.getState();
String zip_code = (a.getZipCode()==null) ? "NULL zip" : a.getZipCode();
System.out.println("Street: '" + street + "'");
System.out.println("City: '" + city + "'");
System.out.println("State: '" + state + "'");
System.out.println("Zip: '" + zip_code + "'" );
}
この例では、PERSONオブジェクトとネストされているADDRESSオブジェクトの属性に対応する入力ホスト変数を宣言し、設定します。これらの値を使用して、新しいPERSONオブジェクトをデータベース内のpersons表に挿入します。
...
// Inserting an object
static void insertObject()
{
String new_name = "NEW PERSON";
int new_ssn = 987654;
String new_street = "NEW STREET";
String new_city = "NEW CITY";
String new_state = "NS";
String new_zip = "NZIP";
/*
* Insert a new PERSON object into the persons table
*/
try {
#sql {
INSERT INTO persons
VALUES (PERSON(:new_name, :new_ssn,
ADDRESS(:new_street, :new_city, :new_state, :new_zip))) };
System.out.println("Inserted PERSON object NEW PERSON.");
} catch (SQLException exn) { System.out.println("INSERT failed with "+exn); }
}
...
この例では、persons表からPERSON参照を選択し、この値を使用してemployees表内のPERSON参照を更新します。属性値の基準チェックには、単純な入力ホスト変数を使用します。更新後の値で参照先PERSONオブジェクトを選択し、情報を出力します。ユーザーは変更内容を検証できます。
...
// Updating a REF to an object
static void updateRef()
{
int empnum = 1001;
String new_manager = "NEW PERSON";
System.out.println("Updating manager REF.");
try {
#sql {
UPDATE employees
SET manager =
(SELECT REF(p) FROM persons p WHERE p.name = :new_manager)
WHERE empnumber = :empnum };
System.out.println("Updated manager of employee 1001. Selecting back");
} catch (SQLException exn) {
System.out.println("UPDATE REF failed with "+exn); }
/* Select manager back to verify the update */
Person manager;
try {
#sql {
SELECT deref(manager)
INTO :manager
FROM employees e
WHERE empnumber = :empnum };
System.out.println("Current manager of "+empnum+":");
printPersonDetails(manager);
} catch (SQLException exn) {
System.out.println("SELECT REF failed with "+exn); }
}
...
|
注意: この例では、前述の表の別名構文(p)を使用しています。参照先オブジェクトから参照を選択するには、REF構文を使用します。参照からオブジェクトを選択するには、DEREF構文を使用します。表の別名、REFおよびDEREFの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。 |
Oracle SQLJ実装では、強い型指定のオブジェクトおよび参照と同じように、強い型指定のコレクションをイテレータまたはホスト式で使用して、データの読込みおよび書込みを行えます。
SQLJ開発者にとっては、2種類のコレクション(VARRAYとネストした表)の扱いは基本的に同じですが、実装とパフォーマンスの面で相違点が多少あります。
Oracle SQLJ実装では、複数の構文を使用してネストした表にアクセスし、操作できます。ネストした表は、単独で操作することも、外部のネストした表と一緒に操作することも可能です。ここでは、ネストした表単独の操作を詳細レベルの操作と呼び、ネストした表と外部の表の同時操作をマスター・レベルの操作と呼びます。
ここでは、いくつかの構文について簡単に説明してから、ネストした表の操作例を示します。ネストした表の操作は、VARRAYの操作より複雑です。
MODULETBL_T Oracleコレクション型と関連する表、およびオブジェクト型の定義については、「コレクション型の作成」を参照してください。
|
注意: Oracle SQLJ実装では、VARRAY型およびNESTED TABLE型を型単位でのみ取得できます。これは、Oracle SQL実装とは違い、ネストした表の問合せを選択的に実行できます。 |
ここでは、次の項目について説明します。
Oracle SQLJ実装では、ネストしたイテレータを使用して、ネストした表のデータにアクセスできます。外部のSELECT文でCURSORキーワードを使用し、内部のSELECT文をカプセル化します。「ネスト・イテレータによるネストした表からのデータの選択」を参照してください。
Oracleでは、TABLEキーワードを使用すると、ネストした表を行単位で操作できます。Oracleではこのキーワードを使用すると、副問合せで戻される列値が、スカラー値ではなくネストした表であることが認識されます。単一列値を戻す副問合せまたはネストした表に解決される式の前に、TABLEキーワードを指定する必要があります。
次に、TABLE構文の使用例を示します。
UPDATE TABLE(SELECT a.modules FROM projects a WHERE a.id=555) b
SET module_owner=
(SELECT ref(p) FROM employees p WHERE p.ename= 'Smith')
WHERE b.module_name = 'Zebra';
この例のようにTABLEを使用すると、参照先の単一のネストした表が外部の表の列から選択されます。
|
注意: この例では、前述のように、表の別名構文(projectsにはa、ネストした表にはb、またemployeesにはp)が使用されています。 |
この例では、マスター・レベル(外部の表)と詳細レベル(ネストした表)を同時に明示的に操作します。ここでは、行をprojects表に挿入します。この表の各行にはMODULETBL_T型のネストした表が格納され、この表にはMODULE_Tオブジェクトの行が格納されます。
最初に、スカラー値を設定します(id、name、start_date、duration)。次に、ネストした表の値を設定します。ネストした表の要素は複数の属性を持つオブジェクトなので、抽象化も行います。ネストした表の値を設定するとき、ネストした表の各MODULE_Tオブジェクトに対して、属性値を設定する必要があります。最後に、owner値(初期値NULL)を別の文で設定します。
// Insert Nested table details along with master details
public static void insertProject2(int id) throws Exception
{
System.out.println("Inserting Project with Nested Table details..");
try {
#sql { INSERT INTO Projects(id,name,owner,start_date,duration, modules)
VALUES ( 600, 'Ruby', null, '10-MAY-98', 300,
moduletbl_t(module_t(6001, 'Setup ', null, '01-JAN-98', 100),
module_t(6002, 'BenchMark', null, '05-FEB-98',20) ,
module_t(6003, 'Purchase', null, '15-MAR-98', 50),
module_t(6004, 'Install', null, '15-MAR-98',44),
module_t(6005, 'Launch', null,'12-MAY-98',34))) };
} catch ( Exception e) {
System.out.println("Error:insertProject2");
e.printStackTrace();
}
// Assign project owner to this project
try {
#sql { UPDATE Projects pr
SET owner=(SELECT ref(pa) FROM participants pa WHERE pa.empno = 7698)
WHERE pr.id=600 };
} catch ( Exception e) {
System.out.println("Error:insertProject2:update");
e.printStackTrace();
}
}
この例では、ネストした表を詳細レベルで直接操作します。前述のとおり、ModuletblTは、ネストした表MODULETBL_Tに対するJPublisher生成のカスタム・コレクション・クラス(ORAData実装)、ModuleTはMODULE_Tオブジェクトに対するJPublisher生成のカスタム・オブジェクト・クラスです。ネストした表MODULETBL_TにはMODULE_Tオブジェクトが格納されます。
MODULE_Tオブジェクトのネストした表をprojects表のmodules列から選択し、ModuletblTホスト変数に格納します。
次に、getArray()メソッドを介してネストした表の要素にアクセスするメソッドに、ネストした表が格納されているModuletblT変数を渡します。getArray()メソッドは、データをModuleT[]配列に書き込みます。JPublisherで生成したカスタム・コレクション・クラスには、必ずgetArray()メソッドがあります。その後、このModuleT[]配列の各要素をModuleTオブジェクトにコピーし、各属性をアクセッサ・メソッド(getModuleName()など)で取得し、出力します。JPublisherで生成したカスタム・オブジェクト・クラスには、必ずこのようなアクセッサ・メソッドがあります。
static ModuletblT mymodules=null;
...
public static void getModules2(int projId)
throws Exception
{
System.out.println("Display modules for project " + projId );
try {
#sql {SELECT modules INTO :mymodules
FROM projects WHERE id=:projId };
showArray(mymodules);
} catch(Exception e) {
System.out.println("Error:getModules2");
e.printStackTrace();
}
}
public static void showArray(ModuletblT a)
{
try {
if ( a == null )
System.out.println( "The array is null" );
else {
System.out.println( "printing ModuleTable array object of size "
+a.length());
ModuleT[] modules = a.getArray();
for (int i=0;i<modules.length; i++) {
ModuleT module = modules[i];
System.out.println("module "+module.getModuleId()+
", "+module.getModuleName()+
", "+module.getModuleStartDate()+
", "+module.getModuleDuration());
}
}
}
catch( Exception e ) {
System.out.println("Show Array");
e.printStackTrace();
}
}
この例では、TABLE構文を使用して、ネストした表を詳細レベルで操作します。マスター・レベルの基準に基づき、ネストした表の要素に直接アクセスして更新します。
assignModule()メソッドを使用すると、PROJECTS表のMODULES列からMODULE_Tオブジェクトのネストした表が選択された後、このネストして表の特定の行のMODULE_NAMEが更新されます。同様に、deleteUnownedModules()メソッドを使用すると、MODULE_Tオブジェクトのネストした表が選択され、その後でこのネストした表で所有者のいないモジュール(MODULE_OWNERがNULLのモジュール)が削除されます。
これらのメソッドでは、前述のように表の別名構文を使用します。この例では、ネストした表にm、participants表にpを使用しています。
/* assignModule
Illustrates accessing the nested table using the TABLE construct
and updating the nested table row
*/
public static void assignModule(int projId, String moduleName,
String modOwner) throws Exception
{
System.out.println("Update:Assign '"+moduleName+"' to '"+ modOwner+"'");
try {
#sql {UPDATE TABLE(SELECT modules FROM projects WHERE id=:projId) m
SET m.module_owner=
(SELECT ref(p) FROM participants p WHERE p.ename= :modOwner)
WHERE m.module_name = :moduleName };
} catch(Exception e) {
System.out.println("Error:insertModules");
e.printStackTrace();
}
}
/* deleteUnownedModules
// Demonstrates deletion of the Nested table element
*/
public static void deleteUnownedModules(int projId)
throws Exception
{
System.out.println("Deleting Unowned Modules for Project " + projId);
try {
#sql { DELETE TABLE(SELECT modules FROM projects WHERE id=:projId) m
WHERE m.module_owner IS NULL };
} catch(Exception e) {
System.out.println("Error:deleteUnownedModules");
e.printStackTrace();
}
}
SQLJでは、ネストされているイテレータを使用して、ネストした表にアクセスできます。このためには、次の例に使用されているCURSOR構文を使用する必要があります。コードで名前付きイテレータ・クラスModuleIterを定義します。このクラスをmodules列の型として、別の名前付きイテレータ・クラスProjIterで使用します。設定したProjIterインスタンスの中の各modules項目は、ネスト・イテレータとして表されたネストした表です。
CURSOR構文は、ネストされているSELECT文の要素であり、ネストされているイテレータを移入します。選択されたデータは、イテレータのアクセッサ・メソッドによってユーザーに出力されます。
この例では、前述のように、表の別名構文を使用します。ここでは、projects表にはaを使用し、ネストした表にはbを使用しています。
...
// The Nested Table is accessed using the ModuleIter
// The ModuleIter is defined as Named Iterator
#sql public static iterator ModuleIter(int moduleId ,
String moduleName ,
String moduleOwner);
// Get the Project Details using the ProjIter defined as
// Named Iterator. Notice the use of ModuleIter:
#sql public static iterator ProjIter(int id,
String name,
String owner,
Date start_date,
ModuleIter modules);
...
public static void listAllProjects() throws SQLException
{
System.out.println("Listing projects...");
// Instantiate and initialize the iterators
ProjIter projs = null;
ModuleIter mods = null;
#sql projs = {SELECT a.id,
a.name,
initcap(a.owner.ename) as "owner",
a.start_date,
CURSOR (
SELECT b.module_id AS "moduleId",
b.module_name AS "moduleName",
initcap(b.module_owner.ename) AS "moduleOwner"
FROM TABLE(a.modules) b) AS "modules"
FROM projects a };
// Display Project Details
while (projs.next()) {
System.out.println( "\n'" + projs.name() + "' Project Id:"
+ projs.id() + " is owned by " +"'"+ projs.owner() +"'"
+ " start on "
+ projs.start_date());
// Notice the modules from the ProjIter are assigned to the module
// iterator variable
mods = projs.modules();
System.out.println ("Modules in this Project are : ");
// Display Module details
while(mods.next()) {
System.out.println (" "+ mods.moduleId() + " '"+
mods.moduleName() + "' owner is '" +
mods.moduleOwner()+"'" );
} // end of modules
mods.close();
} // end of projects
projs.close();
}
ここでは、VARRAYをホスト式に取り出す例を示します。次のSQL定義を想定します。
CREATE TYPE PHONE_ARRAY IS VARRAY (10) OF varchar2(30) / /*** Create ADDRESS UDT ***/ CREATE TYPE ADDRESS AS OBJECT ( street VARCHAR(60), city VARCHAR(30), state CHAR(2), zip_code CHAR(5) ) / /*** Create PERSON UDT containing an embedded ADDRESS UDT ***/ CREATE TYPE PERSON AS OBJECT ( name VARCHAR(30), ssn NUMBER, addr ADDRESS ) / CREATE TABLE employees ( empnumber INTEGER PRIMARY KEY, person_data REF person, manager REF person, office_addr address, salary NUMBER, phone_nums phone_array ) /
PHONE_ARRAY SQL型からマッピングするPhoneArrayカスタム・コレクション・クラスを、JPublisherで生成するとします。
次のメソッドでこの表から行を選択し、データをPhoneArray型のホスト変数に格納します。
private static void selectVarray() throws SQLException
{
PhoneArray ph;
#sql {select phone_nums into :ph from employees where empnumber=2001};
System.out.println(
"there are "+ph.length()+" phone numbers in the PhoneArray. They are:");
String [] pharr = ph.getArray();
for (int i=0;i<pharr.length;++i)
System.out.println(pharr[i]);
}
ここでは、ホスト式からVARRAYにデータを挿入する例を示します。前例と同じSQL定義とカスタム・コレクション・クラス(PhoneArray)を使用します。
次のメソッドはPhoneArrayインスタンスを定義し、ホスト変数として使用して、データをデータベース内のVARRAYに挿入します。
// creates a varray object of PhoneArray and inserts it into a new row
private static void insertVarray() throws SQLException
{
PhoneArray phForInsert = consUpPhoneArray();
// clean up from previous demo runs
#sql {delete from employees where empnumber=2001};
// insert the PhoneArray object
#sql {insert into employees (empnumber, phone_nums)
values(2001, :phForInsert)};
}
private static PhoneArray consUpPhoneArray()
{
String [] strarr = new String[3];
strarr[0] = "(510) 555.1111";
strarr[1] = "(617) 555.2222";
strarr[2] = "(650) 555.3333";
return new PhoneArray(strarr);
}
Javaオブジェクトのインスタンスをデータベースとの間で読み書きする場合、Javaクラスに対応するSQLオブジェクト型を定義して、前述のカスタムJavaクラスのマッピング機構を使用すると、利便性が高まることがあります。これによって、Javaオブジェクトへの完全なSQL問合せが可能になります。
ただし、RAWまたはBLOB型のデータベース列を使用して、Javaオブジェクトをそのまま格納した後で取り出す場合もあります。このためには、2つの方法があります。
標準でない型マップの拡張機能を使用するか、型コード・フィールドをシリアライズ可能なクラスに追加することによって、シリアライズ可能なJavaクラスをRAWまたはBLOB列にマップできます。これにより、RAWまたはBLOBとしてシリアライズ可能なクラスのインスタンスを格納できます。
ORAData機能を使用して、RAWまたはBLOB列に格納できるインスタンスを持つ、シリアライズ可能なラッパー・クラスを定義できます。
これらの方法で実行するシリアライズは、Oracle SQLJランタイム・ライブラリのみ対象です。
ここでは、次の項目について説明します。
RAW列またはBLOB列内に直接Javaクラスのインスタンスを格納する場合は、SQLとJavaのマッピングを指定する非標準要件を満たす必要があります。SQLJ文内では、組込み型のように透過的に、シリアライズ可能なJavaオブジェクトを読み書きできます。
SQLとJavaのマッピングを指定する場合、2つのオプションがあります。
型マップを接続コンテキスト宣言で宣言して、この型マップでマッピングを指定します。
public static finalフィールド_SQL_TYPECODEを使用して、マッピングを指定します。
シリアライズ可能なクラスに対する型マップの定義
SAddress、pack.SPersonおよびpack.Manager.InnerSPM(InnerSPMはManagerの内部クラスです)がシリアライズ可能なJavaクラスであるとします。つまり、これらのクラスはjava.io.Serializableインタフェースを実装します。
このクラスは、宣言済の接続コンテキスト型の明示的接続コンテキスト・インスタンスを使用する文でのみ採用する必要があります。次のSerContextがその例です。
SAddress a =...;
pack.SPerson p =...;
pack.Manager.InnerSPM pm =...;
SerContext ctx = new SerContext(url,user,pwd,false);
#sql [ctx] { ... :a ... :OUT p ... :INOUT pm ... };
この場合の要件を次に示します。
接続コンテキスト型は、java.util.PropertyResourceBundleを実装している関連クラスを指定するwith句のtypeMap属性で宣言されている必要があります。例では、SerContextが次のように宣言されています。
#sql public static context SerContext with (typeMap="SerMap");
型マップ・リソースはRAW列またはBLOB列から、シリアライズ可能なJavaクラスまでに非標準マッピングを提供する必要があります。このマッピングは、次の形式のエントリで指定します。JavaクラスがRAW列またはBLOBのいずれの列にマップされているかによって、形式が異なります。
oracle-class.java_class_name=JAVA_OBJECT RAW oracle-class.java_class_name=JAVA_OBJECT BLOB
キーワードoracle-classは、これがOracle固有の拡張機能であることを示しています。例では、SerMap.propertiesリソース・ファイルには次のエントリが含まれます。
oracle-class.SAddress=JAVA_OBJECT RAW oracle-class.pack.SPerson=JAVA_OBJECT BLOB oracle-class.packManager$InnerSPM=JAVA_OBJECT RAW
パッケージとクラス名の分割にはピリオド(.)を使用しますが、内部クラス名を分割するにはドル記号($)を使用する必要があります。
このOracle固有の拡張機能は、標準SQLData型マップ・エントリと同じ型マップ・リソースに配置できます。
フィールド使用によるシリアライズ可能クラスのマッピングの指定
シリアライズ可能クラスに型マップを使用する別の方法として、シリアライズ可能クラスのstaticフィールドを使用して、型マッピングを指定できます。前述の例のSAddressおよびSPersonクラスなど、java.io.Serializableインタフェースを実装するクラスに次のフィールドのいずれかを追加できます。
public final static int _SQL_TYPECODE = oracle.jdbc.OracleTypes.RAW;
public final static int _SQL_TYPECODE = oracle.jdbc.OracleTypes.BLOB;
|
注意: 手動でのクラスへの_SQL_TYPECODEフィールド追加は、型マップ機能の使用に置き換えられました。 |
Javaオブジェクトのシリアライズにおける制限
シリアライズの効果について、認識しておく必要があります。2つのオブジェクトAおよびBが、同じオブジェクトCを共有している場合、AおよびBのシリアライズと以降のシリアライズ解除に際して、それぞれがオブジェクトCのクローンに焦点をあてるため、共有は解かれます。
さらに、与えられたJavaクラスに対して宣言できるシリアライズは、RAWまたはBLOBの1種類のみです。SQLJトランスレータは、実際の使用方法がRAWまたはBLOBのいずれかに適合することのみをチェックします。
RAW列では、サイズが制限されています。シリアライズしたJavaオブジェクトが、列のサイズを超えると、ランタイム・エラーが発生します。
BLOB列の列サイズ制限がより緩やかです。 シリアライズされたJavaオブジェクトのBLOB列への書込みは、Oracle JDBC Oracle Call Interface(OCI)ドライバおよびOracle JDBC Thinドライバでサポートされます。BLOB列からのシリアライズされたオブジェクトの取得は、Oracle9i 以上のすべてのOracle JDBCドライバでサポートされています。
最後に、シリアライズされたJavaオブジェクトをこの方法で処理することは、Oracle固有の拡張機能であり、Oracle SQLJランタイムと、デフォルトのOracle固有コード生成(変換時の-codegen=oracle)またはISO標準コード生成(-codegen=iso)の場合はOracle固有のプロファイルのカスタマイズが必要です。
|
注意: この特定のシリアライズ・メカニズムの実装では、JDBC型マップは使用しません。BLOBまたはRAWに対するマップは、変換時にカスタマイズされたOracleのプロファイルにハードコードされるか、Javaコードに直接生成されます。 |
oracle.sql.STRUCT、oracle.sql.REFまたはoracle.sql.ARRAY以外のoracle.sql.*型にマッピングするカスタムJavaクラスの定義例は、「ORAData実装のその他の使用例」を参照してください。
RAWフィールドに対してJavaオブジェクトのシリアライズおよびシリアライズ解除を行う場合の例では、カスタムJavaクラスをoracle.sql.RAW型にマッピングしています。これは、BLOBフィールドにも同様に当てはまり、カスタムJavaクラスをoracle.sql.BLOB型にマッピングします。
ここでは、このようなアプリケーションの例として、ORADataインタフェースを実装するSerializableDatumクラスの作成を説明します。このクラスは、汎用形式のカスタムJavaクラスに準拠しています。この例では、SerializableDatumの開発手順を詳細に示してから、完全なサンプル・コードを示します。
|
注意: このアプリケーションでは、java.io、java.sql、oracle.sqlおよびoracle.jdbcパッケージのクラスを使用しています。インポート文はここには含まれていません。 |
まず、このクラスのスケルトンから始めます。
public class SerializableDatum implements ORAData
{
// Client methods for constructing and accessing the Java object
public Datum toDatum(java.sql.Connection c) throws SQLException
{
// Implementation of toDatum()
}
public static ORADataFactory getORADataFactory()
{
return FACTORY;
}
private static final ORADataFactory FACTORY =
// Implementation of an ORADataFactory for SerializableDatum
// Construction of SerializableDatum from oracle.sql.RAW
public static final int _SQL_TYPECODE = OracleTypes.RAW;
}
SerializableDatum自体では、ORADataFactoryインタフェースが実装されることはありませんが、このクラスのgetORADataFactory()メソッドを使用すると、このインタフェースを実装したstaticメンバーが戻されます。
_SQL_TYPECODEをOracleTypes.RAWに設定します。データベースに対してこのデータ型の読込みおよび書込みを行うためです。SQLJトランスレータは、この型コード情報に基づき、オンラインで型チェックを行い、ユーザー定義のJava型とSQL型間の互換性を検証します。
次の処理を行うクライアント・メソッドを定義します。
SerializableDatumオブジェクトの作成
SerializableDatumオブジェクトの設定
SerializableDatumオブジェクトからのデータの取得
// Client methods for constructing and accessing a SerializableDatum
private Object m_data;
public SerializableDatum()
{
m_data = null;
}
public void setData(Object data)
{
m_data = data;
}
public Object getData()
{
return m_data;
}
toDatum()メソッドを実装して、SerializableDatumオブジェクトからoracle.sql.RAWオブジェクトへデータをシリアライズします。toDatum()の実装では、m_dataフィールドのオブジェクトをoracle.sql.RAWインスタンスとしてシリアライズした表現で戻す必要があります。
// Implementation of toDatum()
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(m_data);
oos.close();
return new RAW(os.toByteArray());
} catch (Exception e) {
throw new SQLException("SerializableDatum.toDatum: "+e.toString()); }
oracle.sql.RAWオブジェクトからSerializableDatumオブジェクトへのデータ変換を実装します。この段階で、データをシリアライズ解除します。
// Constructing SerializableDatum from oracle.sql.RAW
private SerializableDatum(RAW raw) throws SQLException
{
try {
InputStream rawStream = new ByteArrayInputStream(raw.getBytes());
ObjectInputStream is = new ObjectInputStream(rawStream);
m_data = is.readObject();
is.close();
} catch (Exception e) {
throw new SQLException("SerializableDatum.create: "+e.toString()); }
}
ORADataFactoryを実装します。この例では、無名クラスとして実装します。
// Implementation of an ORADataFactory for SerializableDatum
new ORADataFactory()
{
public ORAData create(Datum d, int sqlCode) throws SQLException
{
if (sqlCode != _SQL_TYPECODE)
{
throw new SQLException
("SerializableDatum: invalid SQL type "+sqlCode);
}
return (d==null) ? null : new SerializableDatum((RAW)d);
}
};
ここでは、前項で作成したSerializableDatumクラスのインスタンスをホスト変数およびイテレータ列として、SQLJアプリケーションで使用する方法を示します。
次の表定義を想定します。
CREATE TABLE PERSONDATA (NAME VARCHAR2(20) NOT NULL, INFO RAW(2000));
次に、SerializableDatumインスタンスをホスト変数として使用します。
...
SerializableDatum pinfo = new SerializableDatum();
pinfo.setData (
new Object[] {"Some objects", new Integer(51), new Double(1234.27) } );
String pname = "MILLER";
#sql { INSERT INTO persondata VALUES(:pname, :pinfo) };
...
次に、SerializableDatumを名前付きイテレータの列として使用する例を示します。
#sql iterator PersonIter (SerializableDatum info, String name);
...
PersonIter pcur;
#sql pcur = { SELECT * FROM persondata WHERE info IS NOT NULL };
while (pcur.next())
{
System.out.println("Name:" + pcur.name() + " Info:" + pcur.info());
}
pcur.close();
...
次に、前項で詳細手順を示したSerializableDatumクラスのすべてのコードを示します。
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.*;
public class SerializableDatum implements ORAData
{
// Client methods for constructing and accessing a SerializableDatum
private Object m_data;
public SerializableDatum()
{
m_data = null;
}
public void setData(Object data)
{
m_data = data;
}
public Object getData()
{
return m_data;
}
// Implementation of toDatum()
public Datum toDatum(Connection c) throws SQLException
{
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(m_data);
oos.close();
return new RAW(os.toByteArray());
} catch (Exception e) {
throw new SQLException("SerializableDatum.toDatum: "+e.toString()); }
}
public static ORADataFactory getORADataFactory()
{
return FACTORY;
}
// Implementation of an ORADataFactory for SerializableDatum
private static final ORADataFactory FACTORY =
new ORADataFactory()
{
public ORAData create(Datum d, int sqlCode) throws SQLException
{
if (sqlCode != _SQL_TYPECODE)
{
throw new SQLException(
"SerializableDatum: invalid SQL type "+sqlCode);
}
return (d==null) ? null : new SerializableDatum((RAW)d);
}
};
// Constructing SerializableDatum from oracle.sql.RAW
private SerializableDatum(RAW raw) throws SQLException
{
try {
InputStream rawStream = new ByteArrayInputStream(raw.getBytes());
ObjectInputStream is = new ObjectInputStream(rawStream);
m_data = is.readObject();
is.close();
} catch (Exception e) {
throw new SQLException("SerializableDatum.create: "+e.toString()); }
}
public static final int _SQL_TYPECODE = OracleTypes.RAW;
}
SQLJでは、弱い型指定のオブジェクト、参照およびコレクションを使用できます。一般的には、これらのクラスの使用はお薦めしません。使用方法も制限されます。ただし、便利な場合もあります。たとえば、汎用コードを記述して、どのようなSTRUCTまたはREFでも使用できます。
ここでは、次の項目について説明します。
Oracleのオブジェクト、参照またはコレクションをSQLJアプリケーションで使用する場合は、強い型指定のカスタム・オブジェクト・クラス、参照クラスまたはコレクション・クラス(ORADataインタフェースを実装したクラス)または強い型指定のカスタム・オブジェクト・クラス(SQLDataインタフェースを実装したクラス)を使用するかわりに、弱い型指定の汎用java.sqlインスタンスまたはoracle.sqlインスタンスを使用できます。ただし、SQLData実装をカスタム・オブジェクト・クラスとして使用した場合、弱い型指定のカスタム参照インスタンスしか使用できません。
Oracle SQLJ実装では、イテレータ列またはホスト式に対して、次に示した弱い型指定を使用できます。
java.sql.Structまたはoracle.sql.STRUCT(オブジェクトの場合)
java.sql.Refまたはoracle.sql.REF(オブジェクト参照の場合)
java.sql.Arrayまたはoracle.sql.ARRAY(コレクションの場合)
ホスト式の場合は、次の方法で使用できます。
入力ホスト式として
INTOリストで出力ホスト式として
こうした弱い型指定は、通常は使用しないようにしてください。SQLJの強い型指定のパラダイムのあらゆるメリットが損なわれるためです。
STRUCTオブジェクト内の各属性またはARRAYオブジェクト内の各要素がoracle.sql.Datumオブジェクトに格納されます。基礎となるデータの形式は、Datumの該当するoracle.sql.*サブタイプ(oracle.sql.NUMBERやoracle.sql.CHARなど)です。STRUCTオブジェクト内の属性は匿名です。STRUCTおよびARRAYクラスは汎用クラスなので、これらのクラスのインスタンスに対してオブジェクトまたはコレクションの読込みや書込みを行う際は、SQLJで型チェックを行えません。
一般的には、オブジェクト、参照およびコレクションに対してカスタムJavaクラスの使用をお薦めします。可能な場合は、JPublisherで生成したクラスを使用してください。
弱い型指定のオブジェクト(StructまたはSTRUCTインスタンス)、参照(RefまたはREFのインスタンス)またはコレクション(ArrayまたはARRAYのインスタンス)は、ホスト式の次のパラメータとしては使用できません。
INパラメータとして(NULLの場合)
ストアド・プロシージャまたはストアド・ファンクションのコールで、OUTまたはINOUTパラメータとして
ストアド・ファンクションの結果式で、OUTパラメータとして
これらのパラメータとして使用できない理由は、元のSQL型の名前(Personなど)を特定できないからです。Oracle JDBCドライバは、元のSQL型の名前を使用して、Javaのユーザー定義型のインスタンスを生成します。
Oracle OPAQUE型は抽象データ型です。単なる一連のバイトとして実装されたデータの場合、内部表現は公開されません。通常、OPAQUE型はOracleで提供され、カスタマによる実装はありません。
OPAQUE型は、基本的な点でオブジェクト型に類似しています。つまり、staticメソッド、インスタンスおよびインスタンス・メソッドの概念が類似しています。一般的に、状態や内部バイト表現を操作できるのは、OPAQUE型を使用して提供されるメソッドのみです。Javaでは、oracle.sql.OPAQUEとして、またはoracle.sql.ORADataインタフェースを実装しているカスタム・クラスとして、OPAQUE型を表現できます。クライアント側では、Javaコードを実装してバイトを操作できます(バイト・パターンが判明していることが前提です)。Oracle Database 11g JPublisherユーティリティを使用すると、このようにORADataを実装するカスタム・クラスを作成し、データベースへのラウンドトリップを繰り返さずにデータを操作できます。
|
参照: 『Oracle Database JPublisherユーザーズ・ガイド』 |
OPAQUE型の主な例は、Oracle Database 11g で提供しているXMLTypeです。Oracleが提供する型によって、データベースのXMLデータをネイティブに処理することが容易になります。
SYS.XMLTypeによって次の機能が提供され、Javaのoracle.xdb.XMLTypeクラスを介して公開されます。
表またはビューの列のデータ型として使用できます。XMLTypeにはあらゆるコンテンツを格納できますが、XMLコンテンツを最適に格納するように設計されています。そのインスタンスはSQLでXMLドキュメントを表現できます。
XMLコンテンツの操作を行う組込みメンバー・ファンクションを含むSQL APIがあります。たとえば、XMLTypeファンクションを使用すると、Oracle Database 11g インスタンスに格納されているXMLデータの作成、問合せ、抽出および索引付けができます。
ストアド・プロシージャで、パラメータ、戻り値および変数に使用できます。
この機能は、PL/SQL、JavaおよびC(OCI)で提供されるAPIを介して使用することもできます。
|
参照: 『Oracle XML DB開発者ガイド』 |