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

前
 
次
 

13 Oracleオブジェクト型の操作

この章では、Java Database Database Connectivity(JDBC)でのユーザー定義オブジェクト型のサポートについて説明します。一般的な、弱い型指定のoracle.sql.STRUCTクラスの機能や、JDBC標準のSQLDataインタフェースまたはOracleのORADataインタフェースを実装するカスタムJavaクラスをマップする方法を説明します。

内容は次のとおりです。


関連項目:

『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』

Oracleオブジェクトのマッピング

Oracleオブジェクト型ではデータベースの複合データ構造がサポートされます。たとえば、name(CHAR型)、phoneNumber(CHAR型)およびemployeeNumber(NUMBER型)の属性を持つPerson型を定義できます。

Oracleでは、Oracleオブジェクト機能とJDBC機能が密接に統合されています。標準の汎用JDBC型を使用してOracleオブジェクトにマップすることも、カスタムJava型定義クラスを作成してマッピングをカスタマイズすることもできます。


注意:

このマニュアルでは、Oracleオブジェクトにマップするために作成するJavaクラスを、カスタムJavaクラス、または、より具体的にカスタム・オブジェクト・クラスと呼びます。これは、オブジェクト参照にマップされるJavaクラスであるカスタム参照クラスや、OracleコレクションにマップされるJavaクラスであるカスタム・コレクション・クラスに対比されるものです。

カスタム・オブジェクト・クラスは、データの読取りおよび書込みを行う標準JDBCインタフェースまたはOracle拡張機能インタフェースを実装できます。JDBCでは、Oracleオブジェクトを特定のJavaクラスのインスタンスとしてインスタンス化します。JDBCを使用してOracleオブジェクトにアクセスするには、次の2つの処理を行います。

  1. Oracleオブジェクト用にJavaクラスを作成します。

  2. そのクラスにデータを移入します。次の方法からどちらかを選んで行います。

    • JDBCを使用して、オブジェクトをSTRUCTオブジェクトとしてインスタンス化します。

    • OracleオブジェクトとJavaクラス間のマッピングを明示的に指定します。

      これにはオブジェクト・データ用にJavaクラスをカスタマイズすることも含まれます。そうすることにより、ドライバが指定されたカスタム・オブジェクト・クラスのインスタンスにデータを移入できるようになります。この場合、Javaクラスにいくつかの制約が生じます。これらの制約を満たすには、JDBC標準java.sql.SQLDataインタフェースまたはOracle拡張機能oracle.sql.ORADataインタフェースを実装するようにクラスを定義します。

Oracle JPublisherユーティリティを使用して、カスタムJavaクラスを生成できます。


注意:

SQLDataインタフェースを使用する場合、弱い型指定のjava.sql.Structオブジェクトで十分な場合を除き、Java型マップを使用してSQLとJavaのマッピングを指定する必要があります。

Oracleオブジェクト用のデフォルトSTRUCTクラスの使用方法

OracleオブジェクトのSQLとJavaのマッピングを行うカスタムJavaクラスを提供しない場合、Oracle JDBCはオブジェクトをjava.sql.Structインタフェースを実装するオブジェクトとしてインスタンス化します。

実際のSQL型が不明な場合は、通常、カスタムJavaオブジェクトのかわりにSTRUCTオブジェクトを使用します。たとえば、Javaアプリケーションを、エンド・ユーザー・アプリケーションではなく、データベースの任意のオブジェクト・データを操作するツールとして使用する場合があります。データベースから選択したデータをSTRUCTオブジェクトに挿入したり、データベースにデータを挿入するためにSTRUCTオブジェクトを作成したりできます。STRUCTオブジェクトは、データをSQL形式で維持するため、データを完全に保存します。アプリケーション固有の形式による情報が不要な場合は、STRUCTオブジェクトを使用すると、データをより効率的に、より正確に保存できます。

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

STRUCTクラス機能

この項では、標準メソッドの機能とoracle.sql.STRUCTのOracle固有の機能を対比させて説明します。また、STRUCT記述子を説明し、STRUCTクラスのメソッド一覧を示すことにより、機能の概要を示します。

標準java.sql.Structメソッド

コードを標準JDBC 2.0に準拠させる必要がある場合、java.sql.Structインスタンスを使用し、次の標準メソッドを使用します。

  • getAttributes(map)

    このメソッドは、構造化オブジェクト型である属性のインスタンス化に使用するJavaクラスを決定するために、指定した型マップのエントリを使用して、属性の値を取得します。他の属性値のJava型は、基礎となるSQL型のデータに対するgetObjectコールの場合と同じです。

  • getAttributes

    このメソッドは、前述のgetAttributes(map)メソッドと同じですが、接続にはデフォルト型マップが使用されます。

  • getSQLTypeName

    このメソッドは、このStructが表すOracleオブジェクト型の完全修飾名を表すJava Stringを戻します。

STRUCTオブジェクトと属性の取出し

この項では、Oracle固有の機能またはJDBC 2.0標準機能を使用して、Oracleオブジェクトとその属性を取り出し、操作する方法を説明します。


注意:

JDBCドライバは、埋込みオブジェクト(つまり、STRUCTオブジェクトの属性であるSTRUCTオブジェクト)を、通常のオブジェクトの場合と同様に、シームレスに処理します。オブジェクトである属性を取り出すJDBCドライバは、同じ変換規則に従って、型マップ(使用可能な場合)またはデフォルトのマッピングを使用します。

java.sql.StructオブジェクトとしてのOracleオブジェクトの取出し

前述の例では、getObjectなどの標準JDBC機能を使用して、データベースからOracleオブジェクトをjava.sql.Structのインスタンスとして取り出すこともできます。getObjectによってjava.lang.Objectが戻されるため、メソッドの出力はStructにキャストする必要があります。たとえば、次のようになります。

ResultSet rs= stmt.executeQuery("SELECT * FROM struct_table");
java.sql.Struct jdbcStruct = (java.sql.Struct)rs.getObject(1);

oracle.sql型としての属性の取出し

STRUCTインスタンスまたはStructインスタンスからoracle.sql型としてOracleオブジェクト属性を取り出すには、次のように、oracle.sql.STRUCTクラスのgetOracleAttributesメソッドを使用します。

oracle.sql.Datum[] attrs = oracleSTRUCT.getOracleAttributes();

または

oracle.sql.Datum[] attrs = ((oracle.sql.STRUCT)jdbcStruct).getOracleAttributes();

標準Java型としての属性の取出し

STRUCTまたはStructインスタンスから標準Java型としてOracleオブジェクト属性を取り出すには、次のように標準getAttributesメソッドを使用します。

Object[] attrs = jdbcStruct.getAttributes();

注意:

Oracle JDBCドライバは、配列および構造の記述子をキャッシュします。そのためパフォーマンスは大きく向上します。ただし、データベース内で構造型の基礎となる型定義を変更すると、その構造型のキャッシュされた記述子は古くなり、アプリケーションでSQLException例外が通知されます。

STRUCTオブジェクトの作成

STRUCTオブジェクトの作成方法の詳細は、「クラスoracle.sql.STRUCTの概要」を参照してください。

STRUCTオブジェクトの文へのバインド

oracle.sql.STRUCTオブジェクトをプリコンパイルされたSQL文またはコール可能文にバインドするには、標準setObjectメソッド(型コードを指定)を使用するか、文オブジェクトをOracle文の型にキャストしてからOracle拡張機能のsetOracleObjectメソッドを使用します。たとえば、次のようになります。

PreparedStatement ps= conn.prepareStatement("text_of_prepared_statement");
STRUCT mySTRUCT = new STRUCT (...);
ps.setObject(1, mySTRUCT, Types.STRUCT);

または

PreparedStatement ps= conn.prepareStatement("text_of_prepared_statement");
STRUCT mySTRUCT = new STRUCT (...);
((OraclePreparedStatement)ps).setOracleObject(1, mySTRUCT);

STRUCT自動属性バッファリング

Oracle JDBCドライバには、STRUCT属性のバッファリングを有効または無効にするためのパブリック・メソッドが用意されています。

oracle.sql.STRUCTクラスには、次のメソッドが含まれます。

  • public void setAutoBuffering(boolean enable)

  • public boolean getAutoBuffering()

setAutoBuffering(boolean)メソッドは自動バッファリングを有効または無効にします。getAutoBufferingメソッドは、現行の自動バッファリング・モードを戻します。デフォルトでは、自動バッファリングは無効です。

Java仮想マシン(JVM)のメモリーに、ARRAYデータをオーバーフローなしに格納できると仮定し、STRUCT属性にgetAttributesメソッドとgetArrayメソッドで複数回アクセスする場合は、JDBCアプリケーションの自動バッファリングを有効にすることをお薦めします。


注意:

変換した属性をバッファリングすると、JDBCアプリケーションでは、大量のメモリーが消費されます。

自動バッファリングを有効にすると、oracle.sql.STRUCTオブジェクトでは、変換したすべての属性のローカル・コピーが保持されます。このデータは保持されるため、この情報に後でアクセスするときにはデータ・フォーマット変換処理を実行しなくて済みます。

Oracleオブジェクト用のカスタム・オブジェクト・クラスの作成と使用方法

Oracleオブジェクト用にカスタム・オブジェクト・クラスを作成する場合は、型マップのエントリを定義する必要があります。ドライバはこの型マップに従ってOracleオブジェクトに対応するカスタム・オブジェクト・クラスをインスタンス化します。

