この章では、JPublisherを使用して次のことを行う方法を説明します。
JPublisherを使用すると、SQLオブジェクトまたはコレクションをJavaクラスとして簡単に公開できます。ここでは、Oracle Databaseサンプル・スキーマの一部であるOrder Entry(OE
)スキーマの例をあげて説明します。サンプル・スキーマをインストールしていなくても、公開対象となる独自のオブジェクト型がある場合は、ユーザー名、パスワードおよびオブジェクト名を適宜置き換えてください。
OE
スキーマのパスワードがOE
の場合、SQLオブジェクト型CATEGORY_TYP
を公開するには次のコマンドを使用します。%
はシステム・プロンプトです。
% jpub -user=OE -sql=CATEGORY_TYP:CategoryTyp
Enter OE password: password
JPublisherの-user
オプションは、ユーザー名を指定します。-sql
オプションでは、公開する型を指定します。SQL型とJavaクラスはコロン(:
)で区切ります。CATEGORY_TYP
はSQL型の名前、CategoryTyp
は生成対象となる対応するJavaクラスの名前です。
JPublisherでは、公開対象となるSQL型の名前が標準出力にエコーされます。
OE.CATEGORY_TYP
JPublisherでは、CategoryTyp.java
ファイルに加えてCategoryTypeRef.java
ファイルも生成されます。これは、OE.CATEGORY_TYP
へのSQLオブジェクト参照に対する強い型指定を持つラッパー・クラスです。どちらのファイルも、Javaコンパイラjavac
でコンパイルできます。
SQLオブジェクト型(この場合はCUSTOMER_TYP
型)を公開する別の例を次に示します。この例では、-user=
の短縮形-u
と-sql=
の短縮形-s
を使用します。
% jpub -u OE -s CUSTOMER_TYP:CustomerTyp
Enter OE password: password
オプションの-u
と-s
に続けて空白を1つ空けて、その後に値を指定します。
JPublisherでは、SQLオブジェクト型のリストがレポートされます。新しいオブジェクト型を検出するたびに、オブジェクトまたはコレクションとしての要素型を持つ属性、オブジェクト参照、コレクションであるかを問わず、その型のラッパー・クラスも自動的に生成されます。OE
スキーマのSQLオブジェクト型のリストは、次のとおりです。
OE.CUSTOMER_TYP OE.CORPORATE_CUSTOMER_TYP OE.CUST_ADDRESS_TYP OE.PHONE_LIST_TYP OE.ORDER_LIST_TYP OE.ORDER_TYP OE.ORDER_ITEM_LIST_TYP OE.ORDER_ITEM_TYP OE.PRODUCT_INFORMATION_TYP OE.INVENTORY_LIST_TYP OE.INVENTORY_TYP OE.WAREHOUSE_TYP
この例では、オブジェクト型ごとに2つのソース・ファイルが生成されます。1つは、オブジェクト型のインスタンスを表すCustomerTyp
などのJavaクラス用で、もう1つは、オブジェクト型の参照を表すCustomerTypeRef
などの参照クラス用です。
JPublisherでデフォルトで使用される命名規則に注意してください。たとえば、OE.PRODUCT_INFORMATION_TYP
というSQL型はJavaクラスのProductInformationTyp
に変換されます。
JPublisherでは埋込み型のラッパー・クラスは自動的に生成されますが、オブジェクト型のサブタイプが自動的に生成されることはありません。この場合は、公開するサブタイプすべてを明示的に列挙する必要があります。CATEGORY_TYP
型には、LEAF_CATEGORY_TYP
、COMPOSITE_CATEGORY_TYP
およびCATALOG_TYP
という3つのサブタイプがあります。このオブジェクト型のサブタイプを公開するためのJPublisherコマンドラインは、次のようになります(ここでは折り返されていますが、1行のコマンドラインです)。
% jpub -u OE -s COMPOSITE_CATEGORY_TYP:CompositeCategoryTyp
-s LEAF_CATEGORY_TYP:LeafCategoryTyp,CATALOG_TYP:CatalogTyp
Enter OE password: password
JPublisherでは、次のように、処理された型が出力としてリストされます。
OE.COMPOSITE_CATEGORY_TYP OE.SUBCATEGORY_REF_LIST_TYP OE.LEAF_CATEGORY_TYP OE.CATALOG_TYP OE.CATEGORY_TYP OE.PRODUCT_REF_LIST_TYP
次のことに注意してください。
複数の型を解析解除する場合は、それぞれカンマで区切って-sql
または-s
オプションに指定する方法と、コマンドラインに複数の-sql
オプションを指定する方法があります。
JPublisherでは、すべてのサブタイプのラッパー・クラスが自動的に生成されることはありませんが、すべてのスーパータイプのラッパー・クラスは生成されます。
CATALOG_TYP
などのメソッドを持つSQLオブジェクトの場合、JPublisherではSQLJクラスを使用してラッパー・メソッドが実装されます。Oracle Database 11gでは、通常のJavaクラスとは異なり、SQLJクラスの使用は下位互換性モードのいずれかを使用しないかぎり参照できません。
注意: Oracle Database 10gより前のバージョンでは、SQLJクラスの生成により参照可能な.sqlj ソース・ファイルが作成されます。Oracle Database 10g以降では、JPublisherの-compatible フラグを値8i 、both8i 、9i またはsqlj に設定すると、参照可能な.sqlj ソース・ファイルが生成されます。
この4つのモードのいずれでも、 |
JPublisherによって生成されたコードで必要な機能や動作が得られない場合は、生成されたラッパー・クラスを拡張することで、その機能をオーバーライドしたり補完できます。次の例を考えます。
% jpub -u OE -s WAREHOUSE_TYP:JPubWarehouse:MyWarehouse
Enter OE password: password
JPublisherの出力は次のとおりです。
OE.WAREHOUSE_TYP
このコマンドを実行すると、JPublisherではJPubWarehouse.java
とMyWarehouse.java
が生成されます。JPubWarehouse.java
ファイルは、このコマンドを実行するたびに生成されます。生成されたMyWarehouse.java
ファイルはカスタマイズでき、後でこのコマンドを実行しても上書きされません。また、MyWarehouse.java
に新規メソッドを追加し、JPubWarehouse.java
からのメソッド実装をオーバーライドすることもできます。
JavaでWAREHOUSE_TYP
インスタンスの実体化に使用されるクラスは、特殊なMyWarehouse
クラスです。オブジェクト型階層のすべての型にユーザー固有のサブクラスが必要な場合は、すべての階層メンバーについて、前述のJPublisherコマンドに示したように、SQL_TYPE
:
JPubClass
:
UserClass
形式のトリプレットを指定する必要があります。
JPublisherでJavaラッパー・クラスを生成してコンパイルした後は、オブジェクト・ラッパーを直接使用できます。
注意: 前述のOE スキーマの使用例はあくまでも具体例を示すことを目的としており、このスキーマの組立てに関して最新ではない場合があります。 |
次のSQLJクラスでは、PL/SQLストアド・プロシージャをコールします。register_warehouse
がIN OUT
パラメータとしてWAREHOUSE_TYP
インスタンスを取るものとします。コードのコメントは、対応する#sql
コマンドを示しています。デフォルトでは、SQLJコードは自動的に生成され変換されます。
java.math.BigDecimal location = new java.math.BigDecimal(10); java.math.BigDecimal warehouseId = new java.math.BigDecimal(10); MyWarehouse w = new MyWarehouse(warehouseId,"Industrial Park",location); // ************************************************************ // #sql { call register_warehouse(:INOUT w) }; // ************************************************************ // // declare temps oracle.jdbc.OracleCallableStatement __sJT_st = null; sqlj.runtime.ref.DefaultContext __sJT_cc = sqlj.runtime.ref.DefaultContext.getDefaultContext(); if (__sJT_cc==null) sqlj.runtime.error.RuntimeRefErrors.raise_NULL_CONN_CTX(); sqlj.runtime.ExecutionContext.OracleContext __sJT_ec = ((__sJT_cc.getExecutionContext()==null) ? sqlj.runtime.ExecutionContext.raiseNullExecCtx() : __sJT_cc.getExecutionContext().getOracleContext()); try { String theSqlTS = "BEGIN register_warehouse( :1 ) \n; END;"; __sJT_st = __sJT_ec.prepareOracleCall(__sJT_cc,"0RegisterWarehouse",theSqlTS); if (__sJT_ec.isNew()) { __sJT_st.registerOutParameter(1,2002,"OE.WAREHOUSE_TYP"); } // set IN parameters if (w==null) __sJT_st.setNull(1,2002,"OE.WAREHOUSE_TYP"); else __sJT_st.setORAData(1,w); // execute statement __sJT_ec.oracleExecuteUpdate(); // retrieve OUT parameters w = (MyWarehouse)__sJT_st.getORAData(1,MyWarehouse.getORADataFactory()); } finally { __sJT_ec.oracleClose(); }
Java Database Connectivity(JDBC)では、通常、SQL型名と対応するJavaクラスとの関係を接続インスタンス用の型マップに登録します。これは、接続ごとに1回必要です。この型マッピングは、次の例のようにして行うことができます。
java.util.Map typeMap = conn.getTypeMap(); typeMap.put("OE.WAREHOUSE_TYP", MyWarehouse.class); conn.setTypeMap(typeMap);
次のJDBCコードは、前述のJPublisher出力(変換後のSQLJコード)と同等です。
CallableStatement cs = conn.prepareCall("{call register_warehouse(?)}"); ((OracleCallableStatement)cs).registerOutParameter (1,oracle.jdbc.OracleTypes.STRUCT,"OE.WAREHOUSE_TYP"); cs.setObject(w); cs.executeUpdate(); w = cs.getObject(1);
SQLオブジェクトをマップするのみではなく、PL/SQLパッケージ全体をJavaクラスとしてカプセル化できます。JPublisherには、PL/SQLパッケージのストアド・プロシージャ用にJavaラッパー・メソッドを作成する機能が用意されています。
ただし、PL/SQLストアド・プロシージャをJavaメソッドとして表すという概念には問題があります。PL/SQLファンクションおよびプロシージャの引数にはPL/SQLのOUT
またはIN OUT
モードが使用されている可能性がありますが、Javaに引数を渡す際には等価のモードが存在しません。たとえば、int
引数を取るメソッドでは、コール元が新規の値を受け取れるようにこの引数を変更することができません。回避策として、JPublisherではOUT
引数とIN OUT
引数について単一要素の配列を生成できます。たとえば、整数配列のint[] abc
を考えてみます。入力値はabc[0]
に指定され、変更後の出力値もabc[0]
で戻されます。SQLオブジェクト型のメソッドのコード生成時にも、同様のパターンが使用されます。
注意: JavaでサポートされないPL/SQL固有の型をストアド・プロシージャで使用する場合は、これらの引数をSQLにマップしてからJavaにマップするための特殊なステップが必要です。 |
次のコマンドでは、SYS.DBMS_LOB
パッケージがJavaに公開されます。
% jpub -u scott -s SYS.DBMS_LOB:DbmsLob
Enter scott password: password
JPublisherの出力は次のとおりです。
SYS.DBMS_LOB
DBMS_LOB
はパブリックに参照できるため、SCOTT
などの異なるスキーマからアクセスできます。このJPublisher起動コードでは、PL/SQLパッケージのコールを含むDbmsLob.java
にSQLJクラスが作成されることに注意してください。生成されるJavaメソッドは、実際はインスタンス・メソッドです。これは、JDBC接続またはSQLJ接続コンテキストを使用してパッケージのインスタンスを作成し、そのインスタンス上でメソッドをコールするという考え方です。
Javaプリミティブ数値型にかわるオブジェクト型の使用
生成されたコードを調べると、JPublisherによって各種メソッドの引数としてjava.lang.Integer
が生成されたことがわかります。int
などのJavaプリミティブ型のかわりにInteger
などのJavaオブジェクト型を使用すると、SQLのNULL
値をJavaのnull
として直接表すことができ、JPublisherではこの値がデフォルトで生成されます。ただし、DBMS_LOB
パッケージの場合、オブジェクト型Integer
ではなくint
が使用されます。-numbertypes
オプションを使用すると、JPublisher起動コードを次のように変更できます。
% jpub -numbertypes=jdbc -u scott -s SYS.DBMS_LOB:DbmsLob
Enter scott password: password
JPublisherの出力は次のとおりです。
SYS.DBMS_LOB
SQLのトップ・レベルにあるプロシージャのラッパー・クラス
JPublisherでは、SQLのトップ・レベルにあるファンクションとプロシージャのラッパー・クラスも生成できます。次の例のように、特殊パッケージ名TOPLEVEL
を使用します。
% jpub -u scott -s TOPLEVEL:SQLTopLevel
Enter scott password: password
JPublisherの出力は次のとおりです。
SCOTT.top-level_scope
SQLのトップ・レベルの有効範囲にストアド・ファンクションまたはストアド・プロシージャがないと、警告が表示されます。
JavaクラスとしてのOracle Streamsアドバンスト・キュー(AQ)の公開は、PL/SQLストアド・プロシージャの公開に似ています。JPublisherでは、AQ Java Message Service(JMS)アプリケーション・プログラミング・インタフェース(API)を使用して、キューをJavaプログラムとして公開します。さらに、このJavaプログラムは、Webサービス・アセンブラによりWebサービスに公開できます。次を実行できます。
Oracle Streams AQは、キュー、トピックおよびストリームに分類できます。キューは、宣言済のペイロード・タイプを持つ1対1のメッセージ・チャネルです。トピックは、宣言済のペイロード・タイプを持つ1対多のメッセージ・チャネルです。ストリームは、ペイロード・タイプがSYS.ANYDATA
のキューまたはトピックです。
キュー、トピックまたはストリームは、次のように-sql
オプションを使用して公開できます。
%jpub -user=scott -sql=AQNAME:javaName Enter scott password: password
AQNAME
はキュー表、キュー、トピックまたはストリームの名前です。javaName
は対応するJavaクラスの名前です。
Microsoft Windowsの場合は、JPublisherでキューを公開するために、次のJavaアーカイブ(JAR)ファイルをCLASSPATH
に追加する必要があります。この2つのファイルは、Oracle Streams AQ用にJPublisherで生成されたコードを実行するために必要になります。
ORACLE_HOME/rdbms/jlib/jmscommon.jar ORACLE_HOME/rdbms/jlib/aqapi.jar
UNIXシステムの場合は、Oracle Database 11gリリース2(11.2)に付属のjpub
スクリプトにこのJARファイルが含まれています。
Oracle Streams AQに関して、-sql
オプションの使用方法はSQL型およびPL/SQLストアド・プロシージャの場合と同じです。サブクラスとインタフェースを指定できます。SQL型およびPL/SQLパッケージに対して使用できるその他のオプション(-genpattern
、-style
、-builtintypes
、-compatible
など)も、Oracle Streams AQに対して使用できます。
キューの公開には、SQL型またはPL/SQLストアド・プロシージャの公開に使用する設定と同じ設定を使用できます。
次のように宣言されているキューtoy_queue
を考えてみます。
CREATE TYPE scott.queue_message AS OBJECT ( Subject VARCHAR2(30), Text VARCHAR2(80) ); dbms_aqadm.create_queue_table ( Queue_table => 'scott.queue_queue_table', Queue_payload_type => 'scott.queue_message' ); dbms_aqadm.create_queue ( queue_name => 'scott.toy_queue', queue_table => 'scott.queue_queue_table' ); dbms_aqadm.start_queue ( queue_name => 'scott.toy_queue' );
次のコマンドでは、toy_queue
がJavaプログラムとして公開されます。
% jpub -user=scott -sql=toy_queue:ToyQueue
Enter scott password: password
注意: キューまたはトピックを作成するときは、ペイロード・タイプとしてSQL型を指定できます。ペイロード・タイプとJMSメッセージ・タイプは相互に変換されます。 |
このコマンドにより、次のAPIを持つToyQueue.java
が生成されます。
public class ToyQueue { public ToyQueue(); public ToyQueue(java.sql.Connection conn); public ToyQueue(javax.sql.DataSource dataSource); public void setConnection(java.sql.Connection conn); public void setDataSource(javax.sql.DataSource ds); public void addTypeMap(String sqlName, String javaName); public void send(QueueMessage payload); public QueueMessage receive(); public QueueMessage receiveNoWait(); public QueueMessage receive(java.lang.String selector, boolean noWait); }
PL/SQLストアド・プロシージャの場合と同じように、JPublisherでは接続用およびデータ・ソース管理用のAPI(setConnection()
やsetDataSource()
など)を生成します。addTypeMap()
メソッドを使用すると、ペイロード・タイプがSQL型階層の場合に型マッピングを指定できます。send()
メソッドは、メッセージをエンキューします。receive()
メソッドは、キューからメッセージをデキューします。このメソッドは、デキューするメッセージが使用可能になるまでキューをブロックします。receiveNoWait()
メソッドは、メッセージをデキューし、デキュー対象のメッセージがない場合はnull
を戻します。ToyQueue
クラスの最後のreceive()
メソッドは、メッセージをデキューし、セレクタの条件を満たします。セレクタとは、AQ規則に指定されている条件です。たとえば、次のような条件を考えてみます。
priority > 3 and Subject IN ('spider','tank')
この条件により、3
より高い優先順位で、かつ、Subject
属性としてspider
とtank
を含むメッセージが選択されます。
QueueMessage
はORAData
のサブクラスで、queue_message
ペイロード・タイプ用に生成されています。これはキューの公開の結果として公開されるSQL型です。次のクライアント・コード例では、生成されたToyQueue
クラスを使用しています。このクライアント・コードはキューにメッセージを送信し、ブロック演算子receive()
を使用してデキューし、キュー内のメッセージがすべてデキューされるまで、receiveNoWait()
を使用してメッセージをデキューし続けます。
... ToyQueue q = new ToyQueue(getConnection()); QueueMessage m = new QueueMessage("scooby doo", "lights out"); q.send(m); System.out.println("Message sent: " + m.getSubject() + " " + m.getText()); m = new QueueMessage("dalmatian", "solve the puzzle"); q.send(m); System.out.println("Message sent: " + m.getSubject() + " " + m.getText()); m = q.receive(); while (m!=null) { System.out.println("Message received: " + m.getSubject() + " " + m.getText()); m = q.receiveNoWait(); } ...
次のように宣言されているトピックについて考えてみます。
CREATE TYPE scott.topic_message AS OBJECT ( Subject VARCHAR2(30), Text VARCHAR2(80) ); dbms_aqadm.create_queue_table ( Queue_table => 'scott.topic_queue_table', Multiple_consumers => TRUE, Queue_payload_type => 'scott.topic_message' ); dbms_aqadm.create_queue ( queue_name => 'scott.toy_topic', queue_table => 'scott.topic_queue_table' ); dbms_aqadm.start_queue ( queue_name => 'scott.toy_topic' );
キュー表topic_queue_table
ではMultiple_consumers
プロパティがTRUE
に設定され、このキュー表にはキューではなくトピックが含まれていることを示します。
トピックは次のように公開できます。
% jpub -user=scott -sql=toy_topic:ToyTopic
Enter scott password: password
このコマンドにより、次のAPIを持つToyTopic.java
が生成されます。
public class ToyTopic { public ToyTopic(javax.sql.DataSource dataSource); public void setConnection(java.sql.Connection conn); public void setDataSource(javax.sql.DataSource ds); public void addTypeMap(String sqlName,String javaName); public void publish(TopicMessage payload); public void publish(TopicMessage payload, java.lang.String[] recipients); public void publish(TopicMessage payload, int deliveryMode, int priority, long timeToLive); public void subscribe(java.lang.String subscriber); public void unsubscribe(java.lang.String subscriber); public TopicMessage receiveNoWait(java.lang.String receiver); public TopicMessage receive(java.lang.String receiver); public TopicMessage receive(java.lang.String receiver, java.lang.String selector); }
publish
メソッドは、全サブスクライバ宛またはサブスクライバ・リスト宛のメッセージをエンキューします。deleveryMode
パラメータは値javax.jms.DeliveryMode.PERSISTENT
またはjavax.jms.DeliveryMode.NON_PERSISTENT
を取ります。ただし、Oracle Database 10gリリース2 (10.2)でサポートされているのはDeliveryMode.PERSISTENT
のみです。priority
パラメータは、メッセージの優先順位を指定します。timeToLive
パラメータは、メッセージがタイムアウトになるまでの経過時間(ミリ秒)を指定します。値0
を指定すると、メッセージがタイムアウトしないことを示します。receive
メソッドは指定された受信者宛のメッセージをデキューします。次のクライアント・コード例では、生成されたToyTopic
クラスを使用しています。クライアントは2つの受信者(ToyParty
とToyFactory
)にメッセージを送信し、トピックをそれぞれToyParty
、ToyLand
およびToyFactory
としてデキューします。
... ToyTopic topic = new ToyTopic(getConnection()); TopicMessage m = new TopicMessage("scooby doo", "lights out"); topic.publish(m, new String[]{"ToyParty", "ToyFactory"}); System.out.println("Message broadcasted: " + m.getSubject() + " " + m.getText()); m = new TopicMessage("dalmatian", "solve the puzzle"); topic.publish(m, new String[]{"ToyParty", "ToyLand"}); System.out.println("Message broadcasted: " + m.getSubject() + " " + m.getText()); m = topic.receive("ToyParty"); System.out.println("ToyParty receive " + m.getSubject() + " " + m.getText()); m = topic.receive("ToyParty"); System.out.println("ToyParty receive " + m.getSubject() + " " + m.getText()); m = topic.receiveNoWait("ToyLand"); System.out.println("ToyFactory receive " + m.getSubject() + " " + m.getText()); m = topic.receiveNoWait("ToyFactory"); System.out.println("ToyFactory receive " + m.getSubject() + " " + m.getText()); m = topic.receiveNoWait("ToyFactory"); ...
ストリームは特殊なAQです。ペイロード・タイプとして持つことができるのはSYS.ANYDATA
のみです。制限事項として、JPublisherで生成されたストリーム用のコードにはDBC Oracle Call Interface (OCI)ドライバが必要です。ただし、キューやトピック用に生成されたコードは、JDBC ThinドライバとJDBC OCIドライバの両方で実行できます。
ストリームの公開はAQの公開と似ています。次のコマンドでは、toy_stream
ストリームが公開されます。
% jpub -user=scott -sql=toy_stream:ToyStream
Enter scott password: password
このコマンドにより、ToyStream.java
ファイルが生成されます。
ストリームの公開とAQまたはトピックの公開との違いは、ストリームを公開するときはペイロード・タイプが常にSYS.ANYDATA
で、これはjava.lang.Object
にマップされるということです。
ToyStream.java
ファイルには次のAPIが含まれています。
public class ToyStream { public ToyStream(); public ToyStream(java.sql.Connection conn); public ToyStream(javax.sql.DataSource dataSource); public void setConnection(java.sql.Connection conn); public void setDataSource(javax.sql.DataSource ds); public void addTypeMap(String sqlName, String javaName); public void publish(Object payload); public void publish(Object payload, java.lang.String[] recipients); public void publish(Object payload, int deliveryMode, int priority, long timeToLive); public void subscribe(java.lang.String subscriber); public void unsubscribe(java.lang.String subscriber); public Object receiveNoWait(java.lang.String receiver); public Object receive(java.lang.String receiver); public Object receive(java.lang.String receiver, java.lang.String selector); public Object receive(java.lang.String receiver, java.lang.String selector, long timeout); }
生成されたToyStream
クラスを使用するコード例を次に示します。
... System.out.println("*** testStream with an OCI connection"); Object response = null; ToyStream stream = new ToyStream(getOCIConnection()); stream.publish("Seaside news", new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println("Received: " + response); stream.publish(new Integer(333), new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println("Received: " + response); stream.publish(new Float(3.33), new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println("Received: " + response); stream.publish("Science Monitor".getBytes(), new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println("Received: " + new String((byte[])response)); stream.publish(new String[]{"gamma", "beta"}, new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println("Received: " + ((String[]) response)[0]); HashMap map = new HashMap(); map.put("US", "dollar"); map.put("Japan", "yen"); map.put("Austrilia", "dollar"); map.put("Britian", "pound"); stream.publish(map, new String[]{"ToyParty"}); response = stream.receive("ToyParty"); map = (HashMap) response; System.out.println("Message received: " + map.get("Britian") + ", " + map.get("US") + ", " + map.get("Austrilia")); stream.addTypeMap("SCOTT.QUEUE_MESSAGE", "queue.wrapper.simple.QueueMessage"); stream.addTypeMap("QUEUE_MESSAGE", "queue.wrapper.simple.QueueMessage"); QueueMessage m = new QueueMessage("Knowing", "world currency"); stream.publish(m, new String[]{"ToyParty"}); response = stream.receive("ToyParty"); System.out.println(response); m = (QueueMessage) response; System.out.println("Message received: " + m.getSubject() + " " + m.getText()); ...
このコード例は、String
、Integer
およびjava.util.Map
などの様々な型のメッセージを送信します。JDBCカスタム型QueueMessage
に関して、addTypeMap()
メソッドがコールされて、SQL型からJavaへの型マッピングが指定されます。
Oracle Database 10gより前のリリースでは、データベース・クライアントからJavaストアド・プロシージャおよびファンクションをコールするには、関連するPL/SQLラッパーへのJDBCコールが必要でした。各PL/SQLラッパーは、SQLシグネチャとJava実装を使用して手動で公開する必要がありました。このプロセスには、次のデメリットがありました。
シグネチャに使用できるのは、SQLに直接の等価型を持つJava型のみでした。
Javaで発行された例外は正しく戻されませんでした。
Oracle Database 10gから、サーバー・サイドJavaコードをコールするためのネイティブJavaインタフェース機能を使用できます。JPublisherの-java
オプションは、これらのデメリットを克服する機能を提供します。
関連するPL/SQLラッパーへのJDBCコールの問題を解消するために、-java
オプションを指定することで、Javaの静的メソッドを直接コールするAPIを使用できます。この機能はWebサービスにも有効です。
-java
オプションの機能は、-sql
オプションの機能をミラー化したもので、サーバー・サイドJavaクラスにアクセスするクライアント・サイドJavaスタブ・クラスを作成します。これは、サーバー・サイドSQLオブジェクトまたはPL/SQLパッケージにアクセスするクライアント・サイドJavaクラスの作成とは対照的です。クライアント・サイド・スタブ・クラスでは、サーバー・サイド・クラスをミラー化するSQLコードが使用され、次の機能が含まれます。
サーバー・クラスのパブリック・メソッド、静的メソッドに対応するメソッド。
2つのコンストラクタ。一方はJDBC接続を使用し、他方はSQLJのデフォルト接続コンテキスト・インスタンスを使用します。
実行時には、スタブ・クラスがJDBC接続を使用してインスタンス化されます。そのメソッドをコールすると、サーバー・サイド・クラスの対応するメソッドがコールされます。このようにして公開されたメソッドに使用されるJava型は、プリミティブであるか、またはシリアライズ可能である必要があります。
次のように、-java
オプションを使用してサーバー・サイドJavaクラスを公開できます。
-java=className
次のAPIを持つoracle.sqlj.checker.JdbcVersion
サーバー・サイドJavaクラスについて考えてみます。
public class oracle.sqlj.checker.JdbcVersion { public oracle.sqlj.checker.JdbcVersion(); public static int getDriverMajorVersion(); public static int getDriverMinorVersion(); public static java.lang.String getDriverName(); public static java.lang.String getDriverVersion(); public static java.lang.String getJdbcLibraryName(); public static java.lang.String getRecommendedRuntimeZip(); public static java.lang.String getRuntimeVersion(); public static java.lang.String getSqljLibraryName(); public static boolean hasNewStatementCache(); public static boolean hasOracleContextIsNew(); public static boolean hasOracleSavepoint(); public static void main(java.lang.String[]); public java.lang.String toString(); public static java.lang.String to_string(); }
たとえば、サーバー上の次のメソッドをコールする必要があるとします。
public String oracle.sqlj.checker.JdbcVersion.to_string();
次のコマンドを使用して、クライアント・サイド起動用のJdbcVersion
を公開します。
% jpub -sql=scott -java=oracle.sqlj.checker.JdbcVersion:JdbcVersion Client
Enter scott password: password
このコマンドにより、次のAPIを含むクライアント・サイドJavaクラスJdbcVersionClient
が生成されます。
public class JdbcVersionClient { public long newInstance(); public JdbcVersionClient(); public JdbcVersionClient(java.sql.Connection conn); public JdbcVersionClient(sqlj.runtime.ref.DefaultContext ctx); public java.lang.String toString(long _handle); public int getDriverMajorVersion(); public int getDriverMinorVersion(); public java.lang.String getDriverName(); public java.lang.String getDriverVersion(); public java.lang.String getJdbcLibraryName(); public java.lang.String getRecommendedRuntimeZip(); public java.lang.String getRuntimeVersion(); public java.lang.String getSqljLibraryName(); public boolean hasNewStatementCache(); public boolean hasOracleContextIsNew(); public boolean hasOracleSavepoint(); public void main(java.lang.String[] p0); public java.lang.String to_string(); }
oracle.sqlj.checker.JdbcVersion
とJdbcVersionClient
を比較してみます。クライアント・サイド・コードでは、すべての静的メソッドがインスタンス・メソッドにマップされます。サーバー・サイド・クラスのインスタンス・メソッド(たとえば、toString()
)は、追加のハンドルを使用してメソッドにマップされます。ハンドルは、サーバーでのoracle.sqlj.checker.JdbcVersion
のインスタンスを表します。ハンドルは、サーバー・サイドのインスタンス・メソッドのコールに使用されます。JdbcVersionClient
の追加メソッドはnewInstance()
で、これはサーバー内にoracle.sqlj.checker.JdbcVersion
の新規インスタンスを作成し、そのハンドルを戻します。
サーバー・サイドJavaクラスの公開には、次のような制約があります。
インスタンス・メソッドを公開できるのは、公開対象のクラスに空のpublic
コンストラクタがある場合のみです。
サポートされるのは、シリアライズ可能なパラメータと戻り型のみです。シリアライズ可能でない型を使用するメソッドは、公開されません。
Oracle Database 11gまたはOracle Database 10gが必要です。
Oracle Database 10gリリース2 (10.2)以降では、JPublisherはサーバー・サイドJavaクラスの公開に対して新しいアプローチを提供します。これは、サーバー・サイドJavaをコールするために次のものを生成します。
サーバー・サイド・クラスのためのJavaストアド・プロシージャ・ラッパー
Javaストアド・プロシージャ・ラッパーのためのPL/SQLラッパー
PL/SQLラッパーをコールするクライアント・サイドJavaコード
Javaストアド・プロシージャはサーバー・サイドJavaコードをラップしますが、これにより、次のことが行われます。
インスタンス・メソッドを静的メソッドにラップします。サーバー・サイドJavaコード内の各メソッドが、静的メソッドによりラップされます。インスタンス・メソッドは、単一インスタンス形式または複数インスタンス形式でマップできます。
Java型をPL/SQLコール仕様に公開可能な型に変換します。たとえば、Java型byte[]
はoracle.sql.BLOB
に変換されます。
PL/SQLラッパーはJavaストアド・プロシージャをコールします。クライアント・サイドJavaコードは、JDBCコールを介してPL/SQLラッパーをコールします。-java
オプションでは、公開対象のクラスがすでにデータベースにロードされていることが前提です。
サポートされているJava型は次のとおりです。
JDBCでサポートされている型
JavaBeans
サポートされている型の配列
シリアライズ可能な型
サーバー・サイド・クラスを公開するには、次のように、-dbjava
オプションを使用します。
-dbjava=server-sideClassName:client-sideClassName
client-sideClassName
設定を指定する必要があります。指定しないと、JPublisherでクライアント・サイドJavaクラスが生成されません。oracle.sqlj.checker.JdbcVersion
を公開するには、次のコマンドを使用します。
% jpub -user=scott -dbjava=oracle.sqlj.checker.JdbcVersion:JdbcVersionClient
Enter scott password: password
このコマンドにより、次の出力が生成されます。
oracle/sqlj/checker/JdbcVersionJPub.java plsql_wrapper.sql plsql_dropper.sql SCOTT.JPUBTBL_VARCHAR2 SCOTT.JPUB_PLSQL_WRAPPER Executing plsql_dropper.sql Executing plsql_wrapper.sql Loading JdbcVersionJPub.java
このコマンドにより、Javaストアド・プロシージャJdbcVersionJPub
、PL/SQLラッパー、およびクライアント・サイドのJdbcVersionClient
クラスが生成されます。JdbcVersionJPub.java
およびplsql_wrapper.sql
は、データベースに自動的にロードされます。JdbcVersionClient
には次のAPIが含まれます。
public class JdbcVersionClient { public JdbcVersionClient(); public JdbcVersionClient(java.sql.Connection conn); public void setConnection(java.sql.Connection conn); public void setDataSource(javax.sql.DataSource ds); public String toString0(); public java.math.BigDecimal getDriverMajorVersion(); public java.math.BigDecimal getDriverMinorVersion(); public String getDriverName(); public String getDriverVersion(); public String getJdbcLibraryName(); public String getRecommendedRuntimeZip(); public String getRuntimeVersion(); public String getSqljLibraryName(); public java.math.BigDecimal hasNewStatementCache(); public java.math.BigDecimal hasOracleContextIsNew(); public java.math.BigDecimal hasOracleSavepoint(); public void main0(JpubtblVarchar2 arg0); public String to_string(); }
JdbcVersion
とJdbcVersionClient
を比較してみます。JPublisherで生成されたコードの制限を示しています。生成されたクライアント・サイドAPIは、元のサーバー・サイドAPIとまったく同じではありません。この制限をわかりやすく示すために、JdbcVersion
とJdbcVersionClient
との違いをいくつか次にリストします。
クライアント・サイドのメソッドではJDBC接続の実行が必要であるため、静的メソッドはすべてインスタンス・メソッドにマップされます。
クライアント・サイドのメソッドでは常にjava.sql.SQLException
がスローされますが、サーバー・サイドのクラスによりスローされる例外は、SQLException
でラップされてクライアントに渡されます。
toString()
メソッドの名前はtoString0()
に変更されます。これはストアド・プロシージャ・ラッパーに起因する制限です。ここでは、java.lang.Object
メソッドを上書きするメソッドは、競合を避けるために名前を変更する必要があります。
パラメータ型と戻り型が異なる可能性があります。サーバー・サイドの数値型はjava.math.BigDecimal
にマップされます。String[]
などの配列型は、JDBCカスタム型にマップされます。たとえば、main()
のパラメータはORAData
のサブクラスであるJpubtblVarchar2
にマップされます。これは、文字列の配列を表すためにJPublisherのコマンドにより生成されます
サーバー・サイドJavaクラスのmain()
メソッドは、Javaストアド・プロシージャの制限により、main0()
に変更されます。
-java
と比較すると、-dbjava
の長所は、サポートされる型の数が多いことと、Oracle Database 10gよりも前のバージョンで機能することです。ただし、短所は、実行時にPL/SQLおよびJavaストアド・プロシージャで追加のレイヤーが使用されることと、クライアント・サイドJavaクラスでメソッド・シグネチャが変更される可能性が高くなることです。
JPublisherでは、サーバー・サイドJavaクラス用のPL/SQLラッパーを生成できます。JavaクラスはPL/SQLパッケージにマップされます。それぞれのPL/SQLメソッドがJavaメソッドに対応します。この機能により、PL/SQLコール仕様を作成する必要と、コール仕様で使用されるSQL型を作成する必要がなくなります。
次のように、-dbjava
オプションを使用すると、サーバー・サイドJavaクラス用のPL/SQLラッパーを生成できます。
-dbjava=server-sideJavaClass
server-sideJavaClass
の後に名前を指定しないでください。指定すると、JPublisherでサーバー・サイドJavaクラスがクライアント・サイドJavaクラスにマップされます。
例として、次のコマンドを使用してoracle.sqlj.checker.JdbcVersion
のためのPL/SQLラッパーを生成します。
% java -dbjava=oracle.sqlj.checker.JdbcVersion
このコマンドにより、次の出力が生成されます。
oracle/sqlj/checker/JdbcVersionJPub.java plsql_wrapper.sql plsql_dropper.sql Executing plsql_dropper.sql Executing plsql_wrapper.sql Loading JdbcVersionJPub.java
このコマンドは、Javaストアド・プロシージャのラッパーJdbcVersionJPub.java
とそのPL/SQLラッパーplsql_wrapper.sql
を生成してロードします。これは、パッケージJPUB_PLSQL_WRAPPER
を宣言します。JPUB_PLSQL_WRAPPER
パッケージを使用すると、oracle.sqlj.checker.JdbcVersion
のメソッドをコールできます。
-dbjava
と一緒に-plsqlfile
および-plsqlpackage
を指定することがよくあります。次のコマンドを考えてみます。
% java -dbjava=oracle.sqlj.checker.JdbcVersion -plsqlfile=jdbcversion.sql -plsqlpackage=jdbcversion
このコマンドにより、次の出力が生成されます。
oracle/sqlj/checker/JdbcVersionJPub.java jdbcversion.sql jdbcversion_dropper.sql Executing jdbcversion_dropper.sql Executing jdbcversion.sql Loading JdbcVersionJPub.java
このコマンドにより、PL/SQLパッケージjdbcversion
をoracle.sqlj.checker.JdbcVersion
のラッパーとして宣言するjdbcversion.sql
が生成されます。このパッケージは次のように宣言されます。
CREATE OR REPLACE PACKAGE jdbcversion AS FUNCTION toString0 RETURN VARCHAR2; FUNCTION getDriverMajorVersion RETURN NUMBER; FUNCTION getDriverMinorVersion RETURN NUMBER; FUNCTION getDriverName RETURN VARCHAR2; FUNCTION getDriverVersion RETURN VARCHAR2; FUNCTION getJdbcLibraryName RETURN VARCHAR2; FUNCTION getRecommendedRuntimeZip RETURN VARCHAR2; FUNCTION getRuntimeVersion RETURN VARCHAR2; FUNCTION getSqljLibraryName RETURN VARCHAR2; FUNCTION hasNewStatementCache RETURN NUMBER; FUNCTION hasOracleContextIsNew RETURN NUMBER; FUNCTION hasOracleSavepoint RETURN NUMBER; PROCEDURE main0(arg0 JPUBTBL_VARCHAR2); FUNCTION to_string RETURN VARCHAR2; END jdbcversion;
Javaストアド・プロシージャの制限により、メソッドtoString()
およびmain()
がtoString0()
およびmain0()
に変更されることに注意してください。
jdbcversion
パッケージ内のPL/SQLストアド・プロシージャは、次のように実行できます。
SQL> SELECT jdbcversion.toString0 FROM DUAL; TOSTRING0 -------------------------------------------------------------------------------- Oracle JDBC driver version 10.2 (10.2.0.0.0) SQLJ runtime: Oracle 9.2.0 for JDBC SERVER/JDK 1.2.x - Built on Oct 10, 2004
-dbjava
コマンドは、静的メソッドとインスタンス・メソッドの両方を公開します。静的メソッドのみを公開する場合は、次の設定を使用します。
-proxyopts=static
サーバー・サイド・クラスに空のpublic
コンストラクタがある場合は、そのインスタンス・メソッドを公開できます。インスタンス・メソッドをコールする方法は2通りあります。サーバー内でデフォルトの単一インスタンスを介する方法と、個別のインスタンスを介する方法です。次のオプションにより、サーバー内でのインスタンス・メソッドのコールに使用される方法が決定されます。
-proxyopts=single|multiple
デフォルト設定は次のとおりです。
-proxyopts=single
前述のSQL文は、単一インスタンスを使用してtoString0()
メソッドをコールしています。
次のように、oracle.sqlj.checker.JdbcVersion
は、-proxyopts=multiple
を使用して公開できます。
% jpub -user=scott -dbjava=oracle.sqlj.checker.JdbcVersion -plsqlfile=jdbcversion.sql -plsqlpackage=jdbcversion
-proxyopts=multiple
Enter scott password: password
このコマンドはPL/SQLパッケージjdbcversion
を生成しますが、次のメソッドでは前述の例とは異なるメソッドを使用します。
CREATE OR REPLACE PACKAGE jdbcversion AS FUNCTION toString0(handleJdbcVersion NUMBER) RETURN VARCHAR2; ... FUNCTION newJdbcVersion RETURN NUMBER; END jdbcversion;
Oracleデータベース10.2以降では、追加のメソッドnewJdbcVersion()
が作成されます。このメソッドを使用してインスタンスを作成し、そのインスタンスを使用してtoString0()
メソッドをコールできます。SQL*Plusで次のスクリプトを実行します。
set serveroutput on DECLARE text varchar2(1000); inst number; BEGIN inst := jdbcversion.newJdbcVersion; text := jdbcversion.toString0(inst); dbms_output.put_line(text); END; /
このスクリプトにより、次が戻されます。
Oracle JDBC driver version 10.2 (10.2.0.0.0) SQLJ runtime: Oracle 9.2.0 for JDBC SERVER/JDK 1.2.x - Built on Oct 10, 2004 PL/SQL procedure successfully completed.
次のパラメータ型と戻り型がサポートされます。
JDBCでサポートされている型
JavaBeans
サポートされている型の配列
JavaBeansはJavaストアド・プロシージャ・レイヤーでは汎用JDBC struct
クラスのoracle.sql.STRUCT
にマップされ、PL/SQLレイヤーではSQLオブジェクト型とSQL表の型にマップされます。次のオプションにより、配列パラメータの処理方法が決定されます。
-proxyopts=arrayin|arrayout|arrayinout|arrayall
デフォルト設定は次のとおりです。
-proxyopts=arrayin
-proxyopts=arrayall
を指定すると、配列パラメータを含むメソッドは3つのPL/SQLメソッドにマップされます。たとえば、foo(int[])
メソッドについて考えてみます。このメソッドは、次のメソッドにマップされます。
PROCEDURE foo(n NUMBERTBL); PRECEDURE foo_o(n IN NUMBER); PROCEDURE foo_io(n IN OUT NUMBER);
最初のメソッドでは配列引数を入力として扱い、2番目のメソッドでは配列を出力値のホルダーとして扱い、3番目のメソッドでは配列を出力値と入力値の両方用のホルダーとして扱います。-proxyopts=arrayin
(デフォルト設定)の場合、foo(int[])
メソッドは最初のメソッドにマップされます。-proxyopts=arrayout
の場合、foo(int[])
メソッドは2番目のメソッドにマップされます。-proxyopts=arrayinout
の場合、foo(int[])
メソッドは3番目のメソッドにマップされます。
ここで、2つのクラスを使用する複雑な例について考えてみます。Add
クラスでは、メソッド内でTotal
および配列を使用します。Total
はJavaBeansで、このため、サーバー・サイド・クラスの公開によりサポートされています。2つのクラスは次のように定義されています。
public class Add { public static int[] add(int[] i, int[] j) { for (int k=0; k<i.length; k++) i[k] = i[k] + j[k]; return i; } public int add(Total arg) { total = total + arg.getTotal(); return total; } private int total; }
public class Total { public void setTotal(int total) { this.total = total; } public int getTotal() { return total; } private int total; }
2つのクラスを次のようにデータベースにロードします。
% loadjava -u scott -r -v -f Add.java Total.java
Password: password
次のコマンドを使用してJPublisherを実行します。
% jpub -user=scott -dbjava=Add -proxyopts=arrayall
このコマンドにより、次の出力が生成されます。
AddJPub.java plsql_wrapper.sql plsql_dropper.sql Executing plsql_dropper.sql Executing plsql_wrapper.sql Loading AddJPub.java
生成されたPL/SQLラッパーplsql_wrapper.sql
には、次の宣言が含まれます。
CREATE OR REPLACE TYPE JPUBOBJ_Total AS OBJECT (total_ NUMBER); CREATE OR REPLACE TYPE JPUBTBL_NUMBER AS TABLE OF NUMBER; CREATE OR REPLACE PACKAGE JPUB_PLSQL_WRAPPER AS FUNCTION add(arg0 JPUBOBJ_Total) RETURN NUMBER; FUNCTION add_io(arg0 JPUBOBJ_Total) RETURN NUMBER; FUNCTION add(arg0 JPUBTBL_NUMBER,arg1 JPUBTBL_NUMBER) RETURN JPUBTBL_NUMBER; FUNCTION add_o(arg0 OUT NUMBER,arg1 OUT NUMBER) RETURN JPUBTBL_NUMBER; FUNCTION add_io(arg0 IN OUT NUMBER,arg1 IN OUT NUMBER) RETURN JPUBTBL_NUMBER; END JPUB_PLSQL_WRAPPER;
次のSQLスクリプトをSQL*Plusで実行するときに、生成されたPL/SQLラッパーが使用されます。
SQL> set serveroutput on SQL> DECLARE totalx JPUBOBJ_Total; n NUMBER; n1 NUMBER; n2 NUMBER; add1 JPUBTBL_NUMBER; add2 JPUBTBL_NUMBER; add3 JPUBTBL_NUMBER; BEGIN totalx := JPUBOBJ_Total(2004); n := JPUB_PLSQL_WRAPPER.add(totalx); n := JPUB_PLSQL_WRAPPER.add(totalx); DBMS_OUTPUT.PUT('total '); DBMS_OUTPUT.PUT_LINE(n); add1 := JPUBTBL_NUMBER(10, 20); add2 := JPUBTBL_NUMBER(100, 200); add3 := JPUB_PLSQL_WRAPPER.add(add1, add2); DBMS_OUTPUT.PUT('add '); DBMS_OUTPUT.PUT(add3(1)); DBMS_OUTPUT.PUT(' '); DBMS_OUTPUT.PUT_LINE(add3(2)); n1 := 99; n2 := 199; add3 := JPUB_PLSQL_WRAPPER.add_io(n1, n2); DBMS_OUTPUT.PUT('add_io '); DBMS_OUTPUT.PUT_LINE(n1); END; /
このスクリプトにより、次の出力が生成されます。
total 4008 add 110 220 add_io 298 PL/SQL procedure successfully completed.
-dbjava
オプションでは、公開対象のクラスがすでにデータベースに存在することが前提です。かわりに-proxyclasses
オプションを使用することもできます。この場合は、公開対象のクラスがCLASSPATHに指定されている必要があります。Add.java
とTotal.java
をコンパイルし、Add
とTotal
をCLASSPATHに含めます。-dbjava
オプションのかわりに、次のコマンドを使用してAdd
を公開できます。
% jpub -proxyclasses=Add
このコマンドにより、次の出力が生成されます。
AddJPub.java plsql_wrapper.sql plsql_dropper.sql Executing plsql_dropper.sql Executing plsql_wrapper.sql
-proxyclasses
オプションにより、生成されたPL/SQLラッパーがロードされます。ただし、生成されたJavaストアド・プロシージャAddJPub.java
はロードされません。これは、このプロシージャでは、公開済のクラスがサーバー上に存在することが必要になるためです。Javaストアド・プロシージャは公開済のクラスとともにロードする必要があります。
たとえば、UNIXシステムの場合は、次のコマンドを使用すると、Add.java
、Total.java
およびAddJPub.java
をロードできます。
% loadjava -u scott -r -v -f Add.java Total.java AddJPub.java
Password: password
Add.java
、Total.java
およびAddJPub.java
がロードされたら、PL/SQLラッパーを使用できます。
PL/SQLへのJavaの公開に使用するメカニズム
JPublisherでは、PL/SQLラッパー(PL/SQLコール仕様とも呼ばれる)を生成することで、サーバー・サイドJavaクラスへの容易なアクセスをサポートしています。PL/SQLラッパーは、1つ以上の指定のJavaクラスのメソッドをコールできるPL/SQLパッケージです。
関連項目: PL/SQLラッパーの詳細は、『Oracle Database Java開発者ガイド』を参照してください。 |
PL/SQLでは、静的メソッドのみがサポートされます。静的メソッドのみを含むJavaクラスまたは静的メソッドのみを公開するJavaクラスは、簡単にラップできます。ただし、公開対象のインスタンス・メソッドを含むJavaクラスの場合は、インスタンス・メソッドをPL/SQL用の静的メソッドとして公開するために中間的なラッパー・クラスが必要です。
また、ラップするJavaクラスのメソッド・コール順序にJavaプリミティブ型以外が使用されている場合も、ラッパー・クラスが必要になります。
ラップするクラスのインスタンス・メソッドの場合、JPublisherではラッパー・クラスに次の一方または両方のメカニズムを使用できます。
ラップされる各クラスをシングルトンとして扱うことができます。これは、1つのデフォルト・インスタンスが使用されることを意味します。このインスタンスは、メソッドが初めてコールされるときに作成され、以降のメソッド・コールごとに再利用されます。ハンドルは不要であり、使用されません。このメカニズムをシングルトン・メカニズムと呼び、これは、JPublisherがWebサービスのクライアント・プロキシ・クラスのラッパー・クラスを提供する場合のデフォルト動作です。
デフォルト・インスタンスへの参照を削除してガベージを収集できるように、release
XXX
()
メソッドが用意されています。
ラップされるクラスのインスタンスは、ハンドル(ID番号とも呼ばれる)を介して識別できます。JPublisherでは、ハンドルとしてlong
型の数値を使用し、ラッパー・クラス内に静的メソッドを作成します。これらのメソッドのメソッド・シグネチャは、メソッドの起動対象であるインスタンスのハンドルを含めるように変更されます。これにより、PL/SQLラッパーではラップされたクラスのインスタンスにアクセスするときにハンドルを使用できます。この使用例では、ラップされる各クラスのインスタンスを作成してハンドルを取得する必要があります。その後は、以降のインスタンス・メソッドのコールごとにハンドルを提供します。このメカニズムをハンドル・メカニズムと呼びます。
指定したハンドルに従って個々のインスタンスを解放できるように、release
XXX
(long)
メソッドが用意されています。既存のインスタンスをすべて解放する場合に備えて、releaseAll
XXX
()
メソッドも用意されています。
-dbjava
オプションにより、生成されたPL/SQLラッパーからテーブル・ファンクションを生成できます。テーブル・ファンクションを使用するのは、ストアド・ファンクションの戻り値やストアド・プロシージャの出力値ではなく、データベース表を介してデータを公開する場合です。テーブル・ファンクションはデータベース表を戻します。
関連項目: テーブル・ファンクションの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。 |
特定のメソッド用に生成されるテーブル・ファンクションは、次の条件を満たしている必要があります。
インスタンス・メソッドをラップする場合は、シングルトン・メカニズムを有効化する必要があります。これは、-dbjava
および-proxyclasses
のデフォルト設定です。
ラップされるWebサービス・メソッドは、OUT
引数を取るストアド・プロシージャ、またはストアド・ファンクションに対応する必要があります。
JPublisherで-proxyopts=tabfun
設定を-dbjava
または-proxyclasses
オプションと併用すると、生成されたPL/SQLラッパー内の各PL/SQLファンクションにテーブル・ファンクションを1つずつ作成するように要求されます。前述のAdd
クラスの例を考えてみます。次のコマンドを実行します。
% jpub -user=scott -dbjava=Add -proxyopts=arrayall,tabfun
Enter scott password: password
このコマンドにより、次の出力が生成されます。
AddJPub.java plsql_wrapper.sql plsql_dropper.sql Executing plsql_dropper.sql Executing plsql_wrapper.sql Loading AddJPub.java
このコマンドは、前述の例で生成されたPL/SQLメソッドに加えて次のテーブル・ファンクションを生成します。
CREATE OR REPLACE PACKAGE JPUB_PLSQL_WRAPPER AS FUNCTION add(arg0 JPUBOBJ_Total) RETURN NUMBER; FUNCTION TO_TABLE_add(cur SYS_REFCURSOR) RETURN GRAPH_TAB_add_JPUBOBJ_Total PIPELINED; FUNCTION add(arg0 JPUBTBL_NUMBER,arg1 JPUBTBL_NUMBER) RETURN JPUBTBL_NUMBER; FUNCTION TO_TABLE_add0(cur SYS_REFCURSOR) RETURN GRAPH_TAB_add_JPUBTBL_NUMBER PIPELINED; FUNCTION add_o(arg0 OUT NUMBER, arg1 OUT NUMBER) RETURN JPUBTBL_NUMBER; FUNCTION TO_TABLE_add_o(cur SYS_REFCURSOR) RETURN GRAPH_TAB_add_o_JPUBTBL_NUMBER PIPELINED; FUNCTION add_io(arg0 IN OUT NUMBER, arg1 IN OUT NUMBER) RETURN JPUBTBL_NUMBER; FUNCTION TO_TABLE_add_io(cur SYS_REFCURSOR) RETURN GRAPH_TAB_add_io_JPUBTBL_NUMB PIPELINED; END JPUB_PLSQL_WRAPPER; /
テーブル・ファンクションでは、グラフという用語が使用されます。この使用方法の場合、グラフとは、テーブル・ファンクションから戻されるデータベース表のスキーマを定義するSQLオブジェクトです。機能には、グラフ・オブジェクト、グラフ・オブジェクトの表、グラフ・オブジェクトの表を戻すテーブル・ファンクションという3つのレベルがあります。グラフ・オブジェクトの表には、ファンクションへの入力と、そのファンクションからの出力が含まれます。
例として、plsql_wrapper.sql
の次の宣言を考えてみます。ここでは、GRAPH_add_io_JPUBTBL_NUMBER_J
グラフ・オブジェクトとグラフ・オブジェクトのGRAPH_TAB_add_io_JPUBTBL_NUMB
表が定義されています。次の2つの型がTO_TABLE_add_io
テーブル・ファンクション用に生成されます。
CREATE OR REPLACE TYPE GRAPH_add_io_JPUBTBL_NUMBER_J AS OBJECT(arg0 NUMBER, arg1 NUMBER, arg0_out NUMBER, arg1_out NUMBER, res JPUBTBL_NUMBER); / CREATE OR REPLACE TYPE GRAPH_TAB_add_io_JPUBTBL_NUMB AS TABLE OF GRAPH_add_io_JPUBTBL_NUMBER_J; /
テーブル・ファンクションが常にREF CURSOR
を入力として取ることにも注意してください。TO_TABLE_add_io
テーブル・ファンクションでは、REF CURSOR
によりarg0
およびarg1
という2つの引数が想定されます。このテーブル・ファンクションは、GRAPH_TAB_add_io_JPUBTBL_NUMB
のインスタンスを戻します。
次のSQLスクリプトを実行します。
SQL> CREATE TABLE tabfun_input(arg0 NUMBER, arg1 NUMBER); SQL> BEGIN INSERT INTO tabfun_input VALUES(97, 106); INSERT INTO tabfun_input VALUES(67, 3); INSERT INTO tabfun_input VALUES(19, 23); INSERT INTO tabfun_input VALUES(98, 271); INSERT INTO tabfun_input VALUES(83, 281); END; /
SQL> SELECT * FROM TABLE(JPUB_PLSQL_WRAPPER.TO_TABLE_add_io(CURSOR(SELECT * FROM tabfun_input)));
この問合せはTO_TABLE_add_io
をコールし、このテーブル・ファンクションの入力と出力が表示されます。
ARG0 ARG1 ARG0_OUT ARG1_OUT RES -------- -------- -------- -------- ------------------------- 97 106 203 106 JPUBTBL_NUMBER(203) 67 3 70 3 JPUBTBL_NUMBER(70) 19 23 42 23 JPUBTBL_NUMBER(42) 98 271 369 271 JPUBTBL_NUMBER(369) 83 281 364 281 JPUBTBL_NUMBER(364)
JPublisherでは、Webサービス定義言語(WSDL)ファイルをPL/SQLパッケージに公開して、データベース・ユーザーがPL/SQLからWebサービスをコールできるようにします。この機能はWebサービスのコールアウトと呼ばれます。JPublisherでは、WSDLファイルを使用してJavaベースのWebサービス・クライアント・プロキシを生成し、さらにクライアント・プロキシ用のPL/SQLラッパーを生成します。クライアント・プロキシは、JPublisherで起動されるOracle Database Webサービス・アセンブラ・ツールにより生成されます。このツールを起動する前に、次のものがデータベースに存在する必要があります。
JPublisherにより生成されたクライアント・プロキシ
JPublisherにより生成されたPL/SQLラッパー
JPublisherにより生成されたJavaストアド・プロシージャ・ラッパー
Java API for XML-based Remote Procedure Call(JAX-RPC)のWebサービス・クライアント・ランタイム、またはOracle Simple Object Access Protocol(SOAP)のWebサービス・クライアント・ランタイム
以上のコンポーネントは、JPublisherで自動的にロードすることも、ユーザーが手動でロードすることもできます。実行時に、Webサービスのコールアウトは次のように機能します。
ユーザーがPL/SQLラッパーをコールします。次いでPL/SQLラッパーがJavaストアド・プロシージャ・ラッパーをコールします。
Javaストアド・プロシージャ・ラッパーがクライアント・プロキシをコールします。
クライアント・プロキシがWebサービス・クライアント・ランタイムを使用してWebサービスをコールします。
PL/SQLでサポートされるのは静的メソッドのみのため、クライアント・プロキシ・クラスのインスタンス・メソッドを静的メソッドとして公開するには、中間レイヤーとしてJavaストアド・プロシージャ・ラッパーが必要です。
Webサービスのコールアウトには次のJARファイルが必要です。これらのファイルは、データベースWebサービスのコールアウト・ユーティリティの10gリリース2に組み込まれています。
dbwsa.jar
dbwsclientws.jar
dbwsclientdb101.jar
dbwsclientdb102.jar
これらのファイルは次のWebサイトからダウンロードできます。
http://www.oracle.com/technetwork/database/database-083829.html
すべてのJARファイルをORACLE_HOME
/sqlj/lib
にコピーする必要があります。dbwsa.jar
ファイルは、JPublisherがWSDLファイルを公開するときにCLASSPATHに含まれている必要があります。UNIXシステムでは、jpub
コマンドライン・スクリプトにORACLE_HOME
/sqlj/lib/dbwsa.jar
が含まれています。したがって、このファイルをCLASSPATHに含める必要はありません。
dbwsclientws.jar
ファイルには、Oracleデータベースのバージョンに関係なく、Webサービス・クライアント・クラスが含まれます。dbwsclientdb101.jar
ファイルには、Oracleデータベース10.1リリースに固有のWebサービス・クライアント・クラスが含まれます。dbwsclientdb102.jar
ファイルには、Oracleデータベース10.2またはOracleデータベース11gに固有のWebサービス・クライアント・クラスが含まれます。
Oracle9i Databaseでサポートされているのは、Oracle SOAP Webサービス・クライアントのみです。Oracle9iリリース2(9.2)より前のデータベースにOracle SOAP Webサービス・クライアント・ランタイムをロードするには、次のコマンドを実行します。
% loadjava -u sys -r -v -s -f -grant public \
${J2EE_HOME}/lib/activation.jar \
${J2EE_HOME}/lib/http_client.jar \
${ORACLE_HOME}/lib/xmlparserv2.jar \
${ORACLE_HOME}/soap/lib/soap.jar \
${J2EE_HOME}/lib/mail.jar
Password: password
このコマンドはUNIXシステムの形式です。それでも、Microsoft Windowsユーザーは、Oracle SOAP Webサービス・クライアントに必要なJARファイルを把握することができます。関連のJARファイルは、Oracle9i Application Serverの各リリースに同梱されています。
Oracle Database 9.2にOracle SOAP Webサービス・クライアントをロードするには、次のコマンドを実行します。
% loadjava -u scott -r -v -f -genmissing
${ORACLE_HOME}/j2ee/home/lib/jssl-1_2.jar
${ORACLE_HOME}/soap/lib/soap.jar
${ORACLE_HOME}/dms/lib/dms.jar
${ORACLE_HOME}/j2ee/home/lib/servlet.jar
${ORACLE_HOME}/j2ee/home/lib/ejb.jar
${ORACLE_HOME}/j2ee/home/lib/mail.jar
Password: password
Oracle Database 10gにOracle SOAP Webサービス・クライアントをロードするには、次のコマンドを実行します。
% loadjava -u scott -r -f -v -genmissing
${ORACLE_HOME}/soap/lib/soap.jar
${ORACLE_HOME}/lib/dms.jar
${ORACLE_HOME}/jlib/javax-ssl-1_1.jar
${ORACLE_HOME}/j2ee/home/lib/servlet.jar
${ORACLE_HOME}/j2ee/home/lib/mail.jar
${ORACLE_HOME}/j2ee/home/lib/activation.jar
${ORACLE_HOME}/j2ee/home/lib/http_client.jar
${ORACLE_HOME}/j2ee/home/lib/ejb.jar
Password: password
注意: ユーザーにGrant Public 権限がある場合、ロードしたクラスを他のスキーマから見えるようにするには、前述のコマンドに-grant public を追加します。 |
Oracle 10gリリース1データベースにOracle JAX-RPCクライアントをロードするには、次の2つのオプションのいずれかを使用します。
次のコマンドを使用して、SYS
スキーマにWebサービス・クライアントをロードします。
% loadjava -u sys -r -v -f -genmissing -s -grant public dbwsclientws.jar dbwsclientdb101.jar
Password: password
次のコマンドを使用して、ユーザー・スキーマにWebサービス・クライアントをロードします。
% loadjava -u scott -r -v -f -genmissing dbwsclientws.jar dbwsclientdb101.jar
Password: password
Oracle 10gリリース2データベースまたはOracle Database 11gにOracle JAX-RPCクライアントをロードするには、次の2つのオプションのいずれかを使用します。
次のコマンドを使用して、SYS
スキーマにWebサービス・クライアントをロードします。
% loadjava -u sys -r -v -f -genmissing -s -grant public dbwsclientws.jar dbwsclientdb102.jar
Password: password
次のコマンドを使用して、ユーザー・スキーマにWebサービス・クライアントをロードします。
% loadjava -u scott -r -v -f -genmissing dbwsclientws.jar dbwsclientdb102.jar
Password: password
注意: ユーザーにGrant Public 権限がある場合、ロードしたクラスを他のスキーマから見えるようにするには、前述のコマンドに-grant public を追加します。 |
Webサービスのコールアウトでは、JPublisherをJDK 1.4以降で実行する必要があります。次のJPublisherのオプションが、Webサービスのコールアウトに関係します。
-proxywsdl=url -httpproxy=host:port -endpoint=url -proxyopts=soap|jaxrpc|noload|tabfun. Default: -proxyopts=jaxrpc|tabfun. -sysuser=user/password
ここで
-proxywsdl
オプションでは、公開されるWebサービスを記述するWSDLファイルのURLまたはパスを指定します。
-httpproxy
オプションでは、WSDLファイルがファイアウォールの外側にある場合に、このファイルへのアクセスに使用するHTTPプロキシを指定します。
-endpoint
オプションでは、WSDLファイルに指定されているエンドポイントではなく、ここで指定するエンドポイントにクライアントをリダイレクトします。
-proxyopts=soap
設定では、PL/SQLラッパーでWebサービスのコールにOracle SOAP Webサービス・クライアント・ランタイムが使用されることを指定します。
-proxyopts=jaxrpc
設定では、PL/SQLラッパーでWebサービスのコールにOracle JAX-RPC Webサービス・クライアント・ランタイムが使用されることを指定します。
-proxyopts=tabfun
設定では、該当するWebサービス操作に対してテーブル・ファンクションが生成されることを指定します。
-proxywsdl
には-sysuser
設定をお薦めします。これは、SYS
権限を持つデータベース・ユーザーを指定します。-sysuser
設定を使用すると、JPublisherにより、生成されたPL/SQLラッパーを実行できる適切なアクセス権限が割り当てられます。また、-sysuser
設定を使用すると、Webサービス・クライアント・ランタイムがデータベースにない場合に、JPublisherでランタイムをロードすることもできます。
たとえば、HelloService
というJAX-RPC Webサービスが次のエンドポイントにデプロイされているとします。
http://localhost:8888/javacallout/javacallout
このWebサービス用のWSDLドキュメントは次の位置にあるとします。
http://localhost:8888/javacallout/javacallout?WSDL
Webサービスは、システム・プロパティ名を指定するJava文字列を取って、そのプロパティの値を戻すgetProperty
操作を提供します。たとえば、getProperty("os.name")
はSunOS
を戻します。
JPublisherでは、WebサービスのWSDL記述に基づいて、Webサービス・クライアント・プロキシの生成を指示し、クライアント・プロキシ用のJavaおよびPL/SQLラッパーを生成できます。これらの機能を実行するには、次のコマンドを使用します。
% jpub -user=scott -sysuser=sys/sys_password -url=jdbc:oracle:thin:@localhost:1521:orcl -proxywsdl=http://localhost:8888/javacallout/javacallout?WSDL -package=javacallout -dir=genproxy Enter scott password: password
このコマンドにより、次の出力が生成されます。
genproxy/HelloServiceJPub.java genproxy/plsql_wrapper.sql genproxy/plsql_dropper.sql genproxy/plsql_grant.sql genproxy/plsql_revoke.sql Executing genproxy/plsql_wrapper.sql Executing genproxy/plsql_grant.sql Loading genproxy/plsql_proxy.jar
-proxyopts
設定を使用すると、JAX-RPCクライアント・プロキシおよびラッパーが生成され、テーブル・ファンクションを使用してWebサービス操作がラップされます。-url
設定はデータベースを示し、-user
設定はスキーマを示し、ここに、JPublisherは生成されたJavaラッパーとPL/SQLラッパーをロードします。-sysuser
設定では、ラッパー・スクリプトの実行パーミッションを付与する権限を持つSYS
アカウントを指定します。
plsql_grant.sql
およびplsql_revoke.sql
スクリプトはJPublisherで生成されます。この2つのスクリプトを使用して、データベース・スキーマ内にPL/SQLラッパーを作成し、これを実行するためのパーミッションを付与し、そのパーミッションを取り消し、データベース・スキーマからPL/SQLラッパーを削除します。
WSDLファイルの内容は次のとおりです。
<?xml version="1.0" encoding="UTF-8"?> <definitions name="HelloService" targetNamespace="http://oracle.j2ee.ws/javacallout/Hello" xmlns:tns="http://oracle.j2ee.ws/javacallout/Hello" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"> <types/> <message name="HelloServiceInf_getProperty"> <part name="String_1" type="xsd:string"/> </message> <message name="HelloServiceInf_getPropertyResponse"> <part name="result" type="xsd:string"/> </message> <portType name="HelloServiceInf"> <operation name="getProperty" parameterOrder="String_1"> <input message="tns:HelloServiceInf_getProperty"/> <output message="tns:HelloServiceInf_getPropertyResponse"/> </operation> </portType> <binding name="HelloServiceInfBinding" type="tns:HelloServiceInf"> <operation name="getProperty"> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://oracle.j2ee.ws/javacallout/Hello"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" use="encoded" namespace="http://oracle.j2ee.ws/javacallout/Hello"/> </output> <soap:operation soapAction=""/> </operation> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/> </binding> <service name="HelloService"> <port name="HelloServiceInfPort" binding="tns:HelloServiceInfBinding"> <soap:address location="/javacallout"/> </port> </service> </definitions>
<message>
要素内のHelloServiceInf
はサービスBean名であり、生成されたJAX-RPCクライアント・プロキシ・スタブ・クラスにより生成および実装されるインタフェースの名前を決定します。HelloServiceInf
インタフェースのシグネチャは次のとおりです。
public interface HelloServiceInf extends java.rmi.Remote { public String getProperty(String prop) throws java.rmi.RemoteException; }
getProperty()
メソッドは、WSDLドキュメントに指定されたgetProperty
操作に対応します。指定されたシステム・プロパティprop
の値を戻します。たとえば、オペレーティング・システムのバージョンを戻すにはos.version
プロパティを指定します。
plsql_wrapper.sql
ファイルは、PL/SQLラッパー・パッケージJPUB_PLSQL_WRAPPER
を定義します。このパッケージは、PL/SQLからWebサービスをコールするために作成されます。これには、Webサービス操作getProperty
からのテーブル・ファンクションの定義が含まれます。plsql_wrapper.sql
ファイル内のスクリプトは次のとおりです。
CREATE OR REPLACE TYPE GRAPH_getProperty AS OBJECT( p0 VARCHAR2(32767), res VARCHAR2(32767) ); / CREATE OR REPLACE TYPE GRAPH_TAB_getProperty AS TABLE OF GRAPH_getProperty; / -- PL/SQL procedures that invoke webserviecs CREATE OR REPLACE PACKAGE JPUB_PLSQL_WRAPPER AS FUNCTION getProperty(p0 VARCHAR2) RETURN VARCHAR2; FUNCTION TO_TABLE_getProperty(cur SYS_REFCURSOR) RETURN GRAPH_TAB_getProperty PIPELINED; END JPUB_PLSQL_WRAPPER; /
JPublisherコマンドラインにはこのWebサービスを公開するために-user
および-sysuser
設定が指定されているため、生成されたJavaコードとPL/SQLラッパーがデータベースにロードされます。すべてがロードされた後、PL/SQLラッパーを使用してWebサービスを起動できます。
PL/SQLラッパーは、getProperty
およびTO_TABLE_getProperty
という2つのファンクションで構成されています。getProperty
ファンクションは、生成されたクライアント・プロキシ・クラス内でgetProperty()
メソッドを直接ラップします。たとえば、次のSQL*Plusコマンドでは、getProperty
を使用してWebサービスを実行中のオペレーティング・システムを判別します。
SQL> SELECT JPUB_PLSQL_WRAPPER.getProperty('os.name') FROM DUAL; JPUB_PLSQL_WRAPPER.GETPROPERTY('OS.NAME') ----------------------------------------- SunOS
TO_TABLE_getProperty
は、getProperty
ファンクションに基づくテーブル・ファンクションです。これは入力としてREF CURSOR
を取り、表を戻します。戻される表のスキーマは、GRAPH_getProperty
により定義されます。この例で、TO_TABLE_getProperty
はVARCHAR2
データを含む1列のみの表から取得されたREF CURSOR
を使用してコールされます。各データ項目はシステム・プロパティ名(os.version
など)です。TO_TABLE_getProperty
は、各行に入力のREF CURSOR
からの項目と、その項目を入力として取るgetProperty
コールの結果が含まれる表を戻します。次のコードは、TO_TABLE_getProperty
の使用例です。
SQL> -- Test Table Function SQL> CREATE TABLE props (name VARCHAR2(50)); SQL> BEGIN INSERT INTO props VALUES('os.version'); INSERT INTO props VALUES('java.version'); INSERT INTO props VALUES('file.separator'); INSERT INTO props VALUES('file.encoding.pkg'); INSERT INTO props VALUES('java.vm.info'); END; / SQL> SELECT * FROM TABLE(JPUB_PLSQL_WRAPPER.TO_TABLE_getProperty(CURSOR(SELECT * FROM props))); P0 RES ------------------------------ os.version 5.8 java.version 1.4.1_03 file.separator / file.encoding.pkg sun.io java.vm.info mixed mode
この例では、VARCHAR2
の1列のみの表が作成されてシステム・プロパティ名が移入され、TO_TABLE_getProperty
を使用してこれらのシステム・プロパティの値が検出されます。この例では、オペレーティング・システムがSun社のSolaris 5.8であることがわかります。