3.1 Javaメソッドの起動
Javaアプリケーションのタイプによって、クライアントがJavaメソッドをコールする方法が決まります。次の項では、Javaメソッドのコールに使用できる各Java Application Programming Interface(API)について説明します。
3.1.1 PL/SQLラッパーの使用
3.1.2 JNIサポートについて
Java Native Interface (JNI)は、Javaネイティブ・メソッドの作成、およびJVMのネイティブ・アプリケーションへの埋込みを行うための標準プログラミング・インタフェースです。JNIの主要目的は、プラットフォーム固有のネイティブ・ライブラリを使用するJavaアプリケーションのバイナリ互換性を提供することです。
ネイティブ・メソッドを使用すると、サーバーのクラッシュ、セキュリティ違反およびデータ破損などの問題が発生する可能性があります。Oracle Databaseでは、JavaアプリケーションにおけるJNIの使用はサポートされていません。JNIを使用する場合、アプリケーションが100パーセントPure Javaではないため、ネイティブ・メソッドをプラットフォーム間で移植する必要があります。
3.1.3 データベースでのSQLJおよびJDBCとJavaの併用について
SQLJおよびJava Database Connectivity(JDBC)APIは、Javaクライアントから使用できます。この2つのAPIにより、指定したユーザー名とパスワードを使用してデータベース上でセッションが確立され、データベースに対するSQL問合せが実行されます。次の表に、APIとその説明を示します。
この項の内容は次のとおりです。
3.1.3.1 JDBCの使用
JDBCは業界標準のAPIであり、SQL文をJavaメソッドの引数として埋め込むことができます。JDBCはX/OpenのSQL Call Level Interface(CLI)に基づき、SQL92標準のエントリ・レベルに準拠しています。オラクル社などの各ベンダーは、標準のjava.sqlパッケージのインタフェースを実装することによって、独自のJDBC実装を作成しています。Oracleには、これらの標準インタフェースを実装する次のJDBCドライバが用意されています。
                        
- 
                              
JDBC Thinドライバ。クライアント側のアプリケーションまたはアプレットに使用でき、Oracleクライアントのインストールを必要としない100パーセントPure Javaのソリューションです。
 - 
                              
JDBC OCIドライバ。クライアント側のアプリケーションに使用するドライバで、Oracleクライアントのインストールが必要です。
 - 
                              
サーバー側JDBCドライバ。Oracle Databaseに埋め込まれています。
 
JDBCを使用する場合は、次のタスクを順番に実行します。
- 接続ハンドルを取得します。
 - 目的のSQL操作に使用する文オブジェクトを作成します。
 - SQL操作にバインドするローカル変数を代入します。
 - 操作を実行します。
 - オプションで結果セットを取得します。
 
多くのアプリケーションではこの方法で十分ですが、複雑な文の場合は作業が煩雑になります。動的なSQL操作の場合は、実行時まで操作内容がわからないため、JDBCを使用する必要があります。ただし、典型的なアプリケーションでは、このSQL操作はあまり採用されていません。
3.1.3.2 SQLJの使用
SQLJには、1回のステップで静的なSQL操作をJavaソース・コードに直接埋め込む業界標準の方法が用意されているため、JDBCのような複数のステップは不要です。Oracle SQLJは、米国規格協会(ANSI)規格X3H2-98-320に準拠しています。
SQLJは、標準のSQLJプログラミング構文をサポートするプリコンパイラであるトランスレータとランタイム・コンポーネントで構成されています。SQLJソース・コードを.sqljファイルに作成した後、トランスレータを使用して処理します。トランスレータでは、SQLJソース・コードが標準Javaソース・コードに変換され、SQL操作がSQLJランタイム・コールに変換されます。Oracle DatabaseのSQLJ実装では、トランスレータがJavaコンパイラをコールして、Javaソース・コードをコンパイルします。SQLJアプリケーションを実行すると、SQLJランタイムがJDBCをコールして、データベースと通信します。
                        