Oracleオブジェクトとその属性データからカスタム・オブジェクト・クラスのインスタンスを作成し、データを移入する方法も指定する必要があります。ドライバによって、カスタム・オブジェクト・クラスからのデータの読取り、およびカスタム・オブジェクト・クラスへのデータの移入が実行できる必要があります。また、カスタム・オブジェクト・クラスは、提供する必要があるかどうかにかかわらず、Oracleオブジェクトの属性に対応するgetXXXメソッドおよびsetXXXメソッドも提供できます。カスタム・クラスの作成とデータ移入、およびドライバの読取り/書込み機能の設定を行うには、次のインタフェースのいずれかを選択します。

作成するカスタム・オブジェクト・クラスでは、これらのインタフェースのどちらかを実装する必要があります。ORADataインタフェースは、カスタム・オブジェクト・クラスに対応するカスタム参照クラスを実装するときにも使用できます。ただし、SQLDataインタフェースを使用している場合、使用できるのは、java.sql.Reforacle.sql.REFなど、弱いJava参照型のみです。SQLDataインタフェースは、SQLオブジェクトのマッピング専用です。

たとえば、データベースにEMPLOYEEというOracleオブジェクト型があり、そのオブジェクト型にはName(CHAR型)およびEmpNum(NUMBER型)という2つの属性が設定されているとします。型マップを使用して、EMPLOYEEオブジェクトがJEmployeeというカスタム・オブジェクト・クラスにマップされるように指定します。JEmployeeクラスでは、SQLDataまたはORADataインタフェースのどちらかを実装できます。

カスタム・オブジェクト・クラスは独自に作成できますが、Oracle JPublisherユーティリティを使用して作成すると便利です。JPublisherは標準SQLDataインタフェースとOracle固有のORADataインタフェースの両方をサポートしており、どちらかを実装するクラスを生成できます。

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

ORADataとSQLDataの利点

2つのインタフェースのどちらを実装するかを決定する場合は、ORADataSQLDataの利点を考慮する必要があります。

SQLDataインタフェースは、SQLオブジェクトのマッピング専用です。ORADataインタフェースは、より柔軟性が高く、他のSQL型と同じようにSQLオブジェクトをマップし、処理をカスタマイズできます。ORADataオブジェクトは、Oracle Databaseの任意のデータ型から作成できます。これは、たとえば、JavaのRAWデータをシリアライズするときに役立ちます。

ORADataの利点

ORADataの利点は次のとおりです。

  • Oracleオブジェクトの型マップ・エントリが必要ありません。

  • Oracle拡張機能に対応しています。

  • oracle.sql.STRUCTからORADataを作成できます。この方法は、ネイティブなJava型への変換が最小限で済むため、より効率的です。

  • toDatumメソッドを使用して、ORADataオブジェクトから、対応するDatumオブジェクトを取得できます。

  • これによりパフォーマンスが改善されます。ORADataは、Oracleオブジェクトを保持するためにドライバが使用する内部書式であるDatum型を直接伴って機能します。

SQLDataの利点

SQLDataはJDBC標準であるため、コードの移植が可能です。

SQLDataを実装するための型マップ

カスタム・オブジェクト・クラスでSQLDataインタフェースを使用する場合、Oracleオブジェクト型をJavaにマップする際に使用するカスタム・オブジェクト・クラスを指定する型マップ・エントリを作成する必要があります。接続オブジェクトのデフォルトの型マップか、結果セットからデータを取得するときに指定する型マップを使用できます。ResultSetインタフェースのgetObjectメソッドには、型マップの指定に使用できるシグネチャがあります。次のいずれかを使用できます。

rs.getObject(int columnIndex);

rs.getObject(int columnIndex, Map map);

SQLData実装を使用する場合、型マップ・エントリを含めないと、オブジェクトはデフォルトでoracle.sql.STRUCTクラスにマップされます。これに対し、ORAData実装には独自のマッピング機能があるため、型マップ・エントリが必要ありません。ORAData実装を使用する場合は、標準getObjectメソッドではなく、Oracle getORADataメソッドを使用します。

型マップを使用して、JavaクラスをOracleオブジェクトのSQL型名に関連付けます。このマップは1対1のマッピングで、ハッシュ表にキーワードと値のペアとして格納されます。Oracleオブジェクトからデータを読み取ると、JDBCドライバでは、型マップが参照され、Oracleオブジェクト型のデータのインスタンス化に使用されるJavaクラスが決定されます。Oracleオブジェクトにデータを書き込むと、JDBCドライバによって、SQLDataインタフェースのgetSQLTypeNameメソッドがコールされ、JavaクラスのSQL型名が取り出されます。SQLとJava間の実際の変換は、ドライバにより実行されます。

Oracleオブジェクトに対応しているJavaクラスの属性では、ネイティブなJava型またはOracleネイティブ型を使用して属性を格納できます。

型マップの作成とSQLData実装のマッピング定義

SQLData実装を使用する場合、型マップを提供するのは、JDBCアプリケーション・プログラマの役割です。型マップは、標準のjava.util.Mapインタフェースを実装するクラスのインスタンスにする必要があります。

独自のクラスも作成できますが、標準のjava.util.Hashtableクラスが要件を満たしています。

型マップに使用するHashtableなどのクラスは、putメソッドを実装します。このメソッドは、キーワードと値のペアを入力として取ります。各キーは完全修飾SQL型名で、対応する値は指定されたJavaクラスのインスタンスです。

型マップは、接続インスタンスに関連付けられます。標準java.sql.ConnectionインタフェースとOracle固有oracle.jdbc.OracleConnectionクラスには、getTypeMapメソッドが含まれています。両方ともMapオブジェクトを戻します。

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

既存の型マップへのエントリの追加

最初に接続インスタンスが確立されたときには、デフォルト型マップは空です。デフォルト型マップにデータを移入する必要があります。

既存の型マップにエントリを追加するには、次の手順を実行してください。

  1. OracleConnectionオブジェクトのgetTypeMapメソッドを使用して、接続の型マップ・オブジェクトを戻します。getTypeMapメソッドはjava.util.Mapオブジェクトを戻します。たとえば、OracleConnectionインスタンスoraconnがあるとします。

    java.util.Map myMap = oraconn.getTypeMap();
    

    注意:

    OracleConnectionインスタンスの型マップが初期化されていないと、getTypeMapを最初にコールしたとき、空のマップが戻されます。

  2. 型マップのputメソッドを使用して、マップ・エントリを追加します。putメソッドでは2つの引数、つまり、SQL型名文字列およびそのSQL型をマップするJavaクラスのインスタンスを指定します。

    myMap.put(sqlTypeName, classObject);
    

    sqlTypeNameは、データベースのSQL型名の完全修飾名を表す文字列です。classObjectは、そのSQL型をマップするJavaクラス・オブジェクトです。次のようにClass.forNameメソッドを使用して、クラス・オブジェクトを取り出します。

    myMap.put(sqlTypeName, Class.forName(className));
    

    たとえば、CORPORATEデータベース・スキーマにPERSON SQLデータ型が定義されている場合は、そのSQLデータ型を、次の文でPersonとして定義されたPERSON Javaクラスにマップできます。

    myMap.put("CORPORATE.PERSON", Class.forName("Person"));
    oraconn.setTypeMap(newMap);
     
    

    マップには、CORPORATEデータベースのPERSON SQLデータ型がPerson Javaクラスにマップされているエントリがあります。


    注意:

    型マップのSQL型名は、Oracle Databaseに大文字で格納されているので、すべて大文字にする必要があります。

新しい型マップの作成

新しい型マップを作成するには、次の一般的な手順を実行します。この例ではjava.util.Hashtableのインスタンスを使用しています。このインスタンスはjava.util.Dictionaryを拡張したもので、java.util.Mapを実装しています。

  1. 新しい型マップ・オブジェクトを作成します。

    Hashtable newMap = new Hashtable();
    
  2. 型マップ・オブジェクトのputメソッドを使用して、マップにエントリを追加します。たとえば、CORPORATEデータベースにEMPLOYEEというSQL型が定義されている場合は、次のように、そのSQL型をEmployee.javaに定義されているEmployeeクラス・オブジェクトにマップできます。

    newMap.put("CORPORATE.EMPLOYEE", class.forName("Employee"));
    
  3. エントリをマップに追加した後に、OracleConnectionオブジェクトのsetTypeMapメソッドを使用して、接続の既存の型マップを上書きする必要があります。たとえば、次のようになります。

    oraconn.setTypeMap(newMap);
    

    この例では、setTypeMapメソッドによってoraconn接続オブジェクトの元のマップがnewMapに上書きされます。


    注意:

    接続インスタンスのデフォルト型マップは、マッピングが必要なときに、マップ名を指定しないと使用されます。たとえば、入力としてマップを指定せずに、結果セットのgetObjectをコールしたときに使用されます。

型マップで指定されていないオブジェクト型のインスタンス化

