ヘッダーをスキップ
Oracle® Database JPublisherユーザーズ・ガイド
11gリリース2 (11.2)
B56279-03
  目次へ
目次
索引へ
索引

前へ
前へ
 
次へ
次へ
 

2 JPublisherの使用方法

この章では、JPublisherを使用して次のことを行う方法を説明します。

ユーザー定義のSQL型の公開

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_TYPCOMPOSITE_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

次のことに注意してください。


注意:

Oracle Database 10gより前のバージョンでは、SQLJクラスの生成により参照可能な.sqljソース・ファイルが作成されます。Oracle Database 10g以降では、JPublisherの-compatibleフラグを値8iboth8i9iまたはsqljに設定すると、参照可能な.sqljソース・ファイルが生成されます。

この4つのモードのいずれでも、sqljコマンドライン・ユーティリティを使用するかわりに、JPublisherの-sqljオプションを使用して.sqljファイルを変換できます。


JPublisherによって生成されたコードで必要な機能や動作が得られない場合は、生成されたラッパー・クラスを拡張することで、その機能をオーバーライドしたり補完できます。次の例を考えます。

% jpub -u OE -s WAREHOUSE_TYP:JPubWarehouse:MyWarehouse
Enter OE password: password

JPublisherの出力は次のとおりです。

OE.WAREHOUSE_TYP

このコマンドを実行すると、JPublisherではJPubWarehouse.javaMyWarehouse.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_warehouseIN 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);

PL/SQLパッケージの公開

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のトップ・レベルの有効範囲にストアド・ファンクションまたはストアド・プロシージャがないと、警告が表示されます。

Oracle Streams AQの公開

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に対して使用できます。

Javaクラスとしてのキューの公開

キューの公開には、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属性としてspidertankを含むメッセージが選択されます。

QueueMessageORADataのサブクラスで、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();
}
...

Javaクラスとしてのトピックの公開

次のように宣言されているトピックについて考えてみます。

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つの受信者(ToyPartyToyFactory)にメッセージを送信し、トピックをそれぞれToyPartyToyLandおよび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");
...

Javaクラスとしてのストリームの公開

ストリームは特殊な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());
...

このコード例は、StringIntegerおよびjava.util.Mapなどの様々な型のメッセージを送信します。JDBCカスタム型QueueMessageに関して、addTypeMap()メソッドがコールされて、SQL型からJavaへの型マッピングが指定されます。

ネイティブJavaインタフェースを介したサーバー・サイドJavaクラスの公開

Oracle Database 10gより前のリリースでは、データベース・クライアントからJavaストアド・プロシージャおよびファンクションをコールするには、関連するPL/SQLラッパーへのJDBCコールが必要でした。各PL/SQLラッパーは、SQLシグネチャとJava実装を使用して手動で公開する必要がありました。このプロセスには、次のデメリットがありました。

Oracle Database 10gから、サーバー・サイドJavaコードをコールするためのネイティブJavaインタフェース機能を使用できます。JPublisherの-javaオプションは、これらのデメリットを克服する機能を提供します。

関連するPL/SQLラッパーへのJDBCコールの問題を解消するために、-javaオプションを指定することで、Javaの静的メソッドを直接コールするAPIを使用できます。この機能はWebサービスにも有効です。

-javaオプションの機能は、-sqlオプションの機能をミラー化したもので、サーバー・サイドJavaクラスにアクセスするクライアント・サイドJavaスタブ・クラスを作成します。これは、サーバー・サイドSQLオブジェクトまたはPL/SQLパッケージにアクセスするクライアント・サイドJavaクラスの作成とは対照的です。クライアント・サイド・スタブ・クラスでは、サーバー・サイド・クラスをミラー化するSQLコードが使用され、次の機能が含まれます。

実行時には、スタブ・クラスが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.JdbcVersionJdbcVersionClientを比較してみます。クライアント・サイド・コードでは、すべての静的メソッドがインスタンス・メソッドにマップされます。サーバー・サイド・クラスのインスタンス・メソッド(たとえば、toString())は、追加のハンドルを使用してメソッドにマップされます。ハンドルは、サーバーでのoracle.sqlj.checker.JdbcVersionのインスタンスを表します。ハンドルは、サーバー・サイドのインスタンス・メソッドのコールに使用されます。JdbcVersionClientの追加メソッドはnewInstance()で、これはサーバー内にoracle.sqlj.checker.JdbcVersionの新規インスタンスを作成し、そのハンドルを戻します。

サーバー・サイドJavaクラスの公開には、次のような制約があります。

