6.2 コール仕様の定義

コール仕様とそのコール仕様によって公開されるJavaメソッドは、JavaメソッドにPUBLICシノニムがないかぎり、同一スキーマに常駐している必要があります。コール仕様の宣言方法は次のとおりです。

  • スタンドアロンPL/SQLファンクションまたはプロシージャ

  • パッケージ化されたPL/SQLファンクションまたはプロシージャ

  • SQLオブジェクト型のメンバー・メソッド

コール仕様によって、Javaメソッドのトップレベルのエントリ・ポイントがOracle Databaseに公開されます。このため、公開できるのはpublic staticメソッドのみです。ただし、例外が1つあります。インスタンス・メソッドは、SQLオブジェクト型のメンバー・メソッドとして公開できます。

パッケージ化されたコール仕様はトップレベルのコール仕様と同様に機能します。このため、メンテナンスを容易にするために、コール仕様をパッケージ本体に配置することもできます。この方法では、他のスキーマ・オブジェクトを無効にせずにコール仕様を変更できます。また、コール仕様をオーバーロードすることもできます。

この項の内容は次のとおりです。

6.2.1 パラメータ・モードの設定について

Javaや他のオブジェクト指向言語では、メソッドは引数として渡されたオブジェクトに値を割り当てることはできません。SQLまたはPL/SQLからメソッドをコールするときに引数の値を変更するには、コール仕様でOUTパラメータまたはIN OUTパラメータとして宣言する必要があります。対応するJavaパラメータは、要素の数が1つのみの配列であることが必要です。

要素の値を適切な型の別のJavaオブジェクトに置き換えるか、またはJavaの型によっては値を変更できます。いずれの方法でも、新しい値がコール元に戻されます。たとえば、コール仕様のNUMBER型のOUTパラメータを、float[] pとして宣言されたJavaパラメータにマッピングし、p[0]に新しい値を割り当てます。

ノート:

OUTパラメータまたはIN OUTパラメータを宣言するファンクションは、SQLのデータ操作言語(DML)文からはコールできません。

6.2.2 データ型のマッピングについて

コール仕様では、対応するSQLパラメータとJavaパラメータ、およびファンクションの結果には互換性のあるデータ型を設定する必要があります。

表6-1に、有効なデータ型マッピングが示されています。Oracle Databaseでは、SQL型とJavaクラス間で自動的に変換が行われます。

表6-1 有効なデータ型マッピング

SQL型 Javaクラス

CHARVARCHAR2LONG

java.lang.String

oracle.sql.CHAR

oracle.sql.ROWID

byte[]

NUMBER

boolean

char

byte

byte[]

short

int

long

float

double

java.lang.Byte

java.lang.Short

java.lang.Integer

java.lang.Long

java.lang.Float

java.lang.Double

java.math.BigDecimal

oracle.sql.NUMBER

BINARY_INTEGER

boolean

char

byte

byte[]

short

int

long

BINARY_FLOAT

oracle.sql.BINARY_FLOAT

byte[]

BINARY_DOUBLE

oracle.sql.BINARY_DOUBLE

byte[]

DATE

oracle.sql.DATE

byte[]

RAW

oracle.sql.RAW

byte[]

BLOB

oracle.sql.BLOB

CLOB

oracle.sql.CLOB

BFILE

oracle.sql.BFILE

ROWID

oracle.sql.ROWID

byte[]

TIMESTAMP

oracle.sql.TIMESTAMP

byte[]

TIMESTAMPWITHTIMEZONE

oracle.sql.TIMESTAMPTZ

TIMESTAMPWITHLOCALTIMEZONE

oracle.sql.TIMESTAMPLTZ

ref cursor

java.sql.ResultSet

sqlj.runtime.ResultSetIterator

ユーザー定義の名前付き型、ADT

oracle.sql.STRUCT

opaque名前付き型

oracle.sql.OPAQUE

NESTED TABLEおよびVARRAY名前付き型

oracle.sql.ARRAY

名前付き型の参照

oracle.sql.REF

次の点を考慮する必要もあります。

  • 最後の4つのSQL型は、まとめて名前付き型と呼ばれています。

  • BLOBCLOBBFILEREF CURSORおよび名前付き型を除くすべてのSQL型は、Javaのバイト配列であるJava型byte[]にマップできます。この場合、引数の変換は、SQL値のRAWバイナリ表現をJavaのバイト配列にコピーする(あるいはその逆へのコピーを行う)ことを意味します。

  • ORADataインタフェースおよび関連メソッドを実装するJavaクラス、または表でoracle.sqlクラスのサブクラスとして示されたJavaクラスは、BINARY_INTEGERおよびREF CURSOR以外のSQL型からマップできます。

  • UROWID型およびNUMBERのサブタイプ(INTEGERREALなど)はサポートされていません。

  • LONGまたはLONG RAWの列から、32KBを超える値をJavaストアド・プロシージャに取り出すことはできません。

6.2.3 サーバー側JDBC内部ドライバの使用

