ヘッダーをスキップ
Oracle® Database SQLJ開発者ガイド
11g リリース2 (11.2)
B56282-02
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

6 オブジェクト、コレクションおよびOPAQUE型

この章では、Oracle SQLJ実装でユーザー定義SQL型がどのようにサポートされるかを説明します。ユーザー定義SQL型に対応するJavaクラスを生成できるOracle JPublisherユーティリティについても説明します。この章の最後には、Oracle OPAQUE型に関する項もあります。

この章では、次の項目について説明します。

Oracleのオブジェクトとコレクションについて

ここでは、Oracle Database 11g のオブジェクトとコレクションの基本的な概念について説明します。


関連項目:

『Oracle Database SQL言語リファレンス』および『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』.

ここでは、次の項目について説明します。

オブジェクトとコレクションの概要

Oracle SQLJ実装では、ユーザー定義のSQLオブジェクト型(複合データ構造体)、関連するSQLオブジェクト参照型およびユーザー定義のSQLコレクション型がサポートされています。Oracleのオブジェクトとコレクションは、複合データ構造体であり、複数のデータ要素で構成されます。

Oracle SQLJ実装では、強い型指定または弱い型指定の、Java表現のオブジェクト型、参照型およびコレクション型を、イテレータまたはホスト式で使用できます。強い型指定の表現では、特定のオブジェクト型、参照型またはコレクション型にマッピングするカスタムJavaクラスを使用しますが、このクラスはJava Database Connectivity (JDBC) 2.0標準のjava.sql.SQLDataインタフェース(オブジェクト型の場合のみ)またはOracleのoracle.sql.ORADataインタフェースを実装する必要があります。どちらのインタフェースもOracle Database 11g JPublisherユーティリティでサポートされており、このユーティリティを使用するとカスタムJavaクラスを自動生成できます。

「強い型指定」という用語は、特定のJava型がSQL名前指定型またはユーザー定義型に対応付けられている場合に使用されます。たとえば、PERSON型がある場合、対応するPerson Javaクラスが対応付けられます。

弱い型指定の表現では、oracle.sql.STRUCT(オブジェクトの場合)、oracle.sql.REF(オブジェクト参照の場合)またはoracle.sql.ARRAY(コレクションの場合)を使用します。弱い型指定の表現では、標準java.sql.Structjava.sql.Refまたはjava.sql.Arrayオブジェクトもかわりに使用できます。

「弱い型指定」という用語は、Java型が汎用的な方法で使用され、複数のSQL名前指定型にマップできる場合に使用されます。Javaクラス(インタフェース)には、SQL型に特有の情報はありません。これは、oracle.sql.STRUCToracle.sql.REFおよびoracle.sql.ARRAY型とjava.sql.Structjava.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オブジェクトの基本概念

Oracle SQLオブジェクトは複合データ構造体であり、関連するデータ項目(各従業員に関する事実など)を1つのデータ単位にまとめたものです。オブジェクト型は、機能的にはJavaのクラスに相当します。Java型をインスタンス化してオブジェクトをいくつでも使用できるように、特定のオブジェクト型のオブジェクトをいくつでも設定して使用できます。

たとえば、CHAR型の属性nameCHAR型のaddressCHAR型のphonenumberおよびNUMBER型のemployeenumberを持つオブジェクト型EMPLOYEEを定義できます。

Oracleのオブジェクトのメソッド(ストアド・プロシージャ)は、オブジェクト型への対応付けができます。これらのメソッドは、staticメソッドまたはインスタンス・メソッドとして、PL/SQLまたはJavaで実装できます。メソッドのシグネチャとして、任意の数の入力、出力または入出力パラメータを使用できます。すべては初期定義によって決まります。

Oracleコレクションの基本概念

Oracle SQLコレクションは、次の2つのカテゴリに分類されます。

  • 可変長配列(VARRAY型)

  • ネストした表(NESTED TABLE型)

どちらのカテゴリも1次元ですが、その要素として複合オブジェクト型を使用できます。VARRAY型は1次元配列に対して使用しますが、NESTED TABLE型は表に含まれる単一列の表に対して使用します。VARRAY型の変数をVARRAYと呼びます。NESTED TABLE型の変数をネストした表と呼びます。

