13 Oracleオブジェクト型の操作
この章では、Java Database Database Connectivity(JDBC)でのユーザー定義オブジェクト型のサポートについて説明します。一般的な、弱い型指定のoracle.sql.STRUCTクラスの機能や、JDBC標準のSQLDataインタフェースまたはOracle固有のOracleDataインタフェースを実装するカスタムJavaクラスをマップする方法を説明します。
ノート:
Oracle Database 12cリリース1 (12.1)以降、oracle.sql.STRUCTクラスは非推奨となり、oracle.jdbc.OracleStructインタフェースに置き換えられています。このインタフェースはoracle.jdbcパッケージに属します。標準互換性には(可能であれば)java.sqlパッケージの使用可能なメソッドを使用し、Oracle固有の拡張機能にはoracle.jdbcパッケージの使用可能なメソッドを使用することを強くお薦めします。oracle.jdbc.OracleStructインタフェースの詳細は、MoSノート1364193.1を参照してください。
内容は次のとおりです。
関連トピック
13.1 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つの主なステップを行います:
-
Oracleオブジェクト用にJavaクラスを作成します。
-
そのクラスにデータを移入します。次の選択肢があります。
-
JDBCを使用して、オブジェクトを
STRUCTオブジェクトとしてインスタンス化します。 -
OracleオブジェクトとJavaクラス間のマッピングを明示的に指定します。
これにはオブジェクト・データ用にJavaクラスをカスタマイズすることも含まれます。そうすることにより、ドライバが指定されたカスタム・オブジェクト・クラスのインスタンスにデータを移入できるようになります。この場合、Javaクラスにいくつかの制約が生じます。これらの制約を満たすには、JDBC標準
java.sql.SQLDataインタフェースまたはOracle拡張機能oracle.jdbc.OracleDataインタフェースを実装するようにクラスを定義します。
-
ノート:
SQLDataインタフェースを使用する場合、弱い型指定のjava.sql.Structオブジェクトで十分な場合を除き、Java型マップを使用してSQLとJavaのマッピングを指定する必要があります。
13.2 Oracleオブジェクト用のデフォルトSTRUCTクラスの使用方法について
この項の内容は次のとおりです。
13.2.1 Structクラスの使用の概要
OracleオブジェクトのSQLとJavaのマッピングを行うカスタムJavaクラスを提供しない場合、Oracle JDBCはオブジェクトをjava.sql.Structインタフェースを実装するオブジェクトとしてインスタンス化します。
実際のSQL型が不明な場合は、通常、カスタムJavaオブジェクトのかわりにSTRUCTオブジェクトを使用します。たとえば、Javaアプリケーションを、エンド・ユーザー・アプリケーションではなく、データベースの任意のオブジェクト・データを操作するツールとして使用する場合があります。データベースから選択したデータをSTRUCTオブジェクトに挿入したり、データベースにデータを挿入するためにSTRUCTオブジェクトを作成したりできます。STRUCTオブジェクトは、データをSQL形式で維持するため、データを完全に保存します。アプリケーション固有の形式による情報が不要な場合は、STRUCTオブジェクトを使用すると、データをより効率的に、より正確に保存できます。
13.2.2 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例外が通知されます。
13.2.3 STRUCTオブジェクトの作成について
STRUCTオブジェクト作成の詳細は、「Package oracle.sql」を参照してください。
ノート:
データベースから適切なSQLオブジェクト型のSTRUCTをフェッチしている場合、STRUCT記述子を取得する最も簡単な方法は、フェッチしたSTRUCTオブジェクトのいずれかでgetDescriptorをコールすることです。STRUCT記述子は1つのSQLオブジェクト型に1つのみ必要です。
13.2.4 STRUCTオブジェクトの文へのバインド
oracle.sql.STRUCTオブジェクトをプリペアド文またはコール可能文にバインドするには、標準setObjectメソッド(型コードを指定)を使用するか、文オブジェクトをOracle文の型にキャストしてからOracle拡張機能のsetOracleObjectメソッドを使用します。たとえば:
PreparedStatement ps= conn.prepareStatement("text_of_prepared_statement");
Struct mySTRUCT = conn.createStruct (...);
ps.setObject(1, mySTRUCT, Types.STRUCT);
または
PreparedStatement ps= conn.prepareStatement("text_of_prepared_statement");
Struct mySTRUCT = conn.createStruct (...);
((OraclePreparedStatement)ps).setOracleObject(1, mySTRUCT);13.2.5 STRUCT自動属性バッファリング
Oracle JDBCドライバには、STRUCT属性のバッファリングを有効または無効にするためのパブリック・メソッドが用意されています。
ノート:
Oracle Database 12cリリース1 (12.1)以降、oracle.sql.STRUCTクラスは非推奨となり、oracle.jdbc.OracleStructインタフェースに置き換えられています。このインタフェースはoracle.jdbcパッケージに属します。標準互換性には(可能であれば)java.sqlパッケージの使用可能なメソッドを使用し、Oracle固有の拡張機能にはoracle.jdbcパッケージの使用可能なメソッドを使用することを強くお薦めします。oracle.jdbc.OracleStructインタフェースの詳細は、MoSノート1364193.1を参照してください。
oracle.sql.STRUCTクラスには、次のメソッドが含まれます。
-
public void setAutoBuffering(boolean enable) -
public boolean getAutoBuffering()
setAutoBuffering(boolean)メソッドは自動バッファリングを有効または無効にします。getAutoBufferingメソッドは、現行の自動バッファリング・モードを戻します。デフォルトでは、自動バッファリングは無効です。
ARRAYデータをオーバーフローせずにJava仮想マシン(JVM)のメモリーに格納できると仮定し、STRUCT属性にgetAttributesメソッドとgetArrayメソッドで複数回アクセスする場合は、JDBCアプリケーションの自動バッファリングを有効にすることをお薦めします。
ノート:
変換した属性をバッファリングすると、JDBCアプリケーションでは、大量のメモリーが消費されます。
自動バッファリングを有効にすると、oracle.sql.STRUCTオブジェクトでは、変換したすべての属性のローカル・コピーが保持されます。このデータは保持されるため、この情報に後でアクセスするときにはデータ・フォーマット変換処理を実行しなくて済みます。
関連トピック
13.3 Oracleオブジェクト用のカスタム・オブジェクト・クラスの作成と使用方法について
この項の内容は次のとおりです。
13.3.1 カスタム・オブジェクト・クラスの作成および使用の概要
Oracleオブジェクト用にカスタム・オブジェクト・クラスを作成する場合は、型マップのエントリを定義する必要があります。ドライバはこの型マップに従ってOracleオブジェクトに対応するカスタム・オブジェクト・クラスをインスタンス化します。
Oracleオブジェクトとその属性データからカスタム・オブジェクト・クラスのインスタンスを作成し、データを移入する方法も指定する必要があります。ドライバによって、カスタム・オブジェクト・クラスからのデータの読取り、およびカスタム・オブジェクト・クラスへのデータの移入が実行できる必要があります。また、カスタム・オブジェクト・クラスは、提供する必要があるかどうかにかかわらず、Oracleオブジェクトの属性に対応するgetXXXメソッドおよびsetXXXメソッドも提供できます。カスタム・クラスの作成とデータ移入、およびドライバの読取り/書込み機能の設定を行うには、次のインタフェースのいずれかを選択します。
-
JDBC標準
SQLDataインタフェース -
Oracleが提供する
OracleDataインタフェースおよびOracleDataFactoryインタフェース
作成するカスタム・オブジェクト・クラスでは、これらのインタフェースのどちらかを実装する必要があります。OracleDataインタフェースは、カスタム・オブジェクト・クラスに対応するカスタム参照クラスを実装するときにも使用できます。ただし、SQLDataインタフェースを使用している場合、使用できるのは、java.sql.Refやoracle.sql.REFなど、弱いJava参照型のみです。SQLDataインタフェースは、SQLオブジェクトのマッピング専用です。
たとえば、データベースにEMPLOYEEというOracleオブジェクト型があり、そのオブジェクト型にはName(CHAR型)およびEmpNum(NUMBER型)という2つの属性が設定されているとします。型マップを使用して、EMPLOYEEオブジェクトがJEmployeeというカスタム・オブジェクト・クラスにマップされるように指定します。JEmployeeクラスでは、SQLDataまたはOracleDataインタフェースのどちらかを実装できます。
関連トピック
13.3.2 OracleDataとSQLDataの利点
2つのインタフェースのどちらを実装するかを決定する場合は、OracleDataとSQLDataの利点を考慮する必要があります。
SQLDataインタフェースは、SQLオブジェクトのマッピング専用です。OracleDataインタフェースは、より柔軟性が高く、他のSQL型と同じようにSQLオブジェクトをマップし、処理をカスタマイズできます。Oracle Databaseにあるどのデータ型からもOracleData実装を作成できます。これは、たとえば、JavaのRAWデータをシリアライズするときに役立ちます。
OracleDataインタフェースの利点
OracleDataインタフェースの利点は次のとおりです。
-
Oracleオブジェクトの型マップ・エントリが必要ありません。
-
Oracle拡張機能に対応しています。
-
oracle.sql.STRUCTからOracleDataを作成できます。この方法は、ネイティブなJava型への変換が最小限で済むため、より効率的です。 -
toJDBCObjectメソッドを使用して、OracleDataオブジェクトから、対応するJDBCオブジェクトを取得できます。
SQLDataの利点
SQLDataはJDBC標準であるため、コードの移植が可能です。
13.3.3 SQLDataを実装するための型マップについて
カスタム・オブジェクト・クラスでSQLDataインタフェースを使用する場合、Oracleオブジェクト型をJavaにマップする際に使用するカスタム・オブジェクト・クラスを指定する型マップ・エントリを作成する必要があります。接続オブジェクトのデフォルトの型マップか、結果セットからデータを取得するときに指定する型マップを使用できます。ResultSetインタフェースのgetObjectメソッドには、型マップの指定に使用できるシグネチャがあります。次のいずれかを使用できます。
rs.getObject(int columnIndex); rs.getObject(int columnIndex, Map map);
SQLData実装を使用する場合、型マップ・エントリを含めないと、オブジェクトはデフォルトでoracle.jdbc.OracleStructインタフェースにマップされます。これに対し、OracleData実装には独自のマッピング機能があるため、型マップ・エントリが必要ありません。OracleData実装を使用する場合、OracleのgetObject(int columnindex, OracleDataFactory factory)メソッドを使用します。
型マップを使用して、JavaクラスをOracleオブジェクトのSQL型名に関連付けます。このマップは1対1のマッピングで、ハッシュ表にキーワードと値のペアとして格納されます。Oracleオブジェクトからデータを読み取ると、JDBCドライバでは、型マップが参照され、Oracleオブジェクト型のデータのインスタンス化に使用されるJavaクラスが決定されます。Oracleオブジェクトにデータを書き込むと、JDBCドライバによって、SQLDataインタフェースのgetSQLTypeNameメソッドがコールされ、JavaクラスのSQL型名が取り出されます。SQLとJava間の実際の変換は、ドライバにより実行されます。
Oracleオブジェクトに対応しているJavaクラスの属性では、ネイティブなJava型またはOracleネイティブ型を使用して属性を格納できます。
13.3.4 SQLDataを実装するための型マップの作成とマッピングの定義について
13.3.4.1 型マップの作成およびマッピングの定義の概要
SQLData実装を使用する場合、型マップを提供するのは、JDBCアプリケーション・プログラマの役割です。型マップは、標準のjava.util.Mapインタフェースを実装するクラスのインスタンスにする必要があります。
独自のクラスも作成できますが、標準のjava.util.Hashtableクラスが要件を満たしています。
型マップに使用するHashtableなどのクラスは、putメソッドを実装します。このメソッドは、キーワードと値のペアを入力として取ります。各キーは完全修飾SQL型名で、対応する値は指定されたJavaクラスのインスタンスです。
型マップは、接続インスタンスに関連付けられます。標準java.sql.ConnectionインタフェースとOracle固有oracle.jdbc.OracleConnectionクラスには、getTypeMapメソッドが含まれています。両方ともMapオブジェクトを戻します。
13.3.4.4 型マップで指定されていないオブジェクト型のインスタンス化について
getObjectコールを使用するときに、適切なエントリを持つ型マップを指定しないと、JDBCドライバはoracle.jdbc.OracleStructインタフェースのインスタンスとしてOracleオブジェクトをインスタンス化します。Oracleオブジェクト型に埋込みオブジェクトが格納されていて、それらのオブジェクトが型マップに存在しない場合、ドライバは埋込みオブジェクトもoracle.jdbc.OracleStructのインスタンスとしてインスタンス化します。埋込みオブジェクトが型マップに存在する場合、getAttributesメソッドをコールすると、埋込みオブジェクトは型マップで指定されたJavaクラスのインスタンスとして戻されます。
13.3.5 SQLData実装によるデータの読取りおよび書込みについて
この項では、Oracleオブジェクトに対応するJavaクラスがSQLDataを実装している場合に、そのOracleオブジェクトに対してデータの読取りまたは書込みを行う方法について説明します。
結果セットからのSQLDataオブジェクトの読取り
ここでは、カスタム・オブジェクト・クラスに対してSQLData実装を選択したときに、OracleオブジェクトのデータをJavaアプリケーションに読み取るステップについて説明します。
このステップでは、すでにOracleオブジェクト型を定義し、対応するカスタム・オブジェクト・クラスを作成し、OracleオブジェクトとJavaクラス間のマッピングを定義する型マップを更新し、文オブジェクトstmtを定義しているものとします。
-
データベースに問合せを行い、OracleオブジェクトをJDBC結果セットに読み取ります。
ResultSet rs = stmt.executeQuery("SELECT emp_col FROM personnel");表
PERSONNELには、SQL型EMP_OBJECTのEMP_COLという列が1つ含まれています。このSQL型は、JavaクラスEmployeeにマップされるように、型マップに定義されています。 -
Oracleの結果セットの
getObjectメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスに結果セットの1行からデータを移入します。型マップにはEmployeeのエントリが含まれるため、getObjectメソッドによりユーザー定義のSQLDataオブジェクトが戻されます。if (rs.next()) Employee emp = (Employee)rs.getObject(1);
型マップにオブジェクトのエントリが存在しない場合は、
getObjectメソッドによりoracle.jdbc.OracleStructオブジェクトが戻されます。getObjectメソッド・シグネチャにより汎用のjava.lang.Object型が戻されるため、出力をOracleStruct型にキャストします。if (rs.next()) OracleStruct empstruct = (OracleStruct)rs.getObject(1);
getObjectメソッドがreadSQLをコールすると、SQLDataインタフェースからreadXXXがコールされます。ノート:
定義済の型マップを使用しないようにするには、
getSTRUCTメソッドを使用します。このメソッドでは、型マップにマッピング・エントリが定義されている場合でも、常にSTRUCTオブジェクトが戻されます。 -
カスタム・オブジェクト・クラスに
getメソッドが定義されている場合、このメソッドを使用して、オブジェクト属性のデータを読み取ります。たとえば、EMPLOYEEに、CHAR型の属性EmpNameおよびNUMBER型の属性EmpNumがある場合は、JavaStringを戻すgetEmpNameメソッドおよびint値を戻すgetEmpNumメソッドを指定します。次に、Javaアプリケーションでこれらのメソッドを次の方法でコールします。String empname = emp.getEmpName(); int empnumber = emp.getEmpNum();
コール可能文OUTパラメータからのSQLDataオブジェクトの取出し
PL/SQLファンクションGETEMPLOYEEをコールするCallableStatementインスタンスcsについて考えてみます。このファンクションには、プログラムによって従業員番号が渡されます。そのファンクションから対応するEmployeeオブジェクトが戻されます。このオブジェクトを取り出すには、次の手順を実行します。
-
GETEMPLOYEEファンクションをコールするCallableStatementを次のように準備します。CallableStatement ocs = conn.prepareCall("{ ? = call GETEMPLOYEE(?) }"); -
empnumberを、GETEMPLOYEEの入力パラメータとして宣言します。SQLDataオブジェクトを、型コードOracleTypes.STRUCTを指定してOUTパラメータとして登録します。次に、文を実行します。これは、次の方法で行います。cs.setInt(2, empnumber); cs.registerOutParameter(1, OracleTypes.STRUCT, "EMP_OBJECT"); cs.execute();
-
getObjectメソッドを使用して、employeeオブジェクトを取り出します。Employee emp = (Employee)cs.getObject(1);
型マップ・エントリが存在しない場合は、
getObjectメソッドによりjava.sql.Structオブジェクトが戻されます。Struct emp = cs.getObject(1);
SQLDataオブジェクトのINパラメータとしてのコール可能文への引渡し
EmployeeオブジェクトをINパラメータとして取り、そのオブジェクトをPERSONNEL表に追加するPL/SQLファンクションaddEmployee(?)があると仮定します。この例のempは、有効なEmployeeオブジェクトです。
-
addEmployee(?)ファンクションをコールするためにCallableStatementを準備します。CallableStatement cs = conn.prepareCall("{ call addEmployee(?) }"); -
setObjectを使用して、empオブジェクトをINパラメータとしてコール可能文に渡します。次に、文をコールします。cs.setObject(1, emp); cs.execute();
SQLData実装を使用したデータのOracleオブジェクトへの書込み
ここでは、カスタム・オブジェクト・クラスに対してSQLData実装を選択したときに、JavaアプリケーションのデータをOracleオブジェクトに書き込むステップについて説明します。
ここでは、あらかじめOracleオブジェクト型を定義し、対応するJavaクラスを作成し、OracleオブジェクトとJavaクラス間のマップを定義する型マップを更新していることを前提としています。
-
カスタム・オブジェクト・クラスに
setメソッドが定義されている場合、このメソッドを使用して、アプリケーションのJava変数のデータをJavaデータ型オブジェクトの属性に書き込みます。emp.setEmpName(empname); emp.setEmpNum(empnumber);
-
Javaデータ型オブジェクトのデータを使用して、データベース表の行に格納されているOracleオブジェクトを更新する文を、適切に準備します。
PreparedStatement pstmt = conn.prepareStatement ("INSERT INTO PERSONNEL VALUES (?)"); -
プリペアド文の
setObjectメソッドを使用して、Javaデータ型オブジェクトをプリペアド文にバインドします。pstmt.setObject(1, emp);
-
文を実行すると、データベースが更新されます。
pstmt.executeUpdate();
13.3.6 OracleDataインタフェースについて
oracle.jdbc.OracleDataおよびoracle.jdbc.OracleDataFactoryインタフェースを実装するカスタム・オブジェクト・クラスを作成して、Javaアプリケーションで使用できるOracleオブジェクトおよびその属性データを作成できます。OracleDataおよびOracleDataFactoryインタフェースはOracle固有であり、JDBC標準の一部ではありません。
ノート:
Oracle Database 12cリリース1 (12.1)以降、OracleDataおよびOracleDataFactoryインタフェースはORADataおよびORADataFactoryインタフェースに置き換わりました。
OracleDataインタフェース機能の理解
OracleDataインタフェースには、次の利点があります。
-
標準JDBC型に対するOracle拡張機能をサポートします。
-
作成中のJavaカスタム・クラスの名前を指定するときに、型マップは必要ありません。
-
これによりパフォーマンスが改善されます。
OracleDataは、Oracleオブジェクトを保持するためにドライバが使用する内部書式であるDatum型を直接伴って機能します。
OracleDataインタフェースおよびOracleDataFactoryインタフェースは次のように実行します。
-
OracleDataクラスのtoJDBCObjectメソッドでは、データをoracle.jdbc.*表現に変換します。 -
OracleDataFactoryでは、カスタム・オブジェクト・クラスのコンストラクタと等価のcreateメソッドが指定されます。このメソッドでは、OracleDataインスタンスを作成して戻します。JDBCドライバは、createメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスをJavaアプリケーションに戻します。このメソッドは、入力として、java.lang.Objectオブジェクトと、OracleTypesクラスで指定された対応するSQL型コードを示す整数を取ります。
OracleDataおよびOracleDataFactoryは、次のように定義されます。
package oracle.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
public interface OracleData
{
public Object toJDBCObject(Connection conn) throws SQLException;
}
package oracle.jdbc;
import java.sql.SQLException;
public interface OracleDataFactory
{
public OracleData create(Object jdbcValue, int sqlType) throws SQLException;
}
connは接続オブジェクトを、jdbcValueは作成されるオブジェクトを初期化するために使用するjava.lang.object型のオブジェクトを、sqlTypeは指定されたDatumオブジェクトのSQL型をそれぞれ表します。
オブジェクト・データの取出しと挿入
オブジェクト・データをOracleDataのインスタンスとして取得および挿入する場合、JDBCドライバでは次のメソッドを使用します。
オブジェクト・データは、次のいずれかの方法で取り出すことができます。
-
Oracle固有
OracleResultSetインタフェースの次のgetObjectメソッドを使用します。ors.getObject(int col_index, OracleDataFactory factory );
このメソッドでは、結果セットのデータの列索引および
OracleDataFactoryインスタンスを入力として取ります。たとえば、カスタム・オブジェクト・クラスにgetOracleDataFactoryメソッドを実装して、getObjectメソッドに入力するOracleDataFactoryインスタンスを作成できます。OracleDataを実装するJavaクラスを使用するときは、型マップは必要ありません。 -
ResultSetインタフェースで指定される標準getObject(index,map)メソッドを使用して、OracleDataのインスタンスとしてデータを取り出します。この場合、指定されたオブジェクト型で使用されるファクトリ・クラスおよび対応するSQL型名を識別するには、型マップにエントリを定義する必要があります。
オブジェクト・データは、次のいずれかの方法で挿入できます。
-
Oracle固有
OraclePreparedStatementクラスの次のsetObjectメソッドを使用します。setObject(int bind_index, Object custom_object);
このメソッドでは、入力としてバインド変数のパラメータ索引、および変数を含むオブジェクト名として
OracleDataのインスタンスを取ります。 -
PreparedStatementインタフェースで指定される標準setObjectメソッドを使用します。このメソッドを別のフォームで使用して、型マップなしでOracleDataインスタンスを挿入することもできます。
この後の項では、getObjectメソッドとsetObjectメソッドについて説明します。
OracleオブジェクトEMPLOYEEの例を引き続き使用するには、Javaアプリケーションに次のコードを記述する必要があります。
OracleData obj = ors.getObject(1, Employee.getOracleDataFactory());
この例では、orsはOracleResultSetインタフェースのインスタンスで、getObjectはOracleDataオブジェクトを取得するために使用されるOracleResultSetインタフェースのメソッドで、EMPLOYEEは結果セットの列1です。static Employee.getOracleDataFactoryメソッドによって、OracleDataFactoryがJDBCドライバに戻されます。JDBCドライバでは、このオブジェクトからcreate()をコールし、結果セットのデータが移入されたEmployeeクラスのインスタンスをJavaアプリケーションに戻します。
ノート:
-
OracleDataおよびOracleDataFactoryは、独立したインタフェースとして定義されるため、必要に応じて異なるJavaクラスから実装できます。 -
OracleDataインタフェースを使用するには、カスタム・オブジェクト・クラスでoracle.jdbc.*をインポートする必要があります。
13.3.7 OracleData実装によるデータの読取りおよび書込みについて
この項では、対応するJavaクラスがOracleDataを実装する場合に、Oracleオブジェクトに対してデータの読取りまたは書込みを行う方法について説明します。
OracleData実装を使用したOracleオブジェクトからのデータの読取り
ここでは、データをOracleオブジェクトからJavaアプリケーションに読み取るステップについて説明します。これらのステップでは、OracleDataを手動で実装するか、Oracle JVM Webサービス・コールアウト・ユーティリティを使用して、カスタム・オブジェクト・クラスを作成します。
このステップで、すでにOracleオブジェクト型を定義して対応するカスタム・オブジェクト・クラスを作成したか、Oracle JVM Webサービス・コールアウト・ユーティリティを使用してこれを作成し、文オブジェクトstmtを定義したものとします。
-
データベースに問合せを行ってOracleオブジェクトを結果セットに読み取り、Oracle結果セットにキャストします。
OracleResultSet ors = (OracleResultSet)stmt.executeQuery ("SELECT Emp_col FROM PERSONNEL");PERSONNELは、1列の表です。列名はEmp_col、型はEmployee_objectです。 -
Oracleの結果セットの
getObjectメソッドを使用して、カスタム・オブジェクト・クラスのインスタンスに結果セットの1行からデータを移入します。getObjectメソッドはjava.lang.Objectオブジェクトを戻します。これを固有のカスタム・オブジェクト・クラスにキャストすることができます。if (ors.next()) Employee emp = (Employee)ors.getObject(1, Employee.getOracleDataFactory());
または
if (ors.next()) Object obj = ors.getObject(1, Employee.getOracleDataFactory());
この例では、
Employeeがカスタム・オブジェクト・クラスの名前であり、orsがOracleResultSetインスタンスの名前であることを想定しています。たとえば、オブジェクトのSQL型名が
EMPLOYEEの場合は、対応するJavaクラスはEmployeeで、OracleDataが実装されます。対応するファクトリ・クラスはEmployeeFactoryで、OracleDataFactoryが実装されます。次の文を使用して、型マップに
EmployeeFactoryエントリを宣言します。map.put ("EMPLOYEE", Class.forName ("EmployeeFactory"));次に、
getObjectの形式を使用し、マップ・オブジェクトを指定します。Employee emp = (Employee) rs.getObject (1, map);
接続のデフォルトの型マップに、指定されたオブジェクト型と対応するSQL型名に対して使用されるファクトリ・クラスを識別するエントリがすでにある場合は、次の形式の
getObjectを使用できます。Employee emp = (Employee) rs.getObject (1);
-
カスタム・オブジェクト・クラスに
getメソッドがある場合は、そのメソッドを使用して、オブジェクト属性のデータをアプリケーションのJava変数に読み取ります。たとえば、EMPLOYEEに、CHAR型のEmpNameおよびNUMBER型のEmpNumがある場合は、JavaStringを戻すgetEmpNameメソッドおよび整数値を戻すgetEmpNumメソッドを指定します。次に、Javaアプリケーションでこれらのメソッドを次の方法でコールします。String empname = emp.getEmpName(); int empnumber = emp.getEmpNum();
OracleData実装を使用したデータのOracleオブジェクトへの書込み
ここでは、データをJavaアプリケーションからOracleオブジェクトに書き込むステップについて説明します。これらのステップでは、OracleDataを手動で実装するか、Oracle JVM Webサービス・コールアウト・ユーティリティを使用して、カスタム・オブジェクト・クラスを作成します。
このステップでは、すでにOracleオブジェクト型を定義し、対応するカスタム・オブジェクト・クラスを作成しているものとします。
ノート:
データベースのINSERTおよびUPDATE操作の実行時には、型マップは使用されません。
-
カスタム・オブジェクト・クラスに
setメソッドが定義されている場合、このメソッドを使用して、アプリケーションのJava変数のデータをJavaデータ型オブジェクトの属性に書き込みます。emp.setEmpName(empname); emp.setEmpNum(empnumber);
-
Javaデータ型オブジェクトのデータを使用して、Oracleオブジェクトを更新するプリペアド文を、適切にデータベース表の行に書き込みます。
OraclePreparedStatement opstmt = conn.prepareStatement ("UPDATE PERSONNEL SET Employee = ? WHERE Employee.EmpNum = 28959);この例では、
connはConnectionオブジェクトです。 -
OraclePreparedStatementインタフェースのsetObjectメソッドを使用して、Javaデータ型オブジェクトをプリコンパイルされた文にバインドします。opstmt.setObject(1,emp);
setObjectメソッドでは、カスタム・オブジェクト・クラス・インスタンスのtoJDBCObjectメソッドをコールすることにより、データベースに書き込むことができるoracle.jdbc.OracleStructオブジェクトが取り出されます。ノート:
Javaデータ型オブジェクトを、
INまたはOUTバインド変数として使用できます。
13.3.8 その他のOracleDataの使用方法
OracleDataインタフェースには、SQLDataインタフェースより優れた柔軟性があります。SQLDataインタフェースは、Oracleオブジェクト型から目的のJava型へのマッピングをカスタマイズする目的で設計されています。SQLDataインタフェースを実装すると、JavaとSQL型間の変換が正常に終了してから、元のSQLオブジェクト・データからカスタムJavaクラス・インスタンスのフィールドへのデータ移入、およびその逆が、JDBCドライバによって実行されます。
OracleDataインタフェースの場合は、Oracleオブジェクト型からJava型へのカスタマイズがサポートされるのみでなく、その場合、Javaオブジェクト型と、oracle.sqlパッケージでサポートされる任意のSQL型の間のマッピングを用意します。
このインタフェースは、oracle.sql.*型をラップするためのカスタムJavaクラスを提供したり、カスタマイズされた変換または機能を実装したりするときに役立ちます。次の使用例が考えられます。
-
データの暗号化および復号化、または妥当性チェックを実行します。
-
読取りまたは書込みを行った値のロギングを実行します。
-
URL情報を含む文字フィールドなどのキャラクタ列を小さなコンポーネントに解析します。
-
文字列を数値定数にマップします。
-
データをより適したJava形式にマップします。たとえば、
DATEフィールドをjava.util.Date形式にマップします。 -
データ表現をカスタマイズします。たとえば、表の列のデータがフィート単位のとき、選択後にメートルで表現します。
-
Javaオブジェクトをシリアライズおよびデシリアライズします。
たとえば、OracleDataを使用すると、データベースの特定のSQLオブジェクト型に対応していないJavaオブジェクトのインスタンスを、SQL型RAWの列に格納できます。OracleDataFactoryのcreateメソッドでは、oracle.sql.RAW型のオブジェクトから目的のJavaオブジェクトへの変換を実装する必要があります。OracleDataのtoJDBCObjectメソッドでは、Javaオブジェクトからoracle.sql.RAW型のオブジェクトへの変換を実装する必要があります。Javaシリアライズを使用してこれを実行することもできます。
JDBCドライバでは、データのRAWバイトをoracle.sql.RAW形式で透過的に取り出します。次に、OracleDataFactoryのcreateメソッドをコールし、oracle.sql.RAWオブジェクトを目的のJavaクラスに変換します。
データベースにJavaオブジェクトを挿入するときは、そのオブジェクトをRAW型の列にバインドするのみでデータベースに格納できます。ドライバは、OracleDataのtoJDBCObjectメソッドを透過的にコールして、Javaオブジェクトをoracle.sql.RAW型のオブジェクトに変換します。このオブジェクトは、データベースにRAW型の1列として格納されます。
JDBCドライバによって使用される内部書式であるoracle.sql.*書式を使用して変換が機能するように設計されているため、OracleDataインタフェースのサポートは非常に効率的でもあります。さらに、型マップはSQLDataインタフェースのために必要なものであり、OracleDataを実装するJavaクラスを使用する場合は不要です。
関連トピック
13.4 オブジェクト型の継承
オブジェクト型の継承により、別のオブジェクト型を拡張して新しいオブジェクト型を作成することができます。新しいオブジェクト型は、拡張元のオブジェクト型のサブタイプになります。サブタイプは、そのスーパータイプに定義されたすべての属性およびメソッドを自動的に継承します。サブタイプは、属性およびメソッドを追加し、スーパータイプから継承されたメソッドをオーバーロードまたはオーバーライドできます。
オブジェクト型の継承によって、代入可能性が導入されます。代入可能性とは、T型の任意のサブタイプに加えて、T型の値を保持するように宣言したスロットの機能です。Oracle JDBCドライバは、透過的に代入可能性を処理します。
データベース・オブジェクトは、情報が失われることなく、最も固有の型で戻されます。たとえば、STUDENT_TオブジェクトがPERSON_Tスロットに格納されている場合、Oracle JDBCドライバはSTUDENT_Tオブジェクトを表すJavaオブジェクトを戻します。
この項の内容は次のとおりです。
13.4.1 サブタイプの作成について
明示的にOracleオブジェクト型に対応するJavaクラスを作成するには、カスタム・オブジェクト・クラスを作成します。オブジェクト型の階層がある場合には、Javaクラスの対応する階層を作成できます。
JDBCでデータベース・サブタイプを作成する最も一般的な方法は、java.sql.Statementインタフェースのexecuteメソッドを使用して、SQLのCREATE TYPEコマンドを実行することです。たとえば、次の図で示すように型継承階層を作成する場合があります。
この場合、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(...) ... ... );
サブタイプを作成すると、実表の列またはオブジェクト型の属性として使用できます。
13.4.2 サブタイプに対してカスタマイズされたクラスの実装について
一般に、カスタマイズされたJavaクラスはデータベース・オブジェクト・タイプを表します。サブタイプに対してカスタマイズされたJavaクラスを作成するときには、Javaクラスでデータベース・オブジェクト・タイプの階層をミラー化するかどうかを選択できます。
クラスの作成には、OracleDataまたはSQLDataソリューションのいずれかを使用して、オブジェクト型の階層をマップできます。
この項の内容は次のとおりです。
13.4.2.1 型継承階層のためのOracleDataの使用について
oracle.sql.OracleDataインタフェースを実装するJavaクラスを使用した、カスタマイズされたマッピングを使用することをお薦めします。OracleDataのマッピングでは、JDBCアプリケーションがOracleDataおよびOracleDataFactoryインタフェースを実装する必要があります。OracleDataFactoryインタフェースを実装するクラスは、オブジェクトを作成するファクトリ・メソッドを格納します。各オブジェクトはそれぞれ1つのデータベース・オブジェクトを表します。
OracleDataインタフェースを実装するクラスの階層は、データベース・オブジェクト・タイプ階層をミラー化できます。たとえば、PERSON_TおよびSTUDENT_Tに対するJavaクラスのマッピングは、次のようになります。
OracleDataを使用するPerson.java
次のコードは、OracleDataおよびOracleDataFactoryインタフェースを実装するPerson_javaクラスを示します。
public static OracleDataFactory getOracleDataFactory()
{
return _personFactory;
}
public Person () {}
public Person(NUMBER ssn, CHAR name, CHAR address)
{
this.ssn = ssn;
this.name = name;
this.address = address;
}
public Object toJDBCObject(OracleConnection c) throws SQLException
{
Object [] attributes = { ssn, name, address };
Struct struct = c.createStruct("HR.PERSON_T", attributes);
return struct;
}
public OracleData create(Object jdbcValue, 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 OracleDataFactory getOracleDataFactory()
{
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 Object toJDBCObject(OracleConnection c) throws SQLException
{
Object [] attributes = { ssn, name, address, deptid, major };
Struct struct = c.createStruct("HR.STUDENT_T", attributes);
return struct;
}
public OracleData create(Object jdbcValue, 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]);
}
}
OracleDataインタフェースを実装するカスタマイズされたクラスでは、必ずしもデータベース・オブジェクト型階層をミラー化する必要はありません。たとえば、スーパークラスを指定せずにStudentクラスを宣言できます。この場合、Studentには、PERSON_Tから継承された属性およびSTUDENT_Tによって宣言された属性を保持するフィールドが含まれます。
OracleDataFactory実装
次の例に示すように、JDBCアプリケーションでは、データベースの問合せにファクトリ・クラスを使用して、Personまたはそのサブクラスのインスタンスを戻します。
ResultSet rset = stmt.executeQuery ("select person from tab1");
while (rset.next())
{
rset.getOracleData(1,Person.getOracleDataFactory());
...
}
OracleDataFactoryインタフェースを実装したクラスでは、関連付けられたカスタム・オブジェクト型のインスタンスおよび任意のサブタイプのインスタンス、または少なくともサポートされるすべての型のインスタンスの作成が可能になります。
次の例で、PersonFactory.getOracleDataFactoryメソッドは、Javaインスタンスperson、studentまたはparttimestudentを戻すことにより、PERSON_Tオブジェクト、STUDENT_TオブジェクトおよびPARTTIMESTUDENT_Tオブジェクトを処理できるファクトリを戻します。
class PersonFactory implements OracleDataFactory
{
static final PersonFactory _factory = new PersonFactory ();
public static OracleDataFactory getOracleDataFactory()
{
return _factory;
}
public OracleData create(Object jdbcValue, int sqlType) throws SQLException
{
STRUCT s = (STRUCT) jdbcValue;
if (s.getSQLTypeName ().equals ("HR.PERSON_T"))
return Person.getOracleDataFactory ().create (jdbcValue, sqlType);
else if (s.getSQLTypeName ().equals ("HR.STUDENT_T"))
return Student.getOracleDataFactory ().create(jdbcValue, sqlType);
else if (s.getSQLTypeName ().equals ("HR.PARTTIMESTUDENT_T"))
return ParttimeStudent.getOracleDataFactory ().create(jdbcValue, sqlType);
else
return null;
}
}
次の例では、次のような表tabl1があることを想定しています。
CREATE TABLE tabl1 (idx NUMBER, person PERSON_T); INSERT INTO tabl1 VALUES (1, PERSON_T (1000, 'HR', '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'));
13.4.2.2 型継承階層のための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);
}
}
13.4.3 サブタイプ・オブジェクトの取得について
一般的なJDBCアプリケーションでは、サブタイプ・オブジェクトは次のいずれかとして戻されます。
-
問合せ結果
-
PL/SQL
OUTパラメータ -
型属性
サブタイプの取得には、デフォルト・マッピング、SQLDataマッピングまたはOracleDataマッピングのいずれかを使用できます。
デフォルト・マッピングの使用
デフォルトで、データベース・オブジェクトは、oracle.jdbc.OracleStructインタフェースのインスタンスとして戻されます。このインスタンスは、宣言された型または宣言された型のサブタイプのオブジェクトを表します。OracleStructインタフェースがデータベースのサブタイプ・オブジェクトを表す場合、このインタフェースにはそのスーパータイプの属性と、サブタイプに定義された属性が含まれます。
Oracle JDBCドライバは、最も固有の型でデータベース・オブジェクトを戻します。JDBCアプリケーションは、OracleStructインタフェースの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 HR.PERSON_T, HR.STUDENT_T or HR.PARTTIMESTUDENT_T
}
SQLDataマッピングの使用
SQLDataマッピングの場合、JDBCドライバはSQLDataインタフェースを実装するクラスのインスタンスとして、データベース・オブジェクトを戻します。
データベース・オブジェクトの取得にSQLDataマッピングを使用するには、次の操作を実行します。
-
目的のオブジェクト型の
SQLDataインタフェースを実装するコンテナ・クラスを実装します。 -
接続型マップに各Oracleオブジェクト型に対応するカスタムJava型を指定するエントリが移入されます。
-
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 ("HR.PERSON_T", Class.forName ("Person"));
map.put ("HR.STUDENT_T", Class.forName ("Student"));
map.put ("HR.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メソッド
OracleDataマッピングの使用
OracleDataマッピングの場合、JDBCドライバはOracleDataインタフェースを実装するクラスのインスタンスとして、データベース・オブジェクトを戻します。
Oracle JDBCドライバは、Oracleオブジェクト型に対するJavaクラスのマッピングを認識する必要があります。Oracle JDBCドライバにこの情報を通知するには、次の2つの方法があります。
-
JDBCアプリケーションは、
getObject(int idx, OracleDataFactory f)メソッドを使用して、データベース・オブジェクトにアクセスします。getObjectメソッドの2番目のパラメータは、カスタマイズされたクラスを作成するファクトリ・クラスのインスタンスを指定します。getObjectメソッドは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.getObject(1, PersonFactory.getOracleDataFactory());
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");
}
}
13.4.4 サブタイプ・オブジェクトの作成
JDBCアプリケーションが、JDBCドライバを持つデータベース・サブタイプ・オブジェクトを作成する場合があります。これらのオブジェクトは、バインド変数としてデータベースに送られるか、JDBCアプリケーション内での情報交換に使用されます。
カスタマイズされたマッピングの場合、JDBCアプリケーションは、開発者が選択した方法に応じて、データベース・サブタイプ・オブジェクトを表すSQLDataまたはOracleDataベースのオブジェクトを作成します。デフォルト・マッピングの場合、データベース・サブタイプ・オブジェクトを表すSTRUCTオブジェクトをJDBCアプリケーションが作成します。スーパータイプから継承されたすべてのデータ・フィールド、およびサブタイプに定義されたすべてのフィールドに値が必要になります。次のコードでその方法を示します。
Connection conn = ... // make a JDBC connection
...
Object[] attrs = {
new Integer(1234), "HR", "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 = conn.createStruct("HR.PARTTIMESTUDENT", attrs);
sは、PERSON_TとSTUDENT_Tから継承したデータ・フィールドおよびPARTTIMESTUDENT_Tで定義されているデータ・フィールドを使用して初期化されます。
13.4.5 サブタイプ・オブジェクトの送信
一般的なJDBCアプリケーションでは、データベース・オブジェクトを表すJavaオブジェクトは次のいずれかとしてデータベースに送られます。
-
データ操作言語(DML)バインド変数
-
PL/SQL
INパラメータ -
オブジェクト型属性値
Javaオブジェクトは、STRUCTクラスのインスタンス、またはSQLDataまたはOracleDataインタフェースを実装するクラスのインスタンスです。Oracle JDBCドライバは、データベースのSQLエンジンが認識できるシリアライズされた形式にJavaオブジェクトを変換します。サブタイプ・オブジェクトは、標準のオブジェクトの場合と同じ方法でバインドされます。
13.4.6 サブタイプ・データ・フィールドへのアクセス
サブタイプ・データ・フィールドにアクセスするためのロジックはカスタマイズされたクラスの一部ですが、デフォルト・マッピングではこのロジックはJDBCアプリケーションそのものに定義されます。データベース・オブジェクトは、oracle.jdbc.OracleStructクラスのインスタンスとして戻されます。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 ("HR.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 ("HR.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 ("HR.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 ();
13.4.7 継承メタデータ・メソッド
Oracle JDBCドライバでは、継承プロパティにアクセスするための一連のメタデータ・メソッドを提供します。継承メタデータ・メソッドは、oracle.sql.StructDescriptorクラスおよびoracle.jdbc.StructMetaDataクラスに定義されます。
StructMetaDataクラスは、サブタイプ属性の継承メタデータ・メソッドを提供します。StructDescriptorクラスのgetMetaDataメソッドは、その型のStructMetaDataのインスタンスを戻します。StructMetaDataクラスには、次の継承メタデータ・メソッドが含まれます。
13.5 オブジェクト型の記述について
Oracle JDBCには、構造化オブジェクト型の属性の名前と型に関する情報を取得する機能があります。これは考え方として、結果セットから列の名前と型に関する情報を取得するのに類似しており、実際ほとんど同一のメソッドを使用します。
この項の内容は次のとおりです。
13.5.1 オブジェクト・メタデータの取出し機能
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番目の属性を指すことになります。
13.5.2 オブジェクト・メタデータの取得
次のステップを使用して、構造化オブジェクト型のメタデータを取り出します。
例13-1 例
次のメソッドは、構造化オブジェクト型の属性情報を取り出す方法を示します。この例には、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);
}
}
}