また、SQLJを使用すると、実行時前にSQL文のエラーを捕捉できます。JDBCコードは、Pure Javaであるため、直接コンパイルされます。コンパイラでは、SQLエラーを検出できません。これに対して、SQLJコードの変換時に、トランスレータでは、埋め込まれているSQL文のセマンティクスおよび構文を解析するため、SQLエラーが開発時に捕捉され、アプリケーションの実行時にエンド・ユーザーによってエラーが捕捉されることはありません。
次に、単純なSQLJプログラムの完全な例を示します。
import java.sql.*;
import sqlj.runtime.ref.DefaultContext;
import oracle.sqlj.runtime.Oracle;
#sql iterator MyIter (String first_name, int employee_id, float salary);
public class MyExample
{
  public static void main (String args[]) throws SQLException
  {
    Oracle.connect("jdbc:oracle:thin:@localhost:5521:orcl", "HR", "<password>");
    #sql { INSERT INTO employees (first_name, employee_id, salary) VALUES ('SMITH', 32, 20000) };
    MyIter iter;
    #sql iter={ SELECT first_name, employee_id, salary FROM employees };
    while (iter.next())
    {
      System.out.println(iter.first_name()+" "+iter.employee_id()+" "+iter.salary());
    }
  }
}
この例では、次の手順を実行します。
- 
                              
イテレータを宣言します。
SQLJでは、イテレータと呼ばれる強い型指定のJDBC結果セットが使用されます。イテレータには、特定のデータ型に使用される特定数の列があります。この例のように、イテレータを使用する前に、イテレータの型を定義する必要があります。
#sql ITERATOR MyIter (String first_name, int employee_id, float salary);
この宣言によって、SQLJではイテレータ・クラス、
MyIterが作成されます。MyIter型のイテレータでは、JavaStringにマップされる最初の列の結果、Javaintにマップされる2番目の列の結果、およびJavafloatにマップされる3番目の列の結果を格納できます。この定義では、3つの列にfirst_name、employee_idおよびsalaryという名前が付けられ、データベース内の参照表の列名と照合されます。MyIterは、名前付きイテレータです。 - 
                              
データベースへの接続。
Oracle.connect("jdbc:oracle:thin:@localhost:5521:orcl","HR", "<password>");SQLJには、
Oracleクラスがあり、そのconnect()メソッドを使用して次の重要なタスクが実行されます。- 
                                    
データベースにアクセスするためにSQLJが使用するOracle JDBCドライバ(この場合は、JDBC Thinドライバ)を登録します。
 - 
                                    
指定のURLにある指定したスキーマ(この場合は、指定されたパスワードを持つユーザー
HR)のデータベース接続をオープンします。この場合は、URLはホストlocalhost、ポート5521およびSIDorclを指し示しています。 - 
                                    
この接続をSQLJ文のデフォルト接続として確立します。JDBC文では接続オブジェクトを明示的に指定する必要がありますが、SQLJ文ではデフォルト接続を暗黙的に使用するか、または必要に応じて別の接続を指定できます。
 
 - 
                                    
 - 
                              
SQL文を処理します。次の操作が実行されます。
- 
                                    
行を
employees表に挿入します。#sql {INSERT INTO employees (first_name, employee_id, salary) VALUES ('SMITH', 32, 20000)}; - 
                                    
次のイテレータがインスタンス化および移入されます。
MyIter iter; #sql iter={SELECT first_name, employee_id, salary FROM employees}; 
 - 
                                    
 - 
                              
イテレータ内に移入されたデータにアクセスします。
while (iter.next()) { System.out.println(iter.first_name()+" "+iter.employee_id()+" "+iter.salary()); }next()メソッドはすべてのイテレータに共通で、JDBC結果セットのnext()メソッドと同じ役割を果し、残りの行がある場合はtrueを戻し、次の行のデータに移動します。列名と名前が一致するイテレータのアクセッサ・メソッドをコールすることで、各行のデータにアクセスできます。これは、すべての名前付きイテレータの特性です。この例では、メソッドfirst_name()、employee_id()およびsalary()を使用してデータにアクセスします。 
3.1.3.3 JDBCとSQLJの比較例
次は、単純な操作を実行するJDBCコードとSQLJコードの例です。
JDBC:
// Assume you already have a JDBC Connection object conn
// Define Java variables
String name;
int id=37115;
float salary=20000;
// Set up JDBC prepared statement.
PreparedStatement pstmt = conn.prepareStatement
("SELECT first_name FROM employees WHERE employee_id=? AND salary>?");
pstmt.setInt(1, id);
pstmt.setFloat(2, salary);
// Execute query; retrieve name and assign it to Java variable.
ResultSet rs = pstmt.executeQuery();
while (rs.next())
{
  name=rs.getString(1);
  System.out.println("Name is: " + name);
}
// Close result set and statement objects.
rs.close()
pstmt.close();
JDBC接続connがすでに確立されていると仮定しています。次の手順を実行する必要があります。
                        