VARRAYは、配列と同様、データ要素の順序付けられた集合であり、各要素が1つの索引を持ち、すべての要素のデータ型が同じです。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のデータ型になります。NUMBER型の表列と同じように、EMPLOYEE型の表列を持つことができます。EMPLOYEE列の各行に、EMPLOYEEオブジェクト全体を格納できます。EMPLOYEEオブジェクトへの参照で構成されるREF EMPLOYEE型の列を持つこともできます。

同様に、NUMBERVARRAY(10)としてVARRAY型MYVARRを定義したり、CHAR(20)のNESTED TABLE型NTBLを定義できます。コレクション型MYVARRNTBLはOracleのデータ型になるので、これらの型の列を表に定義できます。MYVARR列の各行は、最大10数字を収容する配列です。NTBL列の各行は20文字で構成されます。

カスタムJavaクラス

カスタムJavaクラスはファーストクラスの型で、ユーザー定義SQL型に対する読取りおよび書込みを透過的に行う場合に使用できます。カスタムJavaクラスの用途は、SQLとJava間のデータの変換手段を提供し、データへのアクセスを可能にすることです。この機能は、特に、オブジェクトとコレクションのサポートにおいて、またはデータのカスタム変換を行う場合に重要です。

SQLJアプリケーションで使用するすべてのユーザー定義型に対して、カスタムJavaクラスを用意することをお薦めします。Oracle JDBCドライバではデータ変換にこのクラスのインスタンスが使用され、弱い型指定のoracle.sql.STRUCToracle.sql.REFおよびoracle.sql.ARRAYクラスを使用するも便利で、エラーも発生しにくくなります。

SQLJイテレータまたはホスト式で使用するには、カスタムJavaクラスにoracle.sql.ORADataおよびoracle.sql.ORADataFactoryインタフェース、または標準java.sql.SQLDataインタフェースを実装する必要があります。ここでは、これらのインタフェースとカスタムJavaクラス機能の概要を、次の項目について説明します。

カスタムJavaクラスのインタフェース指定

ここでは、ORADataおよびORADataFactoryインタフェースと標準SQLDataインタフェースの指定について説明します。

Oracle Database 11g には、ユーザー定義型(oracle.sql.ORADataおよびoracle.sql.ORADataFactory)に対するOracle固有のカスタムJavaクラス機能をサポートするための一連のAPIが含まれています。

以前この機能のために使用されていたoracle.sql.CustomDatumおよびoracle.sql.CustomDatumFactoryインタフェースは非推奨になりました。

ORADataおよびORADataFactoryの指定

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ドライバは、実行時に接続オブジェクトを使用して、型チェックと型変換を行います。ORADatatoDatum()は、次のように指定します。

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オブジェクトからデータを取り出します。ORADataFactorycreate()は、次のように指定します。

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ユーザーズ・ガイド』

ORADataとCustomDatumインタフェースの比較

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型を使用してください。

SQLDataの指定

標準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仕様を参照してください。

オブジェクト・メソッドでのカスタムJavaクラスのサポート

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クラスの要件

カスタム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_TYPECODEOracleTypes.RAWに初期化します。


    注意:

    OracleTypesクラスは、各Oracleデータ型の型コード(整数の定数)のみを定義します。標準SQL型のOracleTypesエントリは、標準java.sql.Types型定義クラスのエントリと同じです。

  • _SQL_TYPECODESTRUCTREFまたは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型が大/小文字を区別して(引用符で囲んで)宣言されている場合、CaseSensitiveSCOTT.CaseSensitiveなど、宣言されているとおりにSQL名を指定する必要があります。これは、引用符内で同様に名前の大/小文字を区別するJPublisher入力ファイルでの使用方法と異なります。SQL型を大/小文字を区別して宣言していない場合(引用符なし)、ADDRESSSCOTT.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.Refjava.sql.Array、またはoracle.sql.REForacle.sql.ARRAYを使用する方法のみです。

  • SQL型からJava型へのマッピングを指定するとき、SQL型が大/小文字を区別する方法で宣言されている場合、CaseSensitiveまたはSCOTT.CaseSensitiveなど宣言されているとおりにSQL名を指定する必要があります。これは、引用符内で同様に名前の大/小文字を区別するJPublisher入力ファイルでの使用方法と異なります。SQL型を大/小文字を区別して宣言していない場合、ADDRESSSCOTT.ADDRESSのように、すべて大文字でSQL名を指定する必要があります。