getObjectコールを使用するときに、適切なエントリを持つ型マップを指定しないと、JDBCドライバはoracle.sql.STRUCTクラスのインスタンスとしてOracleオブジェクトをインスタンス化します。Oracleオブジェクト型に埋込みオブジェクトが格納されていて、それらのオブジェクトが型マップに存在しない場合、ドライバは埋込みオブジェクトもoracle.sql.STRUCTのインスタンスとしてインスタンス化します。埋込みオブジェクトが型マップに存在する場合、getAttributesメソッドをコールすると、埋込みオブジェクトは型マップで指定されたJavaクラスのインスタンスとして戻されます。

SQLData実装によるデータの読取りおよび書込み

この項では、Oracleオブジェクトに対応するJavaクラスがSQLDataを実装している場合に、そのOracleオブジェクトに対してデータの読取りまたは書込みを行う方法について説明します。

結果セットからのSQLDataオブジェクトの読取り

ここでは、カスタム・オブジェクト・クラスに対してSQLData実装を選択したときに、OracleオブジェクトのデータをJavaアプリケーションに読み取る手順について説明します。

この手順では、すでにOracleオブジェクト型を定義し、対応するカスタム・オブジェクト・クラスを作成し、OracleオブジェクトとJavaクラス間のマッピングを定義する型マップを更新し、文オブジェクトstmtを定義しているものとします。

  1. データベースに問合せを行い、OracleオブジェクトをJDBC結果セットに読み取ります。

    ResultSet rs = stmt.executeQuery("SELECT emp_col FROM personnel");
    

    PERSONNELには、SQL型EMP_OBJECTEMP_COLという列が1つ含まれています。このSQL型は、JavaクラスEmployeeにマップされるように、型マップに定義されています。

  2. 結果セットのgetObjectメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスに結果セットの1行からデータを移入します。型マップにはEmployeeのエントリが含まれるため、getObject メソッドによりユーザー定義のSQLDataオブジェクトが戻されます。

    if (rs.next())
       Employee emp = (Employee)rs.getObject(1);
    

    型マップにオブジェクトのエントリが存在しない場合は、getObjectによりoracle.sql.STRUCTオブジェクトが戻されます。getObjectメソッド・シグネチャによって、汎用のjava.lang.Object型が戻されるため、出力をSTRUCT型にキャストする必要があります。

    if (rs.next())
       STRUCT empstruct = (STRUCT)rs.getObject(1);
    

    getObjectメソッドがreadSQLをコールすると、SQLDataインタフェースからreadXXXがコールされます。


    注意:

    定義済の型マップを使用しないようにするには、getSTRUCTメソッドを使用します。このメソッドでは、型マップにマッピング・エントリが定義されている場合でも、常にSTRUCTオブジェクトが戻されます。

  3. カスタム・オブジェクト・クラスにgetメソッドが定義されている場合、このメソッドを使用して、オブジェクト属性のデータを読み取ります。たとえば、EMPLOYEEに、CHAR型の属性EmpNameおよびNUMBER型の属性EmpNumがある場合は、Java Stringを戻すgetEmpNameメソッドおよびint値を戻すgetEmpNumメソッドを指定します。次に、Javaアプリケーションでこれらのメソッドを次の方法でコールします。

    String empname = emp.getEmpName();
    int empnumber = emp.getEmpNum();
     
    

コール可能文OUTパラメータからのSQLDataオブジェクトの取出し

PL/SQLファンクションGETEMPLOYEEをコールするCallableStatementインスタンスcsについて考えてみます。このファンクションには、プログラムによって従業員番号が渡されます。そのファンクションから対応するEmployeeオブジェクトが戻されます。このオブジェクトを取り出すには、次の手順を実行します。

  1. GETEMPLOYEEファンクションをコールするCallableStatementを次のように準備します。

    CallableStatement ocs = conn.prepareCall("{ ? = call GETEMPLOYEE(?) }");
    
  2. empnumberを、GETEMPLOYEEの入力パラメータとして宣言します。SQLDataオブジェクトを、型コードOracleTypes.STRUCTを指定してOUTパラメータとして登録します。次に、文を実行します。次のように実行します。

    cs.setInt(2, empnumber); 
    cs.registerOutParameter(1, OracleTypes.STRUCT, "EMP_OBJECT"); 
    cs.execute(); 
    
  3. getObjectメソッドを使用して、employeeオブジェクトを取り出します。

    Employee emp = (Employee)cs.getObject(1);
    

    型マップ・エントリが存在しない場合、getObjectjava.sql.Structオブジェクトを戻します。

    Struct emp = cs.getObject(1);
    

SQLDataオブジェクトのINパラメータとしてのコール可能文への引渡し

EmployeeオブジェクトをINパラメータとして取り、そのオブジェクトをPERSONNEL表に追加するPL/SQLファンクションaddEmployee(?)があると仮定します。この例のempは、有効なEmployeeオブジェクトです。

  1. addEmployee(?)ファンクションをコールするためにCallableStatementを準備します。

    CallableStatement cs = 
      conn.prepareCall("{ call addEmployee(?) }");
    
  2. setObjectを使用して、empオブジェクトをINパラメータとしてコール可能文に渡します。次に、文をコールします。

    cs.setObject(1, emp); 
    cs.execute();
    

SQLData実装を使用したデータのOracleオブジェクトへの書込み

ここでは、カスタム・オブジェクト・クラスに対してSQLData実装を選択したときに、JavaアプリケーションのデータをOracleオブジェクトに書き込む手順について説明します。

ここでは、あらかじめOracleオブジェクト型を定義し、対応するJavaクラスを作成し、OracleオブジェクトとJavaクラス間のマップを定義する型マップを更新していることを前提としています。

  1. カスタム・オブジェクト・クラスにsetメソッドが定義されている場合、このメソッドを使用して、アプリケーションのJava変数のデータをJavaデータ型オブジェクトの属性に書き込みます。

    emp.setEmpName(empname);
    emp.setEmpNum(empnumber);
    
  2. Javaデータ型オブジェクトのデータを使用して、データベース表の行に格納されているOracleオブジェクトを更新する文を、適切に準備します。

    PreparedStatement pstmt = conn.prepareStatement
                              ("INSERT INTO PERSONNEL VALUES (?)");
    
  3. プリコンパイルされたSQL文のsetObjectメソッドを使用して、Javaデータ型オブジェクトをプリコンパイルされたSQL文にバインドします。

    pstmt.setObject(1, emp);
    
  4. 文を実行すると、データベースが更新されます。

    pstmt.executeUpdate();
    

ORADataインタフェース

Javaアプリケーションで使用できるOracleオブジェクトとその属性データを作成する方法の1つに、oracle.sql.ORADataインタフェースとoracle.sql.ORADataFactoryインタフェースを実装するカスタム・オブジェクト・クラスを作成する方法があります。ORADataおよびORADataFactoryインタフェースはOracleが提供するもので、JDBC標準の一部ではありません。


注意:

JPublisherユーティリティは、ORADataおよびORADataFactoryインタフェースを実装するクラスの生成をサポートしています。

ORADataの機能

ORADataインタフェースには、次の利点があります。

  • 標準JDBC型に対するOracle拡張機能をサポートします。

  • 作成中のJavaカスタム・クラスの名前を指定するときに、型マップは必要ありません。

  • これによりパフォーマンスが改善されます。ORADataは、Oracleオブジェクトを保持するためにドライバが使用する内部書式であるDatum型を直接伴って機能します。

ORADataおよびORADataFactoryインタフェースでは、次の処理が実行されます。

  • ORADataクラスのtoDatumメソッドでは、データをoracle.sql.*表現に変換します。

  • ORADataFactoryでは、カスタム・オブジェクト・クラスのコンストラクタと等価のcreateメソッドが指定されます。このメソッドでは、ORADataインスタンスを作成して戻します。JDBCドライバは、createメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスをJavaアプリケーションまたはアプレットに戻します。このメソッドは、入力として、oracle.sql.Datumオブジェクトと、OracleTypesクラスで指定された対応するSQL型コードを示す整数を取ります。

ORADataおよびORADataFactoryは、次のように定義されます。

public interface ORAData 
{ 
    Datum toDatum (OracleConnection conn) throws SQLException;
} 
 
public interface ORADataFactory 
{ 
    ORAData create (Datum d, int sql_Type_Code) throws SQLException; 
} 

connは接続オブジェクトを、doracle.sql.Datum型のオブジェクトを、sql_Type_CodeDatumオブジェクトのSQL型コードをそれぞれ表します。

オブジェクト・データの取出しと挿入

オブジェクト・データをORADataのインスタンスとして取得および挿入する場合、JDBCドライバでは次のメソッドを使用します。

オブジェクト・データは、次のいずれかの方法で取り出すことができます。

  • Oracle固有OracleResultSetクラスの次のgetORADataメソッドを使用します。

    ors.getORAData (int col_index, ORADataFactory factory);
    

    このメソッドでは、結果セットのデータの列索引およびORADataFactoryインスタンスを入力として取ります。たとえば、カスタム・オブジェクト・クラスにgetORAFactoryメソッドを実装して、getORADataに入力するORADataFactoryインスタンスを作成できます。ORADataを実装するJavaクラスを使用するときは、型マップは必要ありません。

  • ResultSetインタフェースで指定される標準getObject(index, map)メソッドを使用して、ORADataのインスタンスとしてデータを取り出します。この場合、指定されたオブジェクト型で使用されるファクトリ・クラスおよび対応するSQL型名を識別するには、型マップにエントリを定義する必要があります。