- 
                              
Javaの変数
name、idおよびsalaryを定義します。 - 
                              
PreparedStatementインスタンスを作成します。SQL文内の値を動的に設定する必要がある場合はいつでも、プリコンパイルされたSQL文を使用できます。異なる変数値を設定して、同じプリペアド文を繰返し使用できます。プリペアド文中の疑問符(?)は、Java変数用のプレースホルダです。前述の例では、これらの変数には、
pstmt.setInt()およびpstmt.setFloat()メソッドを使用して値が割り当てられます。最初の「?」は、int変数idを参照し、値は37115に設定されます。2番目の「?」は、float変数salaryを参照し、値は20000に設定されます。 - 
                              
問合せを実行し、データを
ResultSetオブジェクトに戻します。 - 
                              
ResultSetオブジェクトから目的のデータを取り出し、表示します。この場合は、first_name列です。この例では1行のみですが、通常結果セットには複数行のデータが含まれます。 
SQLJ:
String name;
int id=37115;
float salary=20000;
#sql {SELECT first_name INTO :name FROM employees WHERE employee_id=:id AND salary>:salary};
System.out.println("Name is: " + name);
SQLJでは、SQL文を直接Javaコードに埋め込めることに加え、バインド式とも呼ばれる、SQL文で直接使用できるJavaホスト式をサポートしています。最も単純なホスト式は、この例のような単純な変数です。ただし、比較的複雑な式も使用できます。ホスト式の前には、コロン(:)を置きます。この例で使用されているJavaホスト式は、name、idおよびsalaryです。SQLJではホスト式をサポートしているため、単一行のデータのみを戻す場合は、結果セットなどは不要です。
                        
ノート:
SQLJ文は宣言も含めすべて、#sqlトークンで始まります。
                           
3.1.3.4 SQLJの強い型指定
SQLJでは、結果セットではなく、イテレータのような強い型指定を使用します。これによって、変換時にSQL命令をデータベースに照合してチェックできます。たとえば、SQLJでは、データベースに接続し、問合せ対象のデータベース表と照合して各自のイテレータをチェックできます。トランスレータでは、これらの一致が確認されるため、変換時にSQLエラーを捕捉でき、確認されない場合、エラーはアプリケーションの実行時まで捕捉されません。さらに、スキーマを後で変更した場合、トランスレータを再実行することによって、変更内容がアプリケーションに影響を与えるかどうかを確認できます。
3.1.3.5 SQLJプログラムの変換
Oracle JDeveloperなどの統合開発環境(IDE)では、作成時に、SQLJプログラムを変換、コンパイルおよびカスタマイズできます。Oracle JDeveloperは、Microsoft社のWindowsに対応したJavaプログラミング用のビジュアル開発環境です。IDEを使用しない場合は、フロントエンドのSQLJユーティリティsqljを使用します。このユーティリティは次のように実行できます。
                        
%sqlj MyExample.sqlj
SQLJトランスレータでは、SQL操作の構文とセマンティクスがチェックされます。オンライン・チェックを使用して、操作をデータベースに照合してチェックできます。これを行う場合は、トランスレータ・オプションの設定に、サンプル・データベース・スキーマを指定する必要があります。スキーマのデータは、プログラムで最終的に実行されるスキーマのデータと同一である必要はありません。ただし、表には対応する名前とデータ型を持つ列が含まれている必要があります。次の例のように、ユーザー・オプションを使用してオンライン・チェックを使用可能にし、スキーマのユーザー名、パスワードおよびURLを指定します。
%sqlj -user=HR@jdbc:oracle:thin:@localhost:5521:orcl MyExample.sqlj
Password: password
3.1.4 コマンドライン・インタフェースの使用について
Oracle JVMへのコマンドライン・インタフェースは、JDKまたはJREのシェル・コマンドの使用方法と似ています。次のことが可能です。
- 
                           