型マップ・リソースで指定したマッピング

まず、ISO SQLJ規格に従ったマッピング表現を想定します。Addresspack.Personおよびpack.Manager.InnerPM(InnerPMManagerの内部クラスです)が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文では無視されます。


注意:

  • _SQL_NAMEフィールドを実装するクラスが明示的接続コンテキスト型および関連型マップとともにSQLJ文内で使用されている場合、その型マップが使用され、_SQL_NAMEフィールドは無視されます。これにより、既存のSQLJプログラムのISO SQLJ規格への移行は簡素化されます。

  • _SQL_NAMEフィールドで指定した静的SQL-Java型の対応は、基礎となる接続で使用しているJDBC型マップからは独立しています。このため、SQLDataラッパーを使用しているSQLJコードおよびJDBCコードを混在させている場合は、注意が必要です。


カスタムJavaクラスのコンパイル

SQLJのコマンドラインには、カスタムJavaクラス(ORADataまたはSQLDataを実装)の.javaファイルを、アプリケーションの.sqljファイルと一緒に指定できます。ただし、この指定は、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 (デフォルト)に設定する必要があり、この設定にしない場合は移植性に関する多数の警告がSQLJから通知されます。-warnフラグの詳細は、「トランスレータからの警告(-warn)」を参照してください。

カスタム・データの読込みと書込み

カスタムJavaクラス・インスタンスを使用すると、Oracle SQLJおよびJDBC実装では、ユーザー定義型の読込みと書込みが可能になります(ただし、これらは組込み型です)。この仕組みをユーザーが意識することはありません。

ORAData実装およびSQLData実装でのデータの読込み/書込みの仕組みは、『Oracle Database JDBC開発者ガイドおよびリファレンス』を参照してください。

ORAData実装のその他の使用例

ここまでは、カスタム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つのオブジェクト型、PERSONADDRESS

  • 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)

  • PERSONADDRESSオブジェクト(「オブジェクト型の作成」に同じ定義が示されています)

  • 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
)
/

JPublisherとカスタムJavaクラスの作成

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オブジェクトを、それぞれ参照とコレクションにマッピングする方法のみです。

ここでは、次の項目について説明します。


関連項目:

  • 『Oracle Database JPublisherユーザーズ・ガイド』

  • 『Oracle Database JDBC開発者ガイドおよびリファレンス』


JPublisherで生成される内容

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固有の機能を使用するため、移植できなくなります。

  • JPublisherではCustomDatumインタフェースは非推奨になりましたが、このインタフェースの実装は-compatibleオプションによってサポートされています。


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.*クラスが使用されるためです)。

カスタムJavaクラスの生成

ここでは、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インタフェースが指定されます。


注意:

CustomDatumインタフェースは、ORADataに引き継がれ、Oracle9i データベースでは非推奨になりましたが、このインタフェースの実装が必要な場合は、JPublisherのcustomdatum-compatible設定を使用して実装が可能です。これに-usertypes=oracle設定を組み合せることによって、生成されたクラスにCustomDatumインタフェースが実装されます。デフォルトは-compatible=oradataです。

-compatible=8iまたは-compatible=both8iを設定しても、JPublisherでCustomDatumが使用され、同様にOracle8i のJPublisherと互換性のあるコード生成が実行されます。詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。


次に示すJPublisherのコマンドラインは、それぞれORADataCustomDatumおよび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では無視されます。

Javaにマッピングするユーザー定義型の指定

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の場合は、クラスHomeAddressHomeAddress.sqljまたは.javaのソース・ファイルに生成されます。


注意:

  • JPublisherコマンドラインでは、大/小文字を区別していないSQL名のみサポートされます。大/小文字を区別する方法でユーザー定義型をSQLに定義した場合、コマンドラインや引用符ではなくJPublisherのINPUTファイルに名前を指定する必要があります。

  • 旧バージョンのJPublisherとの下位互換用として、-sqlのかわりに-typesオプションを従来どおり使用できます。


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.sqljAddressRef.javaPerson.sqljPersonRef.javaPhoneArray.javaParticipantT.sqljParticipantTRef.javaModuleT.sqljModuleTRef.javaおよびModuletblT.javaが生成されます。ソース・ファイルの例は、「JPublisherのカスタムJavaクラスの例」を参照してください。

