java のメソッドを公開するには、コール仕様を記述します。 このコール仕様により、java のメソッド名、引数の型および戻り値の型が、 対応する SQL にマップされます。
コール仕様は単に java メソッドを公開するだけで、 コール仕様を通してメソッドをコールすると、 ランタイム・システムは最小のオーバーヘッドでコールを処理します。
java のクラスを oracle 8i データベースに組み込むことで、 java アプリケーションは、 データベース・セッションのコンテキスト内に組み込まれます。
JServer は、そのセッション内のコール全般にわたって、 クライアントの java の状態を保持します。
1つのセッションのコンテキストで、 クライアントは次のことを実行します。
ここでは、oracle 8.1.6 での動作について述べます。
oracle JVM が正しくインストールされていることを、以下の手順で確認してください。
> svrmgrl Oracle Server Manager Release 3.1.6.0.0 - Production Copyright (c) 1997, 1999, Oracle Corporation. All Rights Reserved. Oracle8i Enterprise Edition Release 8.1.6.1.0 - Production With the Partitioning option JServer Release 8.1.6.0.0 - Production SVRMGR> connect internal SVRMGR> SELECT count(*) FROM dba_objects WHERE object_type LIKE 'JAVA%'; SVRMGR> COUNT(*) ---------- 8663
8.1.5 では最低でも 4000、 8.1.6 では最低でも 8000 が返ってこない場合は、 oracle JVM のインストールに失敗していますので、 手動で再インストールする必要があります。
その場合、以下の条件を満たす必要があります。
svrmgrl SVRMGR> connect internal SVRMGR> select * from dba_data_files; : SVRMGR> alter database datafile 'ロールバックセグメント・ファイル名' resize 250m; SVRMGR> alter database datafile 'システム表領域ファイル名' resize XX+30M;
cd $ORACLE_HOME/javavm/install svrmgrl SVRMGR> connect internal SVRMGR> @initjvm
Hello、Hello2 という java ストアド・プロシージャを作って見ましょう。
java ストアド・プロシージャには、以下の制限があります。
public class Hello { public static void HelloWorld() { System.out.println("Hello World !!"); } } class Hello2 { public static String Hello() { return "Hello World 2" ; } }
loadjava を使って、oracle データベースにスキーマ・オブジェクトとして、 java のソース、クラス、リソース・ファイルを登録できます。 java アーカイブ (JAR/ZIP) を登録するのが最も簡単です。
さらに dropjava を使って、java のソース、クラス、スキーマ・オブジェクトを、 データベース・スキーマから削除できます。
loadjava は java プログラム内のすべてのクラスの外部参照を、 次の3つのモードで解決します。
モード | 説 明 |
---|---|
ロード後 | -resolve はクラスをロードし INVALID をつけ、その後解決します。 ロードされていないクラスに依存するクラスは、すべて INVALID です。 |
ロード時 | -andresolve はクラスをロードし解決します。 一般的に、このモードはお薦めしません。 |
解決の延期 | 上記2つを指定しないと、クラスをロードしますが、 コンパイルも解決もしません。INVALID がついたクラスを call すると、 リゾルバが実行され、解決されたものは VALID に変更されます。 |
Hello.class と Hello2.class をロードします。
loadjava -resolve -user scott/tiger Hello.class loadjava -resolve -user scott/tiger Hello2.class
このとき、"ORA-00904:列名が無効です" のエラーが出るのは、 oracle JVM が正しくインストールされていないときです。 「java ストアド・プロシージャの開発の前提」を参考に, 再インストールしてから再試行してください。
クラスの登録がうまくいったかどうか、以下の手順で確認します。
SVRMGR> set charwidth 30 Charwidth 30 SVRMGR> select object_name,OBJECT_TYPE,status,timestamp from user_objects 2 where status='VALID' and OBJECT_TYPE like 'JAVA%'; OBJECT_NAME OBJECT_TYPE STATUS TIMESTAMP ------------------------------ ------------------ ------- ------------------- Hello JAVA CLASS VALID 2001-01-29:16:31:49 Hello2 JAVA CLASS VALID 2001-01-30:14:51:38 2 行選択されました。
クラス・スキーマ・オブジェクトが、正常に解決されると status は VALID になります。
java クラスの公開には、コール仕様を記述します。 これは、ロードされたクラス内のメソッド名をエントリ・ポイントとして指定します。 また、対応する SQL と java メソッドへの引数および戻り値には、 互換性のあるデータ型を設定する必要があります。
有効なデータ型のマッピング方法は、 以下のデータ型のマッピング表に示します。
次のコール仕様が宣言できます。
トップレベルの PL/SQL の場合は、2通あります。
java ストアド・プロシージャに戻り値がない場合と、ある場合です。
sqlplus scott/tiger SQL> create or replace PROCEDURE hello 2 as language java name 'Hello.HelloWorld'; 3 /
sqlplus scott/tiger SQL> create or replace FUNCTION hello2 return VARCHAR2 2 as language java name 'Hello2.Hello() return java.lang.String'; 3 /
SQL の call 文を使うと、トップレベルで公開された java メソッドを、 PL/SQL パッケージ文または SQL オブジェクト型でコールできます。
SQL*Plus からは、次のように call 文を対話形式で実行もできますし、 PL/SQL ストアド・ファンクションとしてコールすることもできます。
SQL> call hello(); SQL> variable myString varchar2(20); 2 call hello2() into :myString; 3 print myString;
hello2 は、以下のようにしても、コールできます。
SQL> select hello2 from dual;
コールの結果の出力は hello の場合は、トレース・ファイル
($ORACLE_BASE/admin/$ORACLE_SID/udump ディレクトリの下)
にかかれます。
また hello2 の場合は、直接返ってきます。
[ /home4/ora816/app/oracle/admin/ora816/udump/ora_14492.trc の内容 ] Oracle8i Enterprise Edition Release 8.1.6.1.0 - Production With the Partitioning option JServer Release 8.1.6.0.0 - Production ORACLE_HOME = /home4/ora816/app/oracle/product/8.1.6 System name: Linux Node name: perugia.it.ns-sol.co.jp Release: 2.2.5-22 Version: #1 Wed Jun 2 09:17:03 EDT 1999 Machine: i686 Instance name: ora816 Redo thread mounted by this instance: 1 Oracle process number: 11 Unix process pid: 14492, image: oracle@perugia.it.ns-sol.co.jp (TNS V1-V3) *** 2001-01-29 16:32:20.553 *** SESSION ID:(10.46818) 2001-01-29 16:32:20.552 Hello World !!
SQL> コールが完了しました。 MYSTRING -------------------------------- Hello World 2
System.out と System.err の出力先は、カレント・トレース・ファイルです。 これを SQL*Plus テキスト・バッファにリダイレクトするには、 次のように指定します。 そうするとコール終了時に出力されます。
最小バッファ・サイズは 2000 バイト、 最大バッファ・サイズは 999,999 バイトです。
SQL> set serveroutput on size 10000 SQL> call dbms_java.set_output(10000);
java ストアド・プロシージャが SQL 文を実行中に例外を検出すると、 そのプロシージャに対して java.sql.SQLException のサブクラスとしてインスタンス化された例外が投げられます。
この例外クラスには、getErrorCode() と getMessage() メソッドがあり、 これらを使って Oracle エラーコードとエラーメッセージを得られます。
SQL や PL/SQL からコールされたストアド・プロシージャで キャッチされない例外が発生した場合は、次のエラーメッセージがでます。
ORA-29532 不明な Java 例外で Java コールが終了しました:<文字列> <文字列> には java からのエラーが入ります。
java でデータベースにアクセスするプログラムを作り、 コール仕様を宣言し、svrmgr から呼んでみましょう。
import java.sql.*; import oracle.jdbc.driver.*; class Emp { public static void delEmp(String cond) throws SQLException { String defconn = "jdbc:default:connection:"; Emp e = new Emp(); int len; len = e.chkarg(cond); if (len == 0) return; Connection conn = DriverManager.getConnection(defconn); String sql = "DELETE FROM EMP WHERE " + cond; System.out.println("SQL = " + sql); try { Statement stmt = conn.createStatement(); stmt.executeUpdate(sql); stmt.close(); } catch (SQLException ex) {System.err.println(ex.getMessage());} } public static void main(String[] args ) throws SQLException { Emp e = new Emp(); if (args.length != 0) System.out.println("len = " + e.chkarg(args[0])); } int chkarg(String arg) { int len; try { len = arg.length(); if (len == 0 ) { System.out.println("chkarg : arg is null"); return 0; } } catch (Exception e) { System.out.println("chkarg : Exception= "+e.toString()); System.out.flush(); return 0; } return len; } } class ROWcnt { public static int CountROW(String tblNM) throws SQLException { String defconn = "jdbc:default:connection:"; String sql_base = "SELECT COUNT(*) FROM "; String tbl_emp = "EMP"; String sql,tblnm ; Emp e = new Emp(); int rows = 0,tlen; tblnm = tbl_emp; tlen = e.chkarg(tblNM); if (tlen != 0) tblnm = tblNM; sql = sql_base + tblnm; System.out.println("SQL = " + sql + ": tblnm = "+ tblnm); System.out.flush(); Connection conn = DriverManager.getConnection(defconn); try { Statement stmt = conn.createStatement(); ResultSet rset = stmt.executeQuery(sql); while (rset.next()) { rows = rset.getInt(1);} rset.close(); stmt.close(); } catch (SQLException ex) {System.err.println(ex.getMessage());} return rows; } public static void main(String[] args) throws SQLException { Emp e = new Emp(); if (args.length != 0) System.out.println("len = " + e.chkarg(args[0])); } }
SVRMGR> create or replace PROCEDURE delEmp (table_name IN VARCHAR2) 2> as language java name 'Emp.delEmp(java.lang.String)'; 3> / 文が処理されました。 SVRMGR> create or replace FUNCTION cntRow (table_name IN VARCHAR2) return NUMBER 2> as language java name 'ROWcnt.CountROW(java.lang.String) return int'; 3> / 文が処理されました。 SVRMGR> select ename,sal from emp; ENAME SAL ---------- ---------- SMITH 800 ALLEN 1600 WARD 1250 JONES 2975 MARTIN 1250 BLAKE 2850 CLARK 2450 SCOTT 3000 KING 5000 TURNER 1500 ADAMS 1100 JAMES 950 FORD 3000 MILLER 1300 14 行選択されました。 SVRMGR> call delEmp('sal >1500'); 文が処理されました。 SQL = DELETE FROM EMP WHERE sal >1500 SVRMGR> select ename,sal from emp; ENAME SAL ---------- ---------- SMITH 800 WARD 1250 MARTIN 1250 TURNER 1500 ADAMS 1100 JAMES 950 MILLER 1300 7 行選択されました。 SVRMGR> @callRow 文が処理されました。 SQL = SELECT COUNT(*) FROM emp: tblnm = emp ROWNO ---------- 7
---- callRow.sql -------------- variable rowno NUMBER call cntRow('emp') into :rowno; print rowno;
Java はヒープをクラスのロード域とオブジェクトの格納域の両方として使用します。
(オブジェクト格納域は、一般的に new したオブジェクト用格納域です)
一方、JServer はヒープを2つに分けて使用します。 クラスのロード域としては shared_pool を使用し、 オブジェクトの格納域としては、 専用サーバの場合は UGA を、マルチスレッドサーバの場合は java_pool を使用します。
init.ora で変更できる JServer のメモリ初期化パラメータは、以下のとおりです。
パラメータ名 | 説 明 |
---|---|
shared_pool_size | JServer の クラス・ローダがクラスのロード用として使います。 |
java_pool_size | JServer のメモリ・マネージャが使います。 MTS の場合は、実行時のオブジェクトを格納するのに使いますが、 専用サーバの場合は、ここを使わず UGA を使います。 |
java_soft_sessionspace_limit | 1つのセッション期間中、java のメモリ使用量がこれを超えると、 警告をトレース・ファイルに書きこみます。デフォルトは 1MB です。 |
java_max_sessionspace_size | 使用可能なセッション領域のハード・リミットを設定します。 java のメモリ使用量がセッション期間中このサイズを超えると、 メモリ不足障害エラーが発生します。デフォルトは 4GB です。 |
これらのパラメータを確認する方法は、以下のとおりです。
svrmgrl SVRMGR> connect internal SVRMGR> show parameter session; NAME TYPE VALUE ----------------------------------- ------- ------------------------------ java_max_sessionspace_size 整数値 0 java_soft_sessionspace_limit 整数値 0 license_max_sessions 整数値 0 license_sessions_warning 整数値 0 mts_sessions 整数値 0 session_cached_cursors 整数値 0 session_max_open_files 整数値 10 sessions 整数値 60 SVRMGR> select * from v$sgastat where pool='java pool'; POOL NAME BYTES ----------- -------------------------- ---------- java pool free memory 17989632 java pool memory in use 2981888 2 行選択されました。
8.1.6 では、java_max_sessionspace_size、java_soft_sessionspace_limit の値は、正しく表示されません。
このマニュアルには以下の誤記があります。
ページ | 誤 | 正 |
---|---|---|
3-7 | CALL proc | call proc |
3-7 | CALL func(?,?) | call func(?,?) |
4-8 | / (最後の行に追加) | |
4-14 | CALL balance(?) | call balance(?) |
用語 | 意 味 |
---|---|
JServer | java 対応 oracle 8i データベース・サーバ |
Aurora | oracle 8i JVM の名称 |
セッション | java コードを実行するユーザは、 サーバ上でセッションを確立する必要があります。通常、 1人のユーザがサーバに接続している期間が1つのセッションになります。 |
コール | セッション内で java コードを実行すること。 |
java スキーマ・ オブジェクト |
java ソース、クラス、リソースの総称 |
ORA 番号 | 意 味 | Page |
---|---|---|
00904 | loadjava で出る場合は
oracle JVM が正しくインストールされていないためです。 対策:手動で JServer をインストールしてください。 |
|
04031 | loadjava で出る場合は java_pool_size の不足ですから、多くしてください。 メッセージには shared pool と出ますが java_pool_size のことです。 |
|
29532 | java ストアド・プロシージャ実行中にエラーが検出されました。:XXXX 対策:java からのメッセージ XXX を参考にしてください。 |
|
29545 | クラスファイルの形式が不正です 対策:クラスファイルを作り直して dropjava/loadjava してください。 |
D:2-20 |
29549 | "jdbc:default:connection:" で接続しているセッションが
すでに終了しています。 対策:connect しなおしてください。 |
|
29552 | クラスファイルの形式が不正です 対策:クラスファイルを作り直して dropjava/loadjava してください。 |
D:2-20 |
例 外 | 意 味 | Page |
---|---|---|
oracle.aurora.vm.DeadlockError | スレッド間でデッドロックを検出した | D:2-41 |
oracle.aurora.vm.LimboError | 1つしかないスレッドを中断した | D:2-41 |
ThreadDeathException | スレッドの実行を停止した | D:2-42 |
oracle.aurora.awt. UnsupportedOperation |
awt を使おうとした。 aurora ではサポートしていません | D:2-28 |
java.io.IOException | 前コールの終了時にクローズした STREAM に対して IO をした。 対策:open しなおしてください。 |
D:2-36 |
ClassNotFound | コール時に使用するクラスが見つからない 対策:コールするクラスで使う全クラスを loadjava する |
S:2-5 |
OutOfMemoryError | JServer が使うヒープ領域がなくなった。 対策:Statement,ResultSet などで不要になったものは close() する。 |
JServer 8.1.6 では、以下のバグが報告されています。
項 目 | 内 容 |
---|---|
svrmgrl | Total System Global Area に表示時される値に、 java_pool_size 分が含まれていない |
svrmgrl | show parameter session; の java_max_sessionspace_size、java_soft_sessionspace_limit の 表示される値が正しくない |
java.lang.System.arraycopy() | java.lang.NullPointerException が出て正しく動作しない |
SQL 型 | java クラス |
---|---|
CHAR,NCHAR LONG,VARCHAR2 NVARCHAR2 |
oracle.sql.CHAR java.lang.String java.sql.Date java.sql.Time java.sql.Timestamp java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double java.math.BigDecimal byte,short,int,long,float,double |
DATE | oracle.sql.DATE java.sql.Date java.sql.Time java.sql.Timestamp java.lang.String |
NUMBER | oracle.sql.NUMBER java.lang.Byte java.lang.Short java.lang.Integer java.lang.Long java.lang.Float java.lang.Double java.math.BigDecimal byte,short,int,long,float,double |
RAW,LONG RAW | oracle.sql.RAW byte[] |
ROWID | oracle.sql.CHAR oracle.sql.ROWID java.lang.String |
BFILE | oracle.sql.BFILE |
BLOB | oracle.sql.BLOB oracle.jdbc2.Blob |
CLOB,NCLOB | oracle.sql.CLOB oracle.jdbc2.Clob |
OBJECT | oracle.sql.STRUCT oracle.SqljData oracle.jdbc2.Struct |
REF | oracle.sql.REF oracle.jdbc2.Ref |
TABLE,VARRAY | oracle.sql.ARRAY oracle.jdbc2.Array |