標準の
-classpath構文を使用して、ロードするクラスの検索場所を示します。 - 
                           
標準の
-D構文を使用して、システム・プロパティを設定します。 
このインタフェースは、文字列(VARCHAR2)引数を取り、この引数をコマンドライン入力として解析するPL/SQLファンクションです。形式が適切であれば、Oracle JVMで指定のJavaメソッドを実行します。これを実行するために、PL/SQLのパッケージDBMS_JAVAには次のファンクションが用意されています。
                     
runjava
このファンクションは、唯一の引数としてJavaコマンドラインを取り、Oracle JVMでこれを実行します。戻り値は、正常に実行された場合はNULL、それ以外はエラー・メッセージになります。コマンドラインの形式は、JDKのシェル・コマンドと同じです。次のようになります。
[option switches] name_of_class_to_execute [arg1 arg2 ... argn]
オプションのスイッチ-classpath、-D、-Xbootclasspathおよび-jarを使用できます。このファンクションは、現在のコマンドを実行する前に、以前にJavaを使用したときからセッションに残っているJavaの状態をすべてクリアするという点でrunjava_in_current_sessionファンクションと異なります。特に、クラスを初期化するときに引数-classpathおよび-Dから導出された静的変数の値が、現在のコマンドラインでのこれらのスイッチの値を確実に反映するために必要です。
                     
FUNCTION runjava(cmdline VARCHAR2) RETURN VARCHAR2;
runjava_in_current_session
このファンクションは、現在のコマンドラインを実行する前に、以前にJavaを使用したときからセッションに残っているJavaの状態をクリアしない点を除き、runjavaファンクションと同じです。
                     
FUNCTION runjava_in_current_session(cmdline VARCHAR2) RETURN VARCHAR2;
構文
コマンドラインの構文の形式は次のとおりです。
[-options] classname [arguments...] [-options] -jar jarfile [arguments...]
オプション
-classpath -D -Xbootclasspath -Xbootclasspath/a -Xbootclasspath/p -cp
ノート:
最初の形式では、引数を指定したclassnameで特定されるクラスのmainメソッドが実行されます。2番目の形式では、JARで識別されるJARファイルのマニフェストでMain-Class属性によって特定されるクラスのmainメソッドが実行されます。これは、JDK/JREの構文解釈法と似ています。
                        
引数の概要
次の表に、コマンドライン引数の概要を示します。
表3-1 コマンドライン引数の概要
| 引数 | 説明 | 
|---|---|
| 
                                  classpath  | 
                              
                                  ディレクトリ、JARアーカイブおよびZIPアーカイブのコロン(:)で区切られたリスト(Windowsシステムの場合はセミコロンで区切られたリスト)を受け入れて、クラス・ファイルを検索します。通常、  | 
                           
| 
                                  D  | 
                              
                                  既存のJavaセッションの状態がない場合、システム・プロパティの値を設定します。コマンドライン・インタフェースのデフォルトの動作、つまり  | 
                           
| 
                                  Xbootclasspath  | 
                              
                                  ディレクトリ、JARアーカイブおよびZIPアーカイブのコロン(:)で区切られたリスト(Windowsシステムの場合はセミコロンで区切られたリスト)を受け入れます。このオプションは、ブートストラップ・クラスおよびリソースの検索パスの設定に使用します。  | 
                           
| 
                                  
  | 
                              
                                  ディレクトリ、JARアーカイブおよびZIPアーカイブのコロン(:)で区切られたリスト(Windowsシステムの場合はセミコロンで区切られたリスト)を受け入れます。ブートストラップ・クラスのパスの最後に付加されます。  | 
                           