カスタムJavaクラスの設定方法を特定するために、JPublisherは指定されたスキーマに接続し、指定されたオブジェクト型の属性または指定されたコレクション型の要素を調べます。


注意:

単一の-sql設定に複数のマッピングを指定するかわりに、同一のコマンドラインに複数の-sqlオプションを使用できます。複数の-sqlオプションの効果は累積されます。

JPublisherで生成するメソッドおよび属性のデフォルト名の大/小文字の使用方法を変更するには、-caseオプションを使用します(オブジェクトまたはコレクションが属性の場合、つまり下位カスタムJavaクラスの場合も、このオプションを使用します)。次の4通りの設定があります。

  • -case=mixed(デフォルト)

    大文字になる文字は、クラス名を構成する各ワードの頭文字、属性名を構成する各ワードの頭文字およびメソッド名の2番目以降のワードの頭文字です。これ以外の文字はすべて小文字になります。JPublisherでは、アンダースコア(_)、ドル記号($)、およびJavaの識別子で無効な文字は、ワード間のセパレータとしてみなされます。これらの文字は処理時に破棄されます。

  • -case=same

    大/小文字区別は、データベース内での表現と同じになります。アンダースコアとドル記号は保持され、無効な文字は破棄されます。

  • -case=upper

    小文字が大文字に変換されます。アンダースコアとドル記号は保持され、無効な文字は破棄されます。

  • -case=lower

    大文字が小文字に変換されます。アンダースコアとドル記号は保持され、無効な文字は破棄されます。


注意:

JPublisherの実行時にJavaにマッピングするユーザー定義型を指定しないと、スキーマ内のすべてのユーザー定義型が処理されます。最上位カスタム・クラスとその他のクラス(オブジェクトの属性またはコレクションの要素)のクラス名は、-caseオプションに基づき生成されます。

型のマッピング指定

JPublisherには、ユーザー定義型およびその属性型や要素型をSQLとJavaとの間でマッピングする方法が数通りあります。

JPublisherではSQL型が次のようなグループに分類されています。各グループに対応するJPublisherオプションは、各項に示したとおりです。

  • 数値型: SQL型NUMBERとして格納されるすべての型

    JPublisherの-numbertypesオプションは、数値型のマッピング指定に使用します。

  • ラージ・オブジェクト(LOB)型: SQL型のBLOBおよびCLOB

    JPublisherの-lobtypesオプションは、LOB型のマッピング指定に使用します。

  • 組込み型: データベース内にSQL型として格納されている型のうち、前に示した分類のどれにも該当しない型(たとえば、CHARVARCHAR2LONGRAWなど)

    JPublisherの-builtintypesオプションは、組込み型のマッピング指定に使用します。

JPublisherでは次の型マッピング・モードを定義します。

  • JDBCマッピング(設定はjdbc): SQL型とネイティブなJava型とのマッピングに標準デフォルト・マッピング型を使用します。この設定は、-numbertypes-lobtypesおよび-builtintypesオプションに有効です。

  • Oracleマッピング(設定はoracle): SQL型へのマッピングの際に、対応するoracle.sql型を使用します。この設定は、-numbertypes-lobtypesおよび-builtintypesオプションに有効です。

  • Object JDBCマッピング(設定はobjectjdbc): JDBCマッピングをさらに拡張したマッピングです。オブジェクトJDBCマッピングでは、適切であれば、Javaプリミティブ型(intfloatdoubleなど)のかわりに、標準java.langパッケージの数値オブジェクト型(java.lang.IntegerFloatDoubleなど)が使用されます。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型

-usertypes

oraclejdbc

oracle

数値型

-numbertypes

oraclejdbcobjectjdbcbigdecimal

objectjdbc

LOB型

-lobtypes

oraclejdbc

oracle

組込み型

-builtintypes

oraclejdbc

jdbc



注意:

以前のリリースで使用された-mappingオプションは、JPublisherでは非推奨ですが、サポートはされています。JPublisherの-mappingオプション設定値を新しいマッピング・オプション設定値に変換する方法の詳細は、『Oracle Database JPublisherユーザーズ・ガイド』を参照してください。

ラッパー・メソッドの生成

OracleオブジェクトをJavaにマッピングするためのカスタム・オブジェクト・クラスを作成する際、-methodsオプションは、OracleオブジェクトのメソッドにJavaラッパーを含めるかどうかをJPublisherに指示します。デフォルトの-methods=true設定では、ラッパーが生成され、JPublisherではカスタム・オブジェクト・クラス用に.javaファイルではなく.sqljファイルが生成されます(ただし、基になるSQLオブジェクトに実際にメソッドない場合を除きます)。

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ファンクションに対しても実行されます。最上位レベルではオーバーロードが許容されません。

カスタムJavaクラスの生成および代替クラスのマッピング

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クラスの初期バージョンが生成されます。存在している場合、ファイルは変更されません。

MyAddressJAddressにサブクラス化されます。MyAddressに対して拡張機能を実装するためには、JPublisherで生成されたMyAddressソース・ファイルを使用し、必要に応じてそのファイルを編集できます。

JPublisher生成クラスを拡張する方法(前例の続き)は、「JPublisher生成クラスの拡張」を参照してください。

JPublisherのINPUTファイルおよびプロパティ・ファイル

JPublisherでは、専用のINPUTファイルおよび標準のプロパティ・ファイルを使用して、型のマッピングなどのオプション設定を指定できます。

JPublisherのINPUTファイルの使用方法

JPublisherのコマンドライン・オプション-inputを使用して、型マッピングを追加するためのINPUTファイルをJPublisherに指定できます。INPUTファイル内のSQLは、コマンドラインの-sqlに相当し、ASまたはGENERATE...AS構文は、コマンドラインのコロン区切り構文に相当します。次の構文を使用しますが、1つのSQLコマンドに指定できるマッピングは1つだけです。

SQL udt1 <GENERATE GeneratedClass1> <AS MapClass1>
SQL udt2 <GENERATE GeneratedClass2> <AS MapClass2>
...

GeneratedClass1GeneratedClass2 が生成され、udt1MapClass1 に、udt2MapClass2 にマッピングされます。


注意:

ユーザー定義型が大/小文字を区別する方法でSQLに定義されている場合、引用符内に名前を指定する必要があります。次に例を示します。
SQL "CaseSenstiveType" AS CaseSensitiveType

大/小文字を区別しないスキーマ名を指定する場合も、次のようになります。

SQL SCOTT."CaseSensitiveType" AS CaseSensitiveType

または、大/小文字を区別するスキーマ名を指定する場合、次のようになります。

SQL "Scott"."CaseSensitiveType" AS CaseSensitiveType

AS句はオプションです。

スキーマ名または型名自体の一部として、ピリオド(.)を使用しないでください。


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ソース・ファイルが生成されます。

  • ユーザー定義型EMPLOYEEMyEmployeeクラスにマッピングされます。JPublisherでは、ファイルがすでに存在していないかぎり、初期バージョンのMyEmployee.sqlj(または.java)に加え、ソース・ファイルEmployee.sqlj(またはEmployee.java)およびMyEmployeeRef.javaが作成されます。データベースから取得したオブジェクトにEMPLOYEE属性があると、Javaでこの属性がMyEmployeeのインスタンスとして生成されます。EMPLOYEEオブジェクトを直接取得する場合は、MyEmployeeのインスタンスに取り込まれます。MyEmployeeコードに関する責任は開発者にありますが、EMPLOYEEオブジェクトに対して特別な機能をJavaで実装するためには、JPublisherで生成したMyEmployeeソース・ファイルを利用して、そのファイルを編集できます。MyEmployeeEmployeeクラスにサブクラス化されます。

JPublisherのプロパティ・ファイルの使用方法

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クラスの作成およびメンバー名の指定

カスタム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

注意:

  • メンバー名を指定すると、それ以外の指定しなかったメンバーすべてにデフォルトの名前が与えられます。

  • 属性を大文字(homeAddressではなくHomeAddress)で指定する理由は、アクセッサ・メソッドと同じネーミング規則が適用されるためです。たとえば、getHomeAddress()はネーミング規則に従っていますが、gethomeAddress()は従っていません。