Java Database Connectivity(JDBC)では、JDBCドライバのセットを管理するDriverManagerクラスを使用して、データベースとの接続を確立します。JDBCドライバがロードされた後、getConnection()メソッドを使用できます。正しいドライバが検出されると、getConnection()メソッドによって、データベース・セッションを表すConnectionオブジェクトが戻されます。SQL文はすべて、そのセッションのコンテキスト内で実行されます。

ただし、サーバー側JDBC内部ドライバは、デフォルトのセッションおよびトランザクション・コンテキスト内で動作します。そのため、すでにデータベースに接続された状態であり、SQL操作はすべてデフォルトのトランザクションの一部です。ドライバは事前に登録されているため、登録する必要はありません。Connectionオブジェクトを取得するには、次のコード行を実行します。

Connection conn = DriverManager.getConnection("jdbc:default:connection:");

INパラメータを取らず、かつ1回のみ実行されるSQL文に対しては、Statementクラスを使用します。Connectionオブジェクト上でcreateStatement()メソッドがコールされると、新しいStatementオブジェクトが戻されます。次に例を示します。

String sql = "DROP " + object_type + " " + object_name;
Statement stmt = conn.createStatement();
stmt.executeUpdate(sql);

INパラメータを取るか、または複数回実行されるSQL文に対しては、PreparedStatementクラスを使用します。1つ以上のパラメータ・プレースホルダを含むことができるSQL文は、プリコンパイルされます。疑問符(?)がプレースホルダとして機能します。Connectionオブジェクト上でprepareStatement()メソッドがコールされると、プリコンパイルされたSQL文が含まれる新しいPreparedStatementオブジェクトが戻されます。次に例を示します。

String sql = "DELETE FROM dept WHERE deptno = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, deptID);
pstmt.executeUpdate();

ResultSetオブジェクトには、SQLの問合せ結果、つまり検索条件を満たす行が含まれます。next()メソッドを使用すると次の行に移動し、その行がカレント行になります。現在の行から列の値を取り出すには、getXXX()メソッドを使用します。次に例を示します。

String sql = "SELECT COUNT(*) FROM " + tabName;
int rows = 0;
Statement stmt = conn.createStatement();
ResultSet rset = stmt.executeQuery(sql);
while (rset.next())
{
  rows = rset.getInt(1);
}

CallableStatementオブジェクトを使用すると、ストアド・プロシージャをコールできます。これには、1つの戻りパラメータと任意の数のINOUTおよびIN OUTのパラメータを含めることができる、コール・テキストが含まれています。コールは、中カッコ({})で区切られるエスケープ句を使用して記述されます。次の例に示すように、エスケープ構文には3つの形式があります。

// parameterless stored procedure
CallableStatement cstmt = conn.prepareCall("{CALL proc}");

// stored procedure
CallableStatement cstmt = conn.prepareCall("{CALL proc(?,?)}");

// stored function
CallableStatement cstmt = conn.prepareCall("{? = CALL func(?,?)}");

重要な点

ストアド・プロシージャにアクセスするJDBCアプリケーションを開発するときは、次の点を考慮する必要があります。

  • 各Oracle JVMセッションには、存在するデータベース・セッションに対して1つの暗黙的なネイティブ接続があります。この接続は概念的で、Javaオブジェクトではありません。これはこのセッションに固有な側面であり、JVM内でオープンまたはクローズできません。

  • サーバー側JDBC内部ドライバは、デフォルトのトランザクション・コンテキスト内で動作します。データベースにすでに接続された状態であり、SQL操作はすべてデフォルトのトランザクションの一部です。このトランザクションはローカル・トランザクションで、Java Transaction API(JTA)またはJava Transaction Service(JTS)によって実装されるようなグローバル・トランザクションの一部ではありません。

  • 文および結果セットは複数のコールにわたって存続するため、ファイナライザはデータベース・カーソルを解放しません。カーソルが不足しないように、処理が終了した後にすべての文および結果セットをクローズしてください。または、DBAに依頼して、初期化パラメータのOPEN_CURSORSで設定されている制限値を増やすこともできます。

  • サーバー側JDBC内部ドライバは、自動コミットをサポートしていません。そのため、アプリケーションで、データベースの変更を明示的にコミットまたはロールバックする必要があります。

  • サーバー側JDBC内部ドライバを使用して、リモート・データベースには接続できません。Javaプログラムを実行しているサーバーにのみ接続できます。サーバー/サーバー接続の場合は、サーバー側JDBC Thinドライバを使用します。クライアント/サーバー接続の場合は、クライアント側JDBC ThinドライバまたはJDBC Oracle Call Interface(OCI)ドライバを使用します。

  • 通常、デフォルトの接続インスタンスはクローズしないでください。このインスタンスは複数の場所に格納される可能性がある単一インスタンスであり、クローズするとそれぞれの場所が使用できなくなります。クローズすると、それ以降にOracleDriver.defaultConnectionメソッドをコールしたときに、新しいオープン・インスタンスが作成されます。OracleDataSource.getConnectionメソッドはコールするたびに新規オブジェクトを戻しますが、毎回、新しいデータベース接続を作成するわけではありません。同じ暗黙的なネイティブ接続を使用し、同じセッション状態(特にローカル・トランザクション)を共有します。