| 
                                  
  | 
                              
                                  ディレクトリ、JARアーカイブおよびZIPアーカイブのコロン(:)で区切られたリスト(Windowsシステムの場合はセミコロンで区切られたリスト)を受け入れます。ブートストラップ・クラスのパスの前に付加されます。  | 
                           
| 
                                  
  | 
                              
                                  
  | 
                           
ノート:
create java systemで作成されたシステム・クラスは、-Xbootclasspathオプションを使用して検出されるファイルやフォルダを使用する前に必ず使用されます。
                        
関連トピック
3.1.5 クライアント側スタブの使用の概要
Oracle Database 10gでは、以前はサーバー側JavaコードをコールするネイティブなJavaインタフェースとして知られていたクライアント側スタブが導入されました。これは、簡略化されたアプリケーション統合です。クライアント側および中間層のJavaアプリケーションは、PL/SQLラッパーを定義しないでデータベース内のJavaを直接コールできます。クライアント側スタブでは、サーバー側Javaクラスのリフレクション機能が使用されます。
以前のリリースでは、データベース・クライアントからJavaストアド・プロシージャおよびファンクションをコールするには、関連するPL/SQLラッパーをJava Database Connectivity(JDBC)でコールする必要がありました。各ラッパーは、SQLシグネチャおよびJava実装とともに手動で公開する必要がありました。この方法には次のデメリットがあります。
- 
                        
シグネチャで許可されるJava型は、相当する型がSQLにある場合のみでした。
 - 
                        
Javaで発行された例外が正しく戻されません。
 
Oracle Database 12c リリース2 (12.2.0.1)以降、クライアント側スタブを生成するためにOracle JVM Webサービス・コールアウト・ユーティリティを使用できます。
3.1.5.1 デフォルトのサービス機能の使用
Oracle Database 11gリリース1(11.1)から、Oracle Databaseクライアントではデフォルトの新しい接続機能が提供されます。Oracle Databaseクライアントをインストールする場合、接続URLにデータベース・サーバーの詳細をすべて指定する必要はありません。特定の状況では、Oracle Database接続アダプタで、データベースがインストールされているコンピュータのホスト名のみが必要になります。
たとえば、JDBC接続URL構文では次のようになります。
jdbc:oracle:driver_type:[username/password]@[//]host_name[:port][:ORCL]
次の指定はオプションになります。
- 
                              
//はオプションです。 - 
                              
:portはオプションです。Oracle Netのデフォルトのリスナー・ポート(1521)が使用されない場合のみ、ポートを指定する必要があります。
 - 
                              
:ORCLまたはサービス名はオプションです。Oracle Databaseクライアントの接続アダプタは、ホストのデフォルト・サービスに接続します。ホストでは、これは
listener.oraファイルのORCLに設定されています。 
ノート:
デフォルト・サービスは、Oracle Database 11gリリース1(11.1)以降の機能です。Oracle Database 11gクライアントより以前のバージョンを使用してデータベースに接続する場合、SIDを指定する必要があります。
                           
3.1.5.2 基本構成を使用したデフォルト・サービスのテスト
次のコード・スニペットは、デフォルト・サービスが定義されているlistener.oraファイルの基本構成を示しています。
                        
MYLISTENER = (ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=testserver1)(PORT=1521))) DEFAULT_SERVICE_MYLISTENER=dbjf.app.myserver.com SID_LIST_MYLISTENER = (SID_LIST=(SID_DESC=(SID_NAME=dbjf) (GLOBAL_DBNAME=dbjf.app.myserver.com)(ORACLE_HOME=/test/oracle)) )
listener.oraファイルを定義した後、次のコマンドを使用してリスナーを再起動します。
                        
lsnrctl start mylistener
これで、次のURLは、listener.oraファイルのこの構成と連携して機能します。
                        
- 
                              
jdbc:oracle:thin:@//testserver1.myserver.com.com - 
                              
jdbc:oracle:thin:@//testserver1.myserver.com:1521 - 
                              
jdbc:oracle:thin:@testserver1.myserver.com - 
                              
jdbc:oracle:thin:@testserver1.myserver.com:1521 - 
                              
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com)(PORT=1521))) - 
                              
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com))) - 
                              
jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=)))