オブジェクト・データは、次のいずれかの方法で挿入できます。

  • Oracle固有OraclePreparedStatementクラスの次のsetORADataメソッドを使用します。

    ops.setORAData (int bind_index, ORAData custom_obj);
    

    このメソッドでは、バインド変数のパラメータ索引、および変数を含むオブジェクト名を入力として取ります。

  • PreparedStatementインタフェースで指定される標準setObjectメソッドを使用します。このメソッドを別のフォームで使用して、型マップなしでORADataインスタンスを挿入することもできます。

この後の項では、getORADataメソッドとsetORADataメソッドについて説明します。

OracleオブジェクトEMPLOYEEの例を引き続き使用するには、Javaアプリケーションに次のコードを記述する必要があります。

ORAData datum = ors.getORAData(1, Employee.getORAFactory());

この例では、orsはOracle結果セット、getORADataORADataオブジェクトの取出しに使用されるOracleResultSetクラス、およびEMPLOYEEは結果セットの列1です。static Employee.getORAFactoryメソッドによって、ORADataFactoryがJDBCドライバに戻されます。JDBCドライバでは、このオブジェクトからcreate()をコールし、結果セットのデータが移入されたEmployeeクラスのインスタンスをJavaアプリケーションに戻します。


注意:

  • ORADataおよびORADataFactoryは、独立したインタフェースとして定義されるため、必要に応じて異なるJavaクラスから実装できます。

  • ORADataインタフェースを使用するには、カスタム・オブジェクト・クラスでoracle.sql.*をインポートする必要があります。


ORAData実装によるデータの読取りおよび書込み

この項では、対応するJavaクラスがORADataを実装する場合に、Oracleオブジェクトに対してデータの読取りまたは書込みを行う方法について説明します。

ORAData実装を使用したOracleオブジェクトからのデータの読取り

ここでは、データをOracleオブジェクトからJavaアプリケーションに読み取る手順について説明します。この手順は、ORADataを手動で実装する場合と、JPublisherを使用してカスタム・オブジェクト・クラスを作成する場合の両方に適用されます。

この手順では、すでにOracleオブジェクト型を定義し、対応するカスタム・オブジェクト・クラスを手動、またはJPublisherを使用して作成し、文オブジェクトstmtを定義しているものとします。

  1. データベースに問合せを行ってOracleオブジェクトを結果セットに読み取り、Oracle結果セットにキャストします。

    OracleResultSet ors = (OracleResultSet)stmt.executeQuery
                          ("SELECT Emp_col FROM PERSONNEL");
    

    PERSONNELは、1列の表です。列名はEmp_col、型はEmployee_objectです。

  2. 結果セットのgetORADataメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスに結果セットの1つの行からデータを移入します。getORADataメソッドはoracle.sql.ORADataオブジェクトを戻します。これを固有のカスタム・オブジェクト・クラスにキャストすることができます。

    if (ors.next())
       Employee emp = (Employee)ors.getORAData(1, Employee.getORAFactory());
    

    または

    if (ors.next())
       ORAData datum = ors.getORAData(1, Employee.getORAFactory());
    

    この例では、Employeeはカスタム・オブジェクト・クラス名、orsOracleResultSetオブジェクト名です。

    getORADataを使用しない場合、JDBCドライバでは、標準JDBC ResultSetgetObjectメソッドを使用して、ORADataデータを取得することができます。ただし、指定されたオブジェクト型で使用されるファクトリ・クラスおよび対応するSQL型名を識別するには、型マップにエントリを定義する必要があります。

    たとえば、オブジェクトのSQL型名がEMPLOYEEの場合は、対応するJavaクラスはEmployeeで、ORADataが実装されます。対応するファクトリ・クラスはEmployeeFactoryで、ORADataFactoryが実装されます。

    次の文を使用して、型マップにEmployeeFactoryエントリを宣言します。

    map.put ("EMPLOYEE", Class.forName ("EmployeeFactory")); 
    

    次に、getObjectの形式を使用し、マップ・オブジェクトを指定します。

    Employee emp = (Employee) rs.getObject (1, map);
    

    接続のデフォルトの型マップに、指定されたオブジェクト型と対応するSQL型名に対して使用されるファクトリ・クラスを識別するエントリがすでにある場合は、次の形式のgetObjectを使用できます。

    Employee emp = (Employee) rs.getObject (1); 
    
  3. カスタム・オブジェクト・クラスにgetメソッドがある場合は、そのメソッドを使用して、オブジェクト属性のデータをアプリケーションのJava変数に読み取ります。たとえば、EMPLOYEEに、CHAR型のEmpNameおよびNUMBER型のEmpNumがある場合は、Java Stringを戻すgetEmpNameメソッドおよび整数値を戻すgetEmpNumメソッドを指定します。次に、Javaアプリケーションでこれらのメソッドを次の方法でコールします。

    String empname = emp.getEmpName();
    int empnumber = emp.getEmpNum();
    

    注意:

    または、コール可能文オブジェクトを使用してデータをフェッチできます。OracleCallableStatementクラスには、getORADataメソッドも定義されています。

ORAData実装を使用したデータのOracleオブジェクトへの書込み

ここでは、データをJavaアプリケーションからOracleオブジェクトに書き込む手順について説明します。この手順は、ORADataを手動で実装する場合と、JPublisherを使用してカスタム・オブジェクト・クラスを作成する場合の両方に適用されます。

この手順では、すでにOracleオブジェクト型を定義し、対応するカスタム・オブジェクト・クラスを作成しているものとします。


注意:

データベースのINSERTおよびUPDATE操作の実行時には、型マップは使用されません。

  1. カスタム・オブジェクト・クラスにsetメソッドが定義されている場合、このメソッドを使用して、アプリケーションのJava変数のデータをJavaデータ型オブジェクトの属性に書き込みます。

    emp.setEmpName(empname);
    emp.setEmpNum(empnumber);
    
  2. Javaデータ型オブジェクトのデータを使用して、Oracleオブジェクトを更新するプリコンパイルされたSQL文を、適切にデータベース表の行に書き込みます。

    OraclePreparedStatement opstmt = conn.prepareStatement
       ("UPDATE PERSONNEL SET Employee = ? WHERE Employee.EmpNum = 28959);
    

    この例では、connConnectionオブジェクトです。

  3. プリコンパイルされたSQL文のsetORADataメソッドを使用して、Javaデータ型オブジェクトをプリコンパイルされたSQL文にバインドします。

    opstmt.setORAData(1, emp);
    

    setORADataメソッドでは、カスタム・オブジェクト・クラス・インスタンスのtoDatumメソッドをコールすることにより、データベースに書き込むことができるoracle.sql.STRUCTオブジェクトが取り出されます。

    この手順では、setObjectメソッドを使用してJavaデータ型をバインドすることもできます。たとえば、次のようになります。

    opstmt.setObject(1,emp);
    

    注意:

    Javaデータ型オブジェクトを、INまたはOUTバインド変数として使用できます。

その他のORADataの使用方法

ORADataインタフェースには、SQLDataインタフェースより優れた柔軟性があります。SQLDataインタフェースは、Oracleオブジェクト型から目的のJava型へのマッピングをカスタマイズする目的で設計されています。SQLDataインタフェースを実装すると、JavaとSQL型間の変換が正常に終了してから、元のSQLオブジェクト・データからカスタムJavaクラス・インスタンスのフィールドへのデータ移入、およびその逆が、JDBCドライバによって実行されます。

ORADataインタフェースの場合は、Oracleオブジェクト型からJava型へのカスタマイズがサポートされるのみでなく、その場合、Javaオブジェクト型と、oracle.sqlパッケージでサポートされる任意のSQL型の間のマッピングを用意します。

このインタフェースは、oracle.sql.*型をラップするためのカスタムJavaクラスを提供したり、カスタマイズされた変換または機能を実装したりするときに役立ちます。次の使用例が考えられます。

  • データの暗号化および復号化、または妥当性チェックを実行します。

  • 読取りまたは書込みを行った値のロギングを実行します。

  • URL情報を含む文字フィールドなどのキャラクタ列を小さなコンポーネントに解析します。

  • 文字列を数値定数にマップします。

  • データをより適したJava形式にマップします。たとえば、DATEフィールドをjava.util.Date形式にマップします。

  • データ表現をカスタマイズします。たとえば、表の列のデータがフィート単位のとき、選択後にメートルで表現します。

  • Javaオブジェクトをシリアライズおよびデシリアライズします。

たとえば、ORADataを使用すると、データベースの特定のSQLオブジェクト型に対応していないJavaオブジェクトのインスタンスを、SQL型RAWの列に格納できます。ORADataFactorycreateメソッドでは、oracle.sql.RAW型のオブジェクトから目的のJavaオブジェクトへの変換を実装する必要があります。ORADatatoDatumメソッドでは、Javaオブジェクトからoracle.sql.RAW型のオブジェクトへの変換を実装する必要があります。これには、Javaのシリアライズなどを使用します。

JDBCドライバでは、データのRAWバイトをoracle.sql.RAW形式で透過的に取り出します。次に、ORADataFactorycreateメソッドをコールし、oracle.sql.RAWオブジェクトを目的のJavaクラスに変換します。

データベースにJavaオブジェクトを挿入するときは、そのオブジェクトをRAW型の列にバインドするのみでデータベースに格納できます。ドライバは、ORADatatoDatumメソッドを透過的にコールして、Javaオブジェクトをoracle.sql.RAW型のオブジェクトに変換します。このオブジェクトは、データベースにRAW型の1列として格納されます。

JDBCドライバによって使用される内部書式であるoracle.sql.*書式を使用して変換が機能するように設計されているため、ORADataインタフェースのサポートは非常に効率的でもあります。さらに、型マップはSQLDataインタフェースのために必要なものであり、ORADataを実装するJavaクラスを使用する場合は不要です。

オブジェクト型の継承

オブジェクト型の継承により、別のオブジェクト型を拡張して新しいオブジェクト型を作成することができます。新しいオブジェクト型は、拡張元のオブジェクト型のサブタイプになります。サブタイプは、そのスーパータイプに定義されたすべての属性およびメソッドを自動的に継承します。サブタイプは、属性およびメソッドを追加し、スーパータイプから継承されたメソッドをオーバーロードまたはオーバーライドできます。

オブジェクト型の継承によって、代入可能性が導入されます。代入可能性とは、T型の任意のサブタイプに加えて、T型の値を保持するように宣言したスロットの機能です。Oracle JDBCドライバは、透過的に代入可能性を処理します。

データベース・オブジェクトは、情報が失われることなく、最も固有の型で戻されます。たとえば、STUDENT_TオブジェクトがPERSON_Tスロットに格納されている場合、Oracle JDBCドライバはSTUDENT_Tオブジェクトを表すJavaオブジェクトを戻します。

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

サブタイプの作成

明示的にOracleオブジェクト型に対応するJavaクラスを作成するには、カスタム・オブジェクト・クラスを作成します。オブジェクト型の階層がある場合には、Javaクラスの対応する階層を作成できます。

JDBCでデータベース・サブタイプを作成する最も一般的な方法は、java.sql.Statementインタフェースのexecuteメソッドを使用して、SQLのCREATE TYPEコマンドを実行することです。たとえば、次のダイアグラムで示すように型継承階層を作成する場合があります。

型継承階層ダイアグラム
図hierarchy.gifの説明

この場合、JDBCコードは次のようになります。

Statement s = conn.createStatement();
s.execute ("CREATE TYPE Person_T (SSN NUMBER, name VARCHAR2(30),
  address VARCHAR2(255))");
s.execute ("CREATE TYPE Student_T UNDER Person_t (deptid NUMBER,
  major VARCHAR2(100))");
s.execute ("CREATE TYPE PartTimeStudent_t UNDER Student_t (numHours NUMBER)");

次のコードでは、ST型のfooメンバー・プロシージャがオーバーロードされ、printメンバー・プロシージャによってT型から継承されたコピーが上書きされます。

CREATE TYPE T AS OBJECT (..., 
  MEMBER PROCEDURE foo(x NUMBER), 
  MEMBER PROCEDURE Print(), 
  ... 
  NOT FINAL; 

CREATE TYPE ST UNDER T (..., 
  MEMBER PROCEDURE foo(x DATE),         <-- overload "foo" 
  OVERRIDING MEMBER PROCEDURE Print(),   <-- override "print" 
  STATIC FUNCTION bar(...) ... 
  ... 
  );

サブタイプを作成すると、実表の列またはオブジェクト型の属性として使用できます。


関連項目:

『Oracle Databaseオブジェクト・リレーショナル開発者ガイド』

サブタイプに対してカスタマイズされたクラスの実装

一般に、カスタマイズされたJavaクラスはデータベース・オブジェクト・タイプを表します。サブタイプに対してカスタマイズされたJavaクラスを作成するときには、Javaクラスでデータベース・オブジェクト・タイプの階層をミラー化するかどうかを選択できます。

クラスの作成には、ORADataまたはSQLDataソリューションのいずれかを使用して、オブジェクト型の階層をマップできます。

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

ORADataを使用した型継承階層の作成

oracle.sql.ORADataインタフェースを実装するJavaクラスを使用した、カスタマイズされたマッピングを使用することをお薦めします。ORADataのマッピングでは、JDBCアプリケーションがORADataおよびORADataFactoryインタフェースを実装する必要があります。ORADataFactoryインタフェースを実装するクラスは、オブジェクトを作成するファクトリを格納します。各オブジェクトはそれぞれ1つのデータベース・オブジェクトを表します。

ORADataインタフェースを実装するクラスの階層は、データベース・オブジェクト・タイプ階層をミラー化できます。たとえば、PERSON_TおよびSTUDENT_Tに対するJavaクラスのマッピングは、次のようになります。

ORADataを使用したPerson_java

次のコードは、ORADataおよびORADataFactoryインタフェースを実装するPerson_javaクラスを示します。

class Person implements ORAData, ORADataFactory 
{ 
  static final Person _personFactory = new Person(); 

  public NUMBER ssn; 
  public CHAR name; 
  public CHAR address; 

  public static ORADataFactory getORADataFactory() 
  { 
    return _personFactory; 
  } 

  public Person () {} 

  public Person(NUMBER ssn, CHAR name, CHAR address) 
  { 
    this.ssn = ssn; 
    this.name = name; 
    this.address = address; 
  } 

  public Datum toDatum(OracleConnection c) throws SQLException 
  { 
    StructDescriptor sd =
      StructDescriptor.createDescriptor("SCOTT.PERSON_T", c); 
    Object [] attributes = { ssn, name, address }; 
    return new STRUCT(sd, c, attributes); 
  } 

  public ORAData create(Datum d, int sqlType) throws SQLException 
  { 
    if (d == null) return null; 
    Object [] attributes = ((STRUCT) d).getOracleAttributes(); 
    return new Person((NUMBER) attributes[0], 
                      (CHAR) attributes[1], 
                      (CHAR) attributes[2]); 
  } 
}

Person.javaを拡張するStudent.java

次のコードは、Person.javaクラスを拡張するStudent.javaクラスを示します。

class Student extends Person 
{ 
  static final Student _studentFactory = new Student (); 

  public NUMBER deptid; 
  public CHAR major; 

  public static ORADataFactory getORADataFactory() 
  { 
    return _studentFactory; 
  } 

  public Student () {} 

  public Student (NUMBER ssn, CHAR name, CHAR address, 
                  NUMBER deptid, CHAR major) 
  { 
    super (ssn, name, address); 
    this.deptid = deptid; 
    this.major = major; 
  } 

  public Datum toDatum(OracleConnection c) throws SQLException 
  { 
    StructDescriptor sd = 
      StructDescriptor.createDescriptor("SCOTT.STUDENT_T", c); 
    Object [] attributes = { ssn, name, address, deptid, major }; 
    return new STRUCT(sd, c, attributes); 
  } 

  public CustomDatum create(Datum d, int sqlType) throws SQLException 
  { 
    if (d == null) return null; 
    Object [] attributes = ((STRUCT) d).getOracleAttributes(); 
    return new Student((NUMBER) attributes[0], 
                       (CHAR) attributes[1], 
                       (CHAR) attributes[2], 
                       (NUMBER) attributes[3], 
                       (CHAR) attributes[4]); 
  } 
}

ORADataインタフェースを実装するカスタマイズされたクラスでは、必ずしもオブジェクト型階層をミラー化する必要はありません。たとえば、スーパークラスを指定せずにStudentクラスを宣言できます。この場合、Studentには、PERSON_Tから継承された属性およびSTUDENT_Tによって宣言された属性を保持するフィールドが含まれます。

ORADataFactoryの実装

次の例に示すように、JDBCアプリケーションでは、データベースの問合せにファクトリ・クラスを使用して、Personまたはそのサブクラスのインスタンスを戻します。

ResultSet rset = stmt.executeQuery ("select person from tab1"); 
while (rset.next()) 
{ 
  Object s = rset.getORAData (1, PersonFactory.getORADataFactory()); 
  ... 
} 

ORADataFactoryインタフェースを実装したクラスでは、関連付けられたカスタム・オブジェクト型のインスタンスおよび任意のサブタイプのインスタンス、または少なくともサポートされるすべての型のインスタンスの作成が可能になります。

次の例で、PersonFactory.getORADataFactoryメソッドは、Javaインスタンスpersonstudentまたはparttimestudentを戻すことにより、PERSON_Tオブジェクト、STUDENT_TオブジェクトおよびPARTTIMESTUDENT_Tオブジェクトを処理できるファクトリを戻します。

class PersonFactory implements ORADataFactory 
{ 
  static final PersonFactory _factory = new PersonFactory (); 

  public static ORADataFactory getORADataFactory() 
  { 
    return _factory; 
  } 

  public ORAData create(Datum d, int sqlType) throws SQLException 
  { 
    STRUCT s = (STRUCT) d; 
    if (s.getSQLTypeName ().equals ("SCOTT.PERSON_T")) 
      return Person.getORADataFactory ().create (d, sqlType); 
    else if (s.getSQLTypeName ().equals ("SCOTT.STUDENT_T")) 
      return Student.getORADataFactory ().create(d, sqlType); 
    else if (s.getSQLTypeName ().equals ("SCOTT.PARTTIMESTUDENT_T")) 
      return ParttimeStudent.getORADataFactory ().create(d, sqlType); 
    else 
      return null; 
  } 
}

次の例では、次のような表tabl1があることを想定しています。

CREATE TABLE tabl1 (idx NUMBER, person PERSON_T); 
INSERT INTO tabl1 VALUES (1, PERSON_T (1000, 'Scott', '100 Oracle Parkway')); 
INSERT INTO tabl1 VALUES (2, STUDENT_T (1001, 'Peter', '200 Oracle Parkway', 101, 'CS')); 
INSERT INTO tabl1 VALUES (3, PARTTIMESTUDENT_T (1002, 'David', '300 Oracle Parkway', 102, 'EE')); 

SQLDataを使用した型継承階層の作成

java.sql.SQLDataインタフェースを実装するカスタマイズされたクラスを使用すると、データベース・オブジェクト・タイプ階層をミラー化できます。サブクラスのreadSQLおよびwriteSQLメソッドは、通常、対応するスーパークラスのメソッドをコールして、そのスーパークラスの属性の読取りまたは書込みを行ってから、サブクラス属性の読取りまたは書込みを行います。たとえば、PERSON_TおよびSTUDENT_Tに対するJavaクラスのマッピングは、次のようになります。

SQLDataを使用したPerson.java

次のコードは、SQLDataインタフェースを実装するPerson.javaクラスを示します。

import java.sql.*; 

public class Person implements SQLData 
{ 
  private String sql_type; 
  public int ssn; 
  public String name; 
  public String address; 

  public Person () {} 

  public String getSQLTypeName() throws SQLException { return sql_type; } 

  public void readSQL(SQLInput stream, String typeName) throws SQLException 
  { 
    sql_type = typeName; 
    ssn = stream.readInt(); 
    name = stream.readString(); 
    address = stream.readString(); 
  } 

  public void writeSQL(SQLOutput stream) throws SQLException 
  { 
    stream.writeInt (ssn); 
    stream.writeString (name); 
    stream.writeString (address); 
  } 
}

Student.javaを拡張するStudent.java

次のコードは、Person.javaクラスを拡張するStudent.javaクラスを示します。

import java.sql.*; 

public class Student extends Person 
{ 
  private String sql_type; 
  public int deptid; 
  public String major; 

  public Student () { super(); } 

  public String getSQLTypeName() throws SQLException { return sql_type; } 

  public void readSQL(SQLInput stream, String typeName) throws SQLException 
  { 
    super.readSQL (stream, typeName);    // read supertype attributes 
    sql_type = typeName;
    deptid = stream.readInt(); 
    major = stream.readString(); 
  } 

  public void writeSQL(SQLOutput stream) throws SQLException 
  { 
    super.writeSQL (stream);        // write supertype
                                         // attributes 
    stream.writeInt (deptid); 
    stream.writeString (major); 
  } 
}

必須ではありませんが、SQLDataインタフェースを実装するカスタマイズされたクラスでは、データベース・オブジェクト型階層のミラー化をお薦めします。たとえば、スーパークラスを指定せずにStudentクラスを宣言できます。この場合、Studentには、PERSON_Tから継承された属性およびSTUDENT_Tによって宣言された属性を保持するフィールドが含まれます。

SQLDataを使用したStudent.java

次のコードは、Person.javaクラスを拡張せずに、SQLDataインタフェースを直接実装するStudent.javaクラスを示します。

import java.sql.*; 

public class Student implements SQLData 
{ 
  private String sql_type; 

  public int ssn; 
  public String name; 
  public String address; 
  public int deptid; 
  public String major; 

  public Student () {} 

  public String getSQLTypeName() throws SQLException { return sql_type; } 

  public void readSQL(SQLInput stream, String typeName) throws SQLException 
  { 
    sql_type = typeName; 
    ssn = stream.readInt(); 
    name = stream.readString(); 
    address = stream.readString(); 
    deptid = stream.readInt(); 
    major = stream.readString(); 
  } 

  public void writeSQL(SQLOutput stream) throws SQLException 
  { 
    stream.writeInt (ssn); 
    stream.writeString (name); 
    stream.writeString (address); 
    stream.writeInt (deptid); 
    stream.writeString (major); 
  } 
}

JPublisherユーティリティ

SQLDataORADataおよびORADataFactoryインタフェースを実装するカスタマイズされたクラスは手動で作成することも可能ですが、Oracle JPublisherを使用してクラスを自動作成することをお薦めします。Oracle JPublisherによって作成され、SQLDataORADataおよびORADataFactoryインタフェースを実装するカスタマイズされたクラスでは、継承階層をミラー化できます。


関連項目:


サブタイプ・オブジェクトの取得

一般的なJDBCアプリケーションでは、サブタイプ・オブジェクトは次のいずれかとして戻されます。

  • 問合せ結果

  • PL/SQL OUTパラメータ

  • 型属性

サブタイプの取得には、デフォルト・マッピング、SQLDataマッピングまたはORADataマッピングのいずれかを使用できます。

デフォルト・マッピングの使用

デフォルトで、データベース・オブジェクトは、oracle.sql.STRUCTクラスのインスタンスとして戻されます。このインスタンスは、宣言された型または宣言された型のサブタイプのオブジェクトを表します。STRUCTクラスがデータベースのサブタイプ・オブジェクトを表す場合、このクラスにはそのスーパータイプの属性と、サブタイプに定義された属性が含まれます。

Oracle JDBCドライバは、最も固有の型でデータベース・オブジェクトを戻します。JDBCアプリケーションは、STRUCTクラスのgetSQLTypeNameメソッドを使用して、STRUCTオブジェクトのSQL型を判断します。次のコードを参照してください。

// tab1.person column can store PERSON_T, STUDENT_T and PARTIMESTUDENT_T objects 
ResultSet rset = stmt.executeQuery ("select person from tab1"); 
while (rset.next()) 
{ 
  oracle.sql.STRUCT s = (oracle.sql.STRUCT) rset.getObject(1); 
  if (s != null) 
    System.out.println (s.getSQLTypeName());    // print out the type name which 
    // may be SCOTT.PERSON_T, SCOTT.STUDENT_T or SCOTT.PARTTIMESTUDENT_T
}

SQLDataマッピングの使用

SQLDataマッピングの場合、JDBCドライバはSQLDataインタフェースを実装するクラスのインスタンスとして、データベース・オブジェクトを戻します。

データベース・オブジェクトの取得にSQLDataマッピングを使用するには、次の操作を実行します。

  1. 目的のオブジェクト型のSQLDataインタフェースを実装するコンテナ・クラスを実装します。

  2. 接続型マップに各Oracleオブジェクト型に対応するカスタムJava型を指定するエントリが移入されます。

  3. SQLオブジェクト値へのアクセスには、getObjectメソッドを使用します。

    JDBCドライバによって、型マップと一致するエントリがチェックされます。一致するエントリが見つかると、ドライバはSQLDataインタフェースを実装するクラスのインスタンスとしてデータベース・オブジェクトを戻します。

次のコードは、カスタマイズされたSQLDataマッピングの全プロセスを示します。

// The JDBC application developer implements Person.java for PERSON_T, 
// Student.java for STUDENT_T 
// and ParttimeStudent.java for PARTTIMESTUDEN_T. 

Connection conn = ...;  // make a JDBC connection 

// obtains the connection typemap 
java.util.Map map = conn.getTypeMap (); 

// populate the type map 
map.put ("SCOTT.PERSON_T", Class.forName ("Person")); 
map.put ("SCOTT.STUDENT_T", Class.forName ("Student")); 
map.put ("SCOTT.PARTTIMESTUDENT_T", Class.forName ("ParttimeStudent")); 

// tab1.person column can store PERSON_T, STUDENT_T and PARTTIMESTUDENT_T objects 
ResultSet rset = stmt.executeQuery ("select person from tab1"); 
while (rset.next()) 
{ 
  // "s" is instance of Person, Student or ParttimeStudent 
  Object s = rset.getObject(1); 

  if (s != null) 
  { 
    if (s instanceof Person) 
      System.out.println ("This is a Person"); 
    else if (s instanceof Student) 
      System.out.println ("This is a Student"); 
    else if (s instanceof ParttimeStudent) 
      System.out.pritnln ("This is a PartimeStudent"); 
    else 
      System.out.println ("Unknown type"); 
  } 
}

JDBCドライバにより、各コールの接続型マップと次のものが照合されます。

  • java.sql.ResultSetおよびjava.sql.CallableStatementインタフェースのgetObjectメソッド

  • java.sql.StructインタフェースのgetAttributeメソッド

  • java.sql.ArrayインタフェースのgetArrayメソッド

  • oracle.sql.REFインタフェースのgetValueメソッド

ORADataマッピングの使用

ORADataマッピングの場合、JDBCドライバはORADataインタフェースを実装するクラスのインスタンスとして、データベース・オブジェクトを戻します。

Oracle JDBCドライバは、Oracleオブジェクト型に対するJavaクラスのマッピングを認識する必要があります。Oracle JDBCドライバにこの情報を通知するには、次の2つの方法があります。

  • JDBCアプリケーションは、getORAData(int idx, ORADataFactory f)メソッドを使用して、データベース・オブジェクトにアクセスします。getORADataメソッドの2番目のパラメータは、カスタマイズされたクラスを作成するファクトリ・クラスのインスタンスを指定します。getORADataメソッドは、OracleResultSetおよびOracleCallableStatementクラスで使用可能です。

  • JDBCアプリケーションは、接続型マップに各Oracleオブジェクト型に対応するカスタムJava型を指定するエントリを移入します。Oracleオブジェクト値へのアクセスには、getObjectメソッドが使用されます。

2番目の方法では、標準getObjectメソッドが使用されます。次のサンプル・コードは、最初の方法を示します。

// tab1.person column can store both PERSON_T and STUDENT_T objects 
ResultSet rset = stmt.executeQuery ("select person from tab1"); 
while (rset.next()) 
{ 
  Object s = rset.getORAData (1, PersonFactory.getORADataFactory()); 
  if (s != null) 
  { 
    if (s instanceof Person) 
      System.out.println ("This is a Person"); 
    else if (s instanceof Student) 
      System.out.println ("This is a Student"); 
    else if (s instanceof ParttimeStudent) 
      System.out.pritnln ("This is a PartimeStudent"); 
    else 
      System.out.println ("Unknown type"); 
  } 
}

サブタイプ・オブジェクトの作成

JDBCアプリケーションが、JDBCドライバを持つデータベース・サブタイプ・オブジェクトを作成する場合があります。これらのオブジェクトは、バインド変数としてデータベースに送られるか、JDBCアプリケーション内での情報交換に使用されます。

カスタマイズされたマッピングの場合、JDBCアプリケーションは、開発者が選択した方法に応じて、データベース・サブタイプ・オブジェクトを表すSQLDataまたはORADataベースのオブジェクトを作成します。デフォルト・マッピングの場合、データベース・サブタイプ・オブジェクトを表すSTRUCTオブジェクトを作成します。スーパータイプから継承されたすべてのデータ・フィールド、およびサブタイプに定義されたすべてのフィールドに値が必要になります。次のコードを参照してください。

Connection conn = ...   // make a JDBC connection 
StructDescriptor desc = StructDescriptor.createDescriptor ("SCOTT.PARTTIMESTUDENT", conn); 
Object[] attrs = { 
  new Integer(1234), "Scott", "500 Oracle Parkway", // data fields defined in
                                                    // PERSON_T 
  new Integer(102), "CS",                           // data fields defined in
                                                    // STUDENT_T 
  new Integer(4)                                    // data fields defined in
                                                    // PARTTIMESTUDENT_T 
}; 
STRUCT s = new STRUCT (desc, conn, attrs);

sは、PERSON_TSTUDENT_Tから継承したデータ・フィールドおよびPARTTIMESTUDENT_Tで定義されているデータ・フィールドを使用して初期化されます。

サブタイプ・オブジェクトの送信

一般的なJDBCアプリケーションでは、データベース・オブジェクトを表すJavaオブジェクトは次のいずれかとしてデータベースに送られます。

  • データ操作言語(DML)バインド変数

  • PL/SQL INパラメータ

  • オブジェクト型属性値

Javaオブジェクトは、STRUCTクラスのインスタンス、またはSQLDataまたはORADataインタフェースを実装するクラスのインスタンスです。Oracle JDBCドライバは、データベースのSQLエンジンが認識できるシリアライズされた形式にJavaオブジェクトを変換します。サブタイプ・オブジェクトは、標準のオブジェクトの場合と同じ方法でバインドされます。

サブタイプ・データ・フィールドへのアクセス

サブタイプ・データ・フィールドにアクセスするためのロジックはカスタマイズされたクラスの一部ですが、デフォルト・マッピングではこのロジックはJDBCアプリケーションそのものに定義されます。データベース・オブジェクトは、oracle.sql.STRUCTクラスのインスタンスとして戻されます。JDBCアプリケーションは、STRUCTクラスの次のいずれかのアクセス・メソッドをコールして、データ・フィールドにアクセスする必要があります。

  • Object[] getAttribute()

  • oracle.sql.Datum[] getOracleAttribute()

getAttributeメソッドのサブタイプ・データ・フィールド

java.sql.StructインタフェースのgetAttributeメソッドは、JDBC 2.0で、オブジェクトのデータ・フィールドにアクセスするために使用されます。このメソッドはjava.lang.Object配列を戻します。ここで、各配列要素はオブジェクト属性を表します。個々の要素型は、JDBC変換マトリックス内の対応する属性型を参照することで確認できます。たとえば、SQL NUMBER属性はjava.math.BigDecimalオブジェクトに変換されます。getAttributeメソッドは、オブジェクト型のスーパータイプで定義されるすべてのデータ・フィールドと、サブタイプで定義されるデータ・フィールドを戻します。最初にスーパータイプのデータ・フィールドがリストされ、次にサブタイプのデータ・フィールドがリストされます。

getOracleAttributeメソッドのサブタイプ・データ・フィールド

getOracleAttributeメソッドはOracleの拡張機能メソッドで、getAttributeメソッドよりも効率的です。getOracleAttributeメソッドは、データ・フィールドを保持するoracle.sql.Datum配列を戻します。oracle.sql.Datum配列の各要素は属性を表します。個々の要素型は、Oracle変換マトリックス内の対応する属性型を参照することで確認できます。たとえば、SQL NUMBER属性はoracle.sql.NUMBERオブジェクトに変換されます。getOracleAttributeメソッドは、そのオブジェクト型のスーパータイプに定義されたすべての属性と、そのサブタイプに定義された属性を戻します。最初にスーパータイプのデータ・フィールドがリストされ、次にサブタイプのデータ・フィールドがリストされます。

次のコードは、getAttributeメソッドの使用方法を示します。

// tab1.person column can store PERSON_T, STUDENT_T and PARTIMESTUDENT_T objects 
ResultSet rset = stmt.executeQuery ("select person from tab1"); 
while (rset.next()) 
{ 
  oracle.sql.STRUCT s = (oracle.sql.STRUCT) rset.getObject(1); 
  if (s != null) 
  { 
    String sqlname = s.getSQLTypeName(); 

    Object[] attrs = s.getAttribute(); 

    if (sqlname.equals ("SCOTT.PERSON") 
    { 
      System.out.println ("ssn="+((BigDecimal)attrs[0]).intValue()); 
      System.out.println ("name="+((String)attrs[1])); 
      System.out.println ("address="+((String)attrs[2])); 
    } 
    else if (sqlname.equals ("SCOTT.STUDENT")) 
    { 
      System.out.println ("ssn="+((BigDecimal)attrs[0]).intValue()); 
      System.out.println ("name="+((String)attrs[1])); 
      System.out.println ("address="+((String)attrs[2])); 
      System.out.println ("deptid="+((BigDecimal)attrs[3]).intValue()); 
      System.out.println ("major="+((String)attrs[4])); 
    } 
    else if (sqlname.equals ("SCOTT.PARTTIMESTUDENT")) 
    { 
      System.out.println ("ssn="+((BigDecimal)attrs[0]).intValue()); 
      System.out.println ("name="+((String)attrs[1])); 
      System.out.println ("address="+((String)attrs[2])); 
      System.out.println ("deptid="+((BigDecimal)attrs[3]).intValue()); 
      System.out.println ("major="+((String)attrs[4])); 
      System.out.println ("numHours="+((BigDecimal)attrs[5]).intValue()); 
    } 
    else 
      throw new Exception ("Invalid type name: "+sqlname); 
  } 
} 
rset.close (); 
stmt.close (); 
conn.close ();

継承メタデータ・メソッド

Oracle JDBCドライバでは、継承プロパティにアクセスするための一連のメタデータ・メソッドを提供します。継承メタデータ・メソッドは、oracle.sql.StructDescriptorクラスおよびoracle.jdbc.StructMetaDataクラスに定義されます。

StructMetaDataクラスは、サブタイプ属性の継承メタデータ・メソッドを提供します。StructDescriptorクラスのgetMetaDataメソッドは、その型のStructMetaDataのインスタンスを戻します。StructMetaDataクラスには、次の継承メタデータ・メソッドが含まれます。

JPublisherを使用したカスタム・オブジェクト・クラスの作成

カスタム・オブジェクト・クラスや、他の種類のカスタムJavaクラスを作成する便利な手段に、Oracle JPublisherユーティリティを使用する方法があります。これによりカスタムJavaクラスの完全な定義が生成されます。それをインスタンス化することで、Oracleオブジェクトからのデータを保持することができます。JPublisherで生成されたクラスには、データをSQLからJavaに、およびJavaからSQLに変換するメソッドと、オブジェクト属性のgetterおよびsetterメソッドが含まれます。

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


関連項目:

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

JPublisherの機能

JPublisherを使用して、JPublisher型マッピングの設定に従ったSQLDataインタフェースまたはORADataインタフェースを実装するカスタム・オブジェクト・クラスを作成できます。

ORADataインタフェースを使用する場合は、JPublisherによって、Oracleオブジェクト型のオブジェクト参照にマップされるカスタム参照クラスも作成されます。SQLDataインタフェースを使用する場合、Jpublisherによるカスタム参照クラスの作成はありません。かわりに、標準java.sql.Refインスタンスを使用します。

その他の機能が必要な場合は、カスタム・オブジェクト・クラスをサブクラス化して、必要な機能を追加することができます。JPublisherを実行する場合、生成されたクラス名と実装するサブクラスの名前の両方を指定するためのコマンドライン・オプションがあります。SQL-Javaマッピングが正しく機能するためには、生成されたクラスの機能の一部に組み込まれサブクラス名が、JPublisherに認識される必要があります。


注意:

JPublisherで生成されたクラスをサブクラス化せずに手動で編集する方法はお薦めできません。クラスを手動で編集した後になんらかの理由によってJPublisherを再実行する場合は、変更を実装しなおす必要があります。

JPublisher型マッピング

JPublisherでは、ユーザー定義型とその属性型をSQLとJavaとの間でマップする、様々な方法が用意されています。この項では、SQL型のカテゴリおよび各カテゴリで使用可能なマップ・オプションの一覧を示します。

SQL型のカテゴリ

Jpublisherでは、SQL型が次のグループに分類されます。それぞれのグループでは、対応する次のJPublisherオプションを指定します。

  • ユーザー定義型(UDT)

    Oracleオブジェクト、参照およびコレクションが含まれます。JPublisher -usertypesオプションを使用して、UDTの型マッピング実装が標準SQLData実装か、Oracle固有のORAData実装かを指定します。

  • 数値型

    NUMBER SQL型としてデータベースに格納されるものが含まれます。JPublisher -numbertypesオプションを使用して、数値型の型マッピングを指定します。

  • ラージ・オブジェクト(LOB)型

    SQL型BLOBおよびCLOBが含まれます。JPublisher -lobtypesオプションを使用して、LOB型の型マッピングを指定します。

  • 組込み型

    前述のカテゴリに当てはまらないSQL型としてデータベースに格納されるものが含まれます。たとえば、CHARVARCHAR2LONGRAWなどがあります。JPublisher -builtintypesオプションを使用して、組込み型の型マッピングを指定します。

型マッピング・モード

JPublisherでは、次の型マッピング・モードが定義されています。このうち2つは、数値型にのみ適用されます。

  • JDBCマッピング(jdbcを設定)

    SQL型とネイティブなJava型間の標準デフォルト・マッピングを使用します。カスタム・オブジェクト・クラスの場合、SQLData実装を使用します。

  • Oracleマッピング(oracleを設定)

    対応するoracle.sql型を使用して、SQL型にマップします。カスタム・オブジェクト・クラス、参照クラスまたはコレクション・クラスの場合、ORAData実装を使用します。

  • オブジェクトJDBCマッピング(objectjdbcを設定)

    これは、JDBCマッピングの拡張要素です。オブジェクトJDBCマッピングでは、適切であれば、Javaプリミティブ型(intfloatdoubleなど)のかわりに、標準java.langパッケージの数値オブジェクト型(java.lang.IntegerFloatDoubleなど)が使用されます。java.lang型はNULL化可能ですが、プリミティブ型はNULL化できません。

  • BigDecimalマッピング(bigdecimalを設定)

    java.math.BigDecimalを使用して、すべての数値属性をマップします。これは、oracle.sql.NUMBERクラスにマップせずに、大きな数値を扱う場合に適しています。


    注意:

    BigDecimalマッピングを使用すると、パフォーマンスが大幅に低下することがあります。

Oracleオブジェクト型からJavaへのマッピング

JPublisher -usertypesオプションを使用すると、Oracleオブジェクト型に対応するカスタムJavaクラスを実装する方法が判断されます。

  • -usertypes=oracleと設定すると(デフォルト設定)、JPublisherによって、カスタム・オブジェクト・クラスに対するORAData実装が作成されます。また、対応するカスタム参照クラスのORAData実装が作成されます。

  • -usertypes=jdbcと設定すると、JPublisherによって、カスタム・オブジェクト・クラスに対するSQLData実装が作成されます。カスタム参照クラスは作成されません。参照型には、java.sql.Refまたはoracle.sql.REFを使用する必要があります。


注意:

-usertypes=oracleを設定してJPublisherを使用すると、SQLコレクション型をマップするためのORAData実装を作成することもできます。

-usertypes=jdbcという設定は、SQLコレクション型をマップするには有効ではありません。SQLDataインタフェースは、Oracleオブジェクト型のマッピング専用です。


属性型からJavaへのマッピング

Oracleオブジェクト型の属性型にマッピングを指定しない場合、JPublisherは次のデフォルトを使用します。

  • 数値属性型の場合、デフォルト・マッピングはオブジェクトJDBCです。

  • LOB属性型の場合、デフォルト・マッピングはOracleです。

  • 組込み型属性の場合、デフォルト・マッピングはJDBCです。

代替マッピングが必要な場合は、必要に応じて、属性型と必要なマッピングに対応する-numbertypes-lobtypesおよび-builtintypesオプションを使用します。

属性型自体がOracleオブジェクト型の場合は、-usertypesの設定に従ってマップされます。


重要:

カスタム・オブジェクト・クラスに対してSQLData実装を指定し、コードを移植可能にする場合は、属性値に対して移植可能なマッピングを使用する必要があることに注意してください。数値型と組込み型のデフォルトは移植可能ですが、LOB型には-lobtypes=jdbcを指定する必要があります。

SQL型カテゴリとマッピング設定のサマリー

表13-1では、SQL型のJPublisherカテゴリ、各カテゴリで使用できるマッピング設定およびデフォルト設定を示しています。

表13-1 JPublisherのSQL型カテゴリ、サポートされる設定およびデフォルト

SQL型カテゴリ JPublisherマップ・オプション マッピング設定 デフォルト

UDT型

-usertypes

oracle、jdbc

oracle

数値型

-numbertypes

oracle、jdbc、objectjdbc、bigdecimal

objectjdbc

LOB型

-lobtypes

oracle、jdbc

oracle

組込み型

-builtintypes

oracle、jdbc

jdbc


オブジェクト型の記述

Oracle JDBCには、構造化オブジェクト型の属性の名前と型に関する情報を取得する機能があります。これは考え方として、結果セットから列の名前と型に関する情報を取得するのに類似しており、実際ほとんど同一のメソッドを使用します。

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

オブジェクト・メタデータの取出し機能

oracle.sql.StructDescriptorクラスには、構造化オブジェクト型に関するメタデータを取り出す機能が含まれています。StructDescriptorクラスには、結果セット・オブジェクトで使用可能な標準getMetaDataメソッドと同じ機能を持つgetMetaDataメソッドがあります。このメソッドは、属性名や型など、属性情報の集合を戻します。このメソッドをStructDescriptorオブジェクトでコールして、そのStructDescriptorオブジェクトによって記述されているOracleオブジェクト型に関するメタデータを取り出します。

StructDescriptorクラスのgetMetaDataメソッドのシグネチャは、標準ResultSetインタフェースのgetMetaDataに指定されているシグネチャと同じです。シグネチャは次のとおりです。

ResultSetMetaData getMetaData() throws SQLException

ただし、このメソッドは、実際にはoracle.jdbc.StructMetaDataのインスタンスを戻します。このクラスは、標準java.sql.ResultSetMetaDataインタフェースが結果セット・メタデータのサポートを指定するのと同じ方法で、構造化オブジェクト・メタデータをサポートします。

次のメソッドもStructMetaDataでサポートされます。

String getOracleColumnClassName(int column) throws SQLException

このメソッドは、指定した属性の値を取り出すためにOracleResultSetクラスのgetOracleObjectメソッドがコールされた場合にインスタンスが作成されるoracle.sql.Datumサブクラスの完全修飾名を戻します。たとえば、oracle.sql.NUMBERが戻されます。

getOracleColumnClassNameメソッドを使用するには、getMetaDataメソッドで戻されたResultSetMetaDataオブジェクトをStructMetaDataにキャストする必要があります。


  • 注意:

    前述のメソッド・シグネチャのcolumnは、誤解を招く表現です。columnの値に4を指定すると、オブジェクトの4番目の属性を指すことになります。

オブジェクト・メタデータを取り出す手順

次の手順を使用して、構造化オブジェクト型のメタデータを取り出します。

  1. 該当する構造化オブジェクト型を記述するStructDescriptorインスタンスを作成するか取り出します。

  2. StructDescriptorインスタンスのgetMetaDataメソッドをコールします。

  3. 適切なメタデータgetterメソッド(getColumnNamegetColumnTypeおよびgetColumnTypeName)をコールします。


    注意:

    構造化オブジェクトの属性自身が構造化オブジェクトの場合、手順1から3を繰り返します。

次のメソッドは、構造化オブジェクト型の属性情報を取り出す方法を示します。この例には、StructDescriptorインスタンスを作成する最初の手順が含まれています。

// 
// Print out the ADT's attribute names and types 
// 
void getAttributeInfo (Connection conn, String type_name) throws SQLException 
{ 
  // get the type descriptor 
  StructDescriptor desc = StructDescriptor.createDescriptor (type_name, conn); 

  // get type metadata 
  ResultSetMetaData md = desc.getMetaData (); 

  // get # of attrs of this type 
  int numAttrs = desc.length (); 

  // temporary buffers 
  String attr_name; 
  int attr_type; 
  String attr_typeName; 

  System.out.println ("Attributes of "+type_name+" :"); 
  for (int i=0; i<numAttrs; i++) 
  { 
    attr_name = md.getColumnName (i+1); 
    attr_type = md.getColumnType (i+1); 
    System.out.println (" index"+(i+1)+" name="+attr_name+" type="+attr_type); 

    // drill down nested object 
    if (attrType == OracleTypes.STRUCT) 
    { 
      attr_typeName = md.getColumnTypeName (i+1); 

      // recursive calls to print out nested object metadata 
      getAttributeInfo (conn, attr_typeName); 
    } 
  } 
}