PL/SQLラッパーを介したサーバー・サイドJavaクラスの公開

Oracle Database 10gリリース2 (10.2)以降では、JPublisherはサーバー・サイドJavaクラスの公開に対して新しいアプローチを提供します。これは、サーバー・サイドJavaをコールするために次のものを生成します。

Javaストアド・プロシージャはサーバー・サイドJavaコードをラップしますが、これにより、次のことが行われます。

PL/SQLラッパーはJavaストアド・プロシージャをコールします。クライアント・サイドJavaコードは、JDBCコールを介してPL/SQLラッパーをコールします。-javaオプションでは、公開対象のクラスがすでにデータベースにロードされていることが前提です。

サポートされているJava型は次のとおりです。

サーバー・サイド・クラスを公開するには、次のように、-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();
}

JdbcVersionJdbcVersionClientを比較してみます。JPublisherで生成されたコードの制限を示しています。生成されたクライアント・サイドAPIは、元のサーバー・サイドAPIとまったく同じではありません。この制限をわかりやすく示すために、JdbcVersionJdbcVersionClientとの違いをいくつか次にリストします。

-javaと比較すると、-dbjavaの長所は、サポートされる型の数が多いことと、Oracle Database 10gよりも前のバージョンで機能することです。ただし、短所は、実行時にPL/SQLおよびJavaストアド・プロシージャで追加のレイヤーが使用されることと、クライアント・サイドJavaクラスでメソッド・シグネチャが変更される可能性が高くなることです。

PL/SQLへのサーバー・サイド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パッケージjdbcversionoracle.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.

次のパラメータ型と戻り型がサポートされます。

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.javaTotal.javaをコンパイルし、AddTotalを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.javaTotal.javaおよびAddJPub.javaをロードできます。

% loadjava -u scott -r -v -f Add.java Total.java AddJPub.java
Password: password

Add.javaTotal.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ではラッパー・クラスに次の一方または両方のメカニズムを使用できます。

テーブル・ファンクションへのサーバー・サイドJavaクラスの公開

-dbjavaオプションにより、生成されたPL/SQLラッパーからテーブル・ファンクションを生成できます。テーブル・ファンクションを使用するのは、ストアド・ファンクションの戻り値やストアド・プロシージャの出力値ではなく、データベース表を介してデータを公開する場合です。テーブル・ファンクションはデータベース表を戻します。


関連項目:

テーブル・ファンクションの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

特定のメソッド用に生成されるテーブル・ファンクションは、次の条件を満たしている必要があります。

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)

PL/SQLへのWebサービス・クライアントの公開

JPublisherでは、Webサービス定義言語(WSDL)ファイルをPL/SQLパッケージに公開して、データベース・ユーザーがPL/SQLからWebサービスをコールできるようにします。この機能はWebサービスのコールアウトと呼ばれます。JPublisherでは、WSDLファイルを使用してJavaベースのWebサービス・クライアント・プロキシを生成し、さらにクライアント・プロキシ用のPL/SQLラッパーを生成します。クライアント・プロキシは、JPublisherで起動されるOracle Database Webサービス・アセンブラ・ツールにより生成されます。このツールを起動する前に、次のものがデータベースに存在する必要があります。

以上のコンポーネントは、JPublisherで自動的にロードすることも、ユーザーが手動でロードすることもできます。実行時に、Webサービスのコールアウトは次のように機能します。

  1. ユーザーがPL/SQLラッパーをコールします。次いでPL/SQLラッパーがJavaストアド・プロシージャ・ラッパーをコールします。

  2. Javaストアド・プロシージャ・ラッパーがクライアント・プロキシをコールします。

  3. クライアント・プロキシがWebサービス・クライアント・ランタイムを使用してWebサービスをコールします。

PL/SQLでサポートされるのは静的メソッドのみのため、クライアント・プロキシ・クラスのインスタンス・メソッドを静的メソッドとして公開するには、中間レイヤーとしてJavaストアド・プロシージャ・ラッパーが必要です。

Webサービスのコールアウトには次のJARファイルが必要です。これらのファイルは、データベースWebサービスのコールアウト・ユーティリティの10gリリース2に組み込まれています。

これらのファイルは次の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つのオプションのいずれかを使用します。

Oracle 10gリリース2データベースまたはOracle Database 11gにOracle JAX-RPCクライアントをロードするには、次の2つのオプションのいずれかを使用します。


注意:

ユーザーに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

ここで

たとえば、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_getPropertyVARCHAR2データを含む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であることがわかります。