JPublisherで生成されるラッパー・メソッド

ここでは、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のカスタムJavaクラスの例

ここでは、次のユーザー定義型に対して、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の属性はstreetzip_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生成クラスの拡張

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.javaMyAddressRefであって、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を実装する)は、AddressJAddressに置換されることを除き、前述のAddressクラスのコードとほぼ同じです。

JPublisher生成のその他の参照クラス: MyAddressRef.java

これまでの項の例の続きとして、JPublisher生成の参照クラスMyAddressRefのコードを検討します(MyAddressクラスがADDRESSオブジェクトのマッピング先となるため、ここで取り上げるクラスはJAddressRefではありません)。また、このクラスにはORADataORADataFactoryも実装されます。この実装は、クラス名が異なることと、アクセッサ・メソッドが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.
  */
}

SQLJ実行文の強い型指定のオブジェクトと参照

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で生成するか、または手動で作成すると、次のようにAddressAddressRefとを名前付きイテレータで使用できます。

#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生成のカスタム参照クラスの標準機能です。


注意:

次の例では、「オブジェクト型の作成」のSQLスクリプトで定義した型と表を使用します。

オブジェクトの更新

この例では、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言語リファレンス』を参照してください。

SQLJ実行文の強い型指定のコレクション

Oracle SQLJ実装では、強い型指定のオブジェクトおよび参照と同じように、強い型指定のコレクションをイテレータまたはホスト式で使用して、データの読込みおよび書込みを行えます。

SQLJ開発者にとっては、2種類のコレクション(VARRAYとネストした表)の扱いは基本的に同じですが、実装とパフォーマンスの面で相違点が多少あります。

Oracle SQLJ実装では、構文の複数の選択肢がサポートされていて、ネストした表(内側の表)にアクセスして、操作できるようになっています(外側の表から切り離して、または外側の表と一緒に操作することも可能です)。ここでは、ネストの内側の表のみの操作を詳細レベルの操作と呼び、ネストの内側の表と外側の表の同時操作をマスター・レベルの操作と呼びます。

ここでは、いくつかの構文について簡単に説明してから、ネストした表の操作例を示します。ネストした表の操作は、VARRAYの操作より複雑です。

MODULETBL_T Oracleコレクション型と関連する表、およびオブジェクト型の定義については、「コレクション型の作成」を参照してください。


注意:

Oracle SQLJ実装では、VARRAY型およびNESTED TABLE型を型単位でのみ取得できます。これは、Oracle SQL実装とは違い、ネストした表の問合せを選択的に実行できます。

ここでは、次の項目について説明します。

ネストした表へのアクセス: TABLE構文とCURSOR構文

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オブジェクトの行が含まれています。

最初に、スカラー値を設定し(idnamestart_dateduration)、ネストした表の値を設定します。ネストした表の要素は複数の属性を持つオブジェクトであるため、別のレベルの抽象化も行いますネストした表の値を設定するとき、ネストした表の各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実装)であり、ModuleTMODULE_Tオブジェクトに対するJPublisher生成のカスタム・オブジェクト・クラスです。ネストした表MODULETBL_TにはMODULE_Tオブジェクトが格納されます。

MODULE_Tオブジェクトのネストした表をprojects表のmodules列から選択し、ModuletblTホスト変数に格納します。

次に、ネストした表を含むModuletblT変数をあるメソッドに渡し、そのメソッドはgetArray()メソッドを使用して要素にアクセスします。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構文によるネストした表の操作

この例では、TABLE構文を使用して、ネストした表を詳細レベルで操作します。マスター・レベルの基準に基づき、ネストした表の要素に直接アクセスして更新します。

assignModule()メソッドを使用すると、PROJECTS表のMODULES列からMODULE_Tオブジェクトのネストした表が選択された後、このネストして表の特定の行のMODULE_NAMEが更新されます。同様に、deleteUnownedModules()メソッドを使用すると、MODULE_Tオブジェクトのネストした表が選択され、その後でこのネストした表で所有者のいないモジュール(MODULE_OWNERNULLのモジュール)が削除されます。

これらのメソッドでは、前述のように表の別名構文を使用します。この例では、ネストした表にmparticipants表に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では、ネストした表にアクセスする1つ方法として、ネストしたイテレータの使用があります。このためには、次の例で使用されている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の取出し

ここでは、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行への挿入

ここでは、ホスト式から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オブジェクトのインスタンスをデータベースとの間で読み書きする場合、Javaクラスに対応するSQLオブジェクト型を定義して、前述のカスタムJavaクラスのマッピング機構を使用すると、利便性が高まることがあります。これによって、Javaオブジェクトへの完全なSQL問合せが可能になります。

ただし、RAWまたはBLOB型のデータベース列を使用して、Javaオブジェクトをそのまま格納した後で取り出す場合もあります。このためには、2つの方法があります。

  • 標準でない型マップの拡張機能を使用するか、型コード・フィールドをシリアライズ可能なクラスに追加することによって、シリアライズ可能なJavaクラスをRAWまたはBLOB列にマップできます。これにより、RAWまたはBLOBとしてシリアライズ可能なクラスのインスタンスを格納できます。

  • ORAData機能を使用して、RAWまたはBLOB列に格納できるインスタンスを持つ、シリアライズ可能なラッパー・クラスを定義できます。

これらの方法で実行するシリアライズは、Oracle SQLJランタイム・ライブラリのみ対象です。

ここでは、次の項目について説明します。

RAWおよびBLOB列に対するJavaクラスのシリアライズ

RAW列またはBLOB列内に直接Javaクラスのインスタンスを格納する場合は、SQLとJavaのマッピングを指定する非標準要件を満たす必要があります。SQLJ文内では、組込み型のように透過的に、シリアライズ可能なJavaオブジェクトを読み書きできます。

SQLとJavaのマッピングを指定する場合、2つのオプションがあります。

  • 型マップを接続コンテキスト宣言で宣言して、この型マップでマッピングを指定します。

  • public static finalフィールド_SQL_TYPECODEを使用して、マッピングを指定します。

シリアライズ可能なクラスに対する型マップの定義

SAddresspack.SPersonおよびpack.Manager.InnerSPM(InnerSPMManagerの内部クラスです)がシリアライズ可能な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クラスがRAWBLOBのどちらの列にマップされているかによって形式が異なります。

    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コードに直接生成されます。

SerializableDatum: ORADataの実装

oracle.sql.STRUCToracle.sql.REFまたはoracle.sql.ARRAY以外のoracle.sql.*型にマッピングするカスタムJavaクラスの定義例は、「ORAData実装のその他の使用例」を参照してください。

RAWフィールドに対してJavaオブジェクトのシリアライズおよびシリアライズ解除を行う場合の例では、カスタムJavaクラスをoracle.sql.RAW型にマッピングしています。これは、BLOBフィールドにも同様に当てはまり、カスタムJavaクラスをoracle.sql.BLOB型にマッピングします。

ここでは、このようなアプリケーションの例として、ORADataインタフェースを実装し、汎用形式のカスタムJavaクラスに準拠するSerializableDatumクラスの作成について説明します。この例では、SerializableDatumの開発手順を詳細に示してから、サンプル・コード全体を示します。


注意:

このアプリケーションでは、java.iojava.sqloracle.sqlおよびoracle.jdbcパッケージのクラスを使用しています。インポート文はここには含まれていません。

  1. まず、このクラスのスケルトンから始めます。

    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型間の互換性を検証します。

  2. 次の処理を行うクライアント・メソッドを定義します。

    • 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;
    }
    
  3. 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()); }
    
  4. 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()); }
    }
    
  5. 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);
       }
    };
    

SQLJアプリケーションのSerializableDatum

ここでは、前項で作成したSerializableDatumクラスのインスタンスをホスト変数およびイテレータ列として、SQLJアプリケーションで使用する方法を示します。

次の表定義を想定します。

CREATE TABLE PERSONDATA (NAME VARCHAR2(20) NOT NULL, INFO RAW(2000));

ホスト変数としてのSerializableDatum

次に、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

次に、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(クラス全体)

次に、前項で詳細手順を示した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.NUMBERoracle.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型

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開発者ガイド』