| Oracle Database 2日でJava開発者ガイド 11g リリース1(11.1) E05692-02 |
|
この章では、Javaアプリケーションで使用できる追加機能について説明します。一部の機能でサンプル・アプリケーションに実装されていないものもありますが、パフォーマンスを向上させるためにコード内で使用できる機能拡張もあります。
この章は次の項で構成されています。
本番環境では、動的SQL、つまり即時にSQL文を生成することが常に必要です。特に、データベースに対して行われる更新の場合など、最終的な問合せが実行時までわからないことがよくあります。
異なる更新値を持つ多数の類似問合せをデータベースに対して実行する必要がある場合は、Statementオブジェクトを拡張するOraclePreparedStatementオブジェクトを使用できます。この場合、リテラルの更新値をバインド変数に置き換えます。また、OracleCallableStatementオブジェクトを介してストアド・プロシージャをコールすることによって、データベースでPL/SQLストアド・ファンクションを使用することもできます。
この項では、次の項目について説明します。
データベースに対して静的SQL問合せを実行する場合は、Statementオブジェクトを使用します。ただし、複数の類似問合せを実行したり、データベースの多数の列に影響する複数の更新を実行する場合は、それぞれの問合せをアプリケーションにハード・コードすることはできません。
同じSQL文を複数回実行する場合は、OraclePreparedStatementを使用できます。次のような問合せについて考えてみます。
SELECT * FROM Employees WHERE ID=xyz;
この問合せのxyzの値が変更されるたびに、SQL文を再度コンパイルする必要があります。
OraclePreparedStatement機能を使用すると、実行するSQL文はプリコンパイルされてPreparedStatementオブジェクトに格納されるため、実行するたびにコンパイルしなくても、何度でも実行できます。文中のデータが変化する場合は、そのデータのプレースホルダとしてバインド変数を使用し、実行時にリテラル値を指定することができます。
OraclePreparedStatementの使用について、次に例を示します。
OraclePreparedStatement pstmt = conn.prepareStatement("UPDATE Employees SET salary = ? WHERE ID = ?"); pstmt.setBigDecimal(1, 153833.00) pstmt.setInt(2, 110592)
OraclePreparedStatementインタフェースを使用するメリットは、次のとおりです。
PreparedStatementオブジェクトを使用することによって、更新をバッチ化できます。
OracleCallableStatementインタフェースを使用すると、データベース上のストアド・プロシージャにアクセスできます。このインタフェースは、OraclePreparedStatementインタフェースを拡張します。OracleCallableStatementインタフェースは、ストアド・プロシージャをコールするために、標準的なJDBCエスケープ構文で構成されています。このインタフェースは、結果パラメータの有無にかかわらず使用できます。ただし、結果パラメータを使用する場合は、それがOUTパラメータとして登録されている必要があります。このインタフェースでは、その他のパラメータとして、INまたはOUT、あるいはその両方を使用できます。
これらのパラメータは、OraclePreparedStatementインタフェースから継承されたアクセッサ・メソッドを使用することによって設定されます。INパラメータは、setXXXメソッドを使用して設定され、OUTパラメータはgetXXXメソッドを使用して取得されます。XXXは、Javaデータ型のパラメータです。
また、CallableStatementは、複数のResultSetオブジェクトを返すこともあります。
例として、次のようにOracleCallableStatementを作成して、fooストアド・プロシージャをコールすることができます。
OracleCallableStatement cs = (OracleCallableStatement) conn.prepareCall("{call foo(?)}");
次のいずれかの方法で、文字列barをこのプロシージャに渡すことができます。
cs.setString(1,"bar"); // JDBC standard // or... cs.setString("myparameter","bar"); // Oracle extension
バインド変数は、SQL文中のリテラルのかわりとなる変数です。バインド変数をOraclePreparedStatementおよびOracleCallableStatementとともに使用して、SQL文の構築に使用されるパラメータ値を指定します。バインド変数を使用すると、本番環境でのパフォーマンスが大幅に向上します。
PL/SQLブロックまたはストアド・プロシージャ・コールには、入力変数と出力変数を区別するために、IN、OUTおよびIN OUT修飾子を使用できます。入力変数値はsetXXXメソッドを使用して設定され、OUT変数値はgetXXXメソッドを使用して取得されます。XXXは、Javaデータ型の値です。この値は、アクセス先のデータベース内の列のSQLデータ型によって異なります。
Oracle Java Database Connectivity(JDBC)ドライバでは、PL/SQLストアド・プロシージャおよび無名ブロックの処理がサポートされています。Oracle JDBCドライバでは、Oracle PL/SQLブロック構文およびほとんどのSQL92エスケープ構文がサポートされています。次のPL/SQLコールは、すべてのOracle JDBCドライバで動作します。
// SQL92 syntax CallableStatement cs1 = conn.prepareCall ( "{call proc (?,?)}" ) ; // stored proc CallableStatement cs2 = conn.prepareCall ( "{? = call func (?,?)}" ) ; // stored func // Oracle PL/SQL block syntax CallableStatement cs3 = conn.prepareCall ( "begin proc (?,?); end;" ) ; // stored proc CallableStatement cs4 = conn.prepareCall ( "begin ? := func(?,?); end;" ) ; // stored func
Oracle構文の例として、ストアド・ファンクションを作成するPL/SQLコードの一部を示します。このPL/SQLファンクションでは文字列を取得して接尾辞を連結します。
create or replace function foo (val1 char) return char as begin return val1 || 'suffix'; end;
次のように、Javaプログラムでこのストアド・ファンクションをコールできます。
OracleDataSource ods = new OracleDataSource(); ods.setURL("jdbc:oracle:thin:@<hoststring>"); ods.setUser("hr"); ods.setPassword("hr"); Connection conn = ods.getConnection(); CallableStatement cs = conn.prepareCall ("begin ? := foo(?); end;"); cs.registerOutParameter(1,Types.CHAR); cs.setString(2, "aa"); cs.executeUpdate(); String result = cs.getString(1);
次の項では、このマニュアルのサンプル・アプリケーションでストアド・プロシージャを使用する方法について説明します。
JDeveloperを使用すると、接続ナビゲータを使用して、ストアド・プロシージャをデータベースに作成できます。次の手順では、サンプル・アプリケーションで従業員レコードを挿入するための代替方法として使用できるストアド・プロシージャを作成します。
HRデータベース内のオブジェクトを表示します。
insert_employeeと入力します。「OK」をクリックします。プロシージャのスケルトン・コードがソース・エディタに表示されます。
PROCEDURE "INSERT_EMPLOYEE" (p_first_name employees.first_name%type, p_last_name employees.last_name%type, p_email employees.email%type, p_phone_number employees.phone_number%type, p_job_id employees.job_id%type, p_salary employees.salary%type )
BEGIN文の後のNULLを示す行を次の内容で置き換えます。
INSERT INTO Employees VALUES (EMPLOYEES_SEQ.nextval, p_first_name , p_last_name , p_email , p_phone_number, SYSDATE, p_job_id, p_salary,.30,100,80);
この文では、DataHandler.javaクラスのaddEmployeeメソッドの最後の3行に使用される、ハードコードされた同じ値が使用されていることがわかります。
END文にプロシージャ名を追加します。
END insert_employee;
例6-6に、ストアド・プロシージャの完成したコードを示します。
PROCEDURE "INSERT_EMPLOYEE" (p_first_name employees.first_name%type, p_last_name employees.last_name%type, p_email employees.email%type, p_phone_number employees.phone_number%type, p_job_id employees.job_id%type, p_salary employees.salary%type ) AS BEGIN INSERT INTO Employees VALUES (EMPLOYEES_SEQ.nextval, p_first_name , p_last_name , p_email , p_phone_number, SYSDATE, p_job_id, p_salary,.30,100,80); END insert_employee;
次の手順では、addEmployeeメソッドのかわりに使用できるメソッドをDataHandler.javaクラスに追加します。ここで追加する新しいメソッドでは、insert_employeeストアド・プロシージャを使用します。
DataHandler.javaファイルがJavaソース・エディタでまだ開いていない場合は、ダブルクリックして開きます。
CallableStatementをインポートします。
import java.sql.CallableStatement;
addEmployeeメソッドの後に、addEmployeeSPメソッドの宣言を追加します。
public String addEmployeeSP(String first_name, String last_name, String email, String phone_number, String job_id, int salary) throws SQLException { }
このメソッドのシグネチャはaddEmployeeのものと同じです。
tryブロックを追加し、このブロック内でデータベースに接続します。
try { getDBConnection(); }
tryブロック内で次のSQL文字列を作成します。
sqlString = "begin hr.insert_employee(?,?,?,?,?,?); end;";
文中の疑問符(?)はバインド変数で、ストアド・プロシージャによって使用される、first_name、last_nameなどの値のプレースホルダの役割を果たします。
CallableStatementを作成します。
CallableStatement callstmt = conn.prepareCall(sqlString);
INパラメータを設定します。
callstmt.setString(1, first_name); callstmt.setString(2, last_name); callstmt.setString(3, email); callstmt.setString(4, phone_number); callstmt.setString(5, job_id); callstmt.setInt(6, salary);
System.out.println("\nInserting with stored procedure: " + sqlString); callstmt.execute();
return "success";
tryブロックの後にcatchブロックを追加して、すべてのエラーを捕捉します。例5-5で作成したlogExceptionをコールします。
catch ( SQLException ex ) { System.out.println("Possible source of error: Make sure you have created the stored procedure"); logException( ex ); return "failure"; }
DataHandler.javaを保存します。
例6-7に、完成したメソッドを示します。
|
注意:
|
public String addEmployeeSP(String first_name, String last_name, String email, String phone_number, String job_id, int salary) throws SQLException { try { getDBConnection(); sqlString = "begin hr.insert_employee(?,?,?,?,?,?); end;"; CallableStatement callstmt = conn.prepareCall(sqlString); callstmt.setString(1, first_name); callstmt.setString(2, last_name); callstmt.setString(3, email); callstmt.setString(4, phone_number); callstmt.setString(5, job_id); callstmt.setInt(6, salary); System.out.println("\nInserting with stored procedure: " + sqlString); callstmt.execute(); return "success"; } catch ( SQLException ex ) { System.out.println("Possible source of error: Make sure you have created the stored procedure"); logException( ex ); return "failure"; } }
この項の手順では、insert.jspページにラジオ・ボタンを追加します。従業員を挿入する場合に、ストアド・プロシージャを使用するか、またはJavaコードでSQL問合せを使用するかを、このラジオ・ボタンによって、ユーザーが選択できるようにします。
insert.jspを開いていない場合は、ビジュアル・エディタで開きます。
jsp:useBeanタグをこのページに追加します。IDとしてempsbeanを入力し、「クラス」としてhr.DataHandlerを参照して選択し、「有効範囲」を「セッション」に設定します。ページ上でUseBeanが選択されている状態で、この行のスタイルをHeading 3ではなくNoneに設定します。
useSPを入力し、「値」にfalseを入力して、「チェック」を選択します。「OK」をクリックします。
useSPを入力し、「値」にtrueを入力して、「チェック」チェック・ボックスが選択されていないことを確認します。
図6-1に、ストアド・プロシージャを使用するときのオプションとなるラジオ・ボタンのあるinsert.jspを示します。
この項の手順では、insert.jspページ上でフォームを処理するinsert_action.jspファイルを変更し、ラジオ・ボタンの選択によって、新しい従業員レコードを挿入するための適切なメソッドを選択します。
insert_action.jspを開いていない場合は、ビジュアル・エディタで開きます。
String useSPFlag = request.getParameter("useSP");
empsbean.addEmployee行を次のコード行で置き換えます。このコード行では、addEmployeeSPメソッドまたはpure JDBC addEmployeeメソッドを選択してレコードを挿入します。
if ( useSPFlag.equalsIgnoreCase("true")) empsbean.addEmployeeSP(first_name, last_name, email, phone_number, job_id, salary.intValue()); // otherwise use pure JDBC insert else empsbean.addEmployee(first_name, last_name, email, phone_number, job_id, salary.intValue());
insert_action.jspを保存します。
これで、アプリケーションを実行すると、挿入ページのラジオ・ボタンを使用して新しい従業員レコードを挿入する方法を選択できます。ブラウザでは、ページは図6-2のように表示されます。
Oracle JDBCドライバは、REF CURSOR型のカーソル変数をサポートします。これは、JDBC標準には含まれません。REF CURSOR型は、JDBC結果セットとしてサポートされます。
カーソル変数は、問合せ作業領域の内容ではなく、メモリーの場所を保持します。カーソル変数を宣言すると、ポインタが作成されます。SQLでは、ポインタのデータ型はREF xです。REFはREFERENCEの短縮であり、xは参照される実体を示します。したがって、REF CURSORはカーソル変数への参照を示します。多数の作業領域を指すために多数のカーソル変数が存在する場合があるため、REF CURSORは、多数の異なる型のカーソル変数を識別するカテゴリまたはデータ型指定子と考えることができます。本来、REF CURSORは問合せ結果をカプセル化します。
Oracleは結果セットを返しません。問合せによって返されたデータにアクセスするには、CURSORおよびREF CURSORを使用します。CURSORには、問合せ結果およびメタデータが含まれます。REF CURSOR(CURSOR変数)データ型には、カーソルへの参照が含まれます。RDBMSとクライアント、またはデータベースでPL/SQLとJavaの間での受け渡しが可能です。問合せまたはストアド・プロシージャから返される場合もあります。
この項では、次の項目について説明します。
カーソル変数を作成するには、REF CURSORカテゴリに属する型を識別することから開始します。次に例を示します。
dept_cv DeptCursorTyp ...
次に、DeptCursorTyp型であることを宣言して、カーソル変数を作成します。
DECLARE TYPE DeptCursorTyp IS REF CURSOR
REF CURSORは、特定のデータ型というよりも、データ型のカテゴリです。ストアド・プロシージャは、REF CURSORカテゴリのカーソル変数を返すことがあります。この出力は、データベース・カーソルまたはJDBC結果セットと同等です。
Javaでは、REF CURSORはResultSetオブジェクトとしてマテリアライズされ、次のようにアクセスできます。
import oracle.jdbc.*; ... CallableStatement cstmt; ResultSet cursor; // Use a PL/SQL block to open the cursor cstmt = conn.prepareCall ("begin open ? for select ename from emp; end;"); cstmt.registerOutParameter(1, OracleTypes.CURSOR); cstmt.execute(); cursor = ((OracleCallableStatement)cstmt).getCursor(1); // Use the cursor like a normal ResultSet while (cursor.next ()) {System.out.println (cursor.getString(1));}
前述の例では、次の手順が実行されます。
prepareCallメソッドを使用して、CallableStatementオブジェクトが作成されます。
REF CURSORを返すPL/SQLプロシージャが実装されます。
REF CURSORには、型コードOracleTypes.CURSORを使用します。
REF CURSORが返されます。
getCursorメソッドを使用するために、CallableStatementオブジェクトがOracleCallableStatementにキャストされます。このメソッドは、標準JDBC Application Program Interface(API)に対するOracleの拡張機能であり、REF CURSORをResultSetオブジェクトに返します。
次の項では、サンプル・アプリケーションを拡張し、新しい従業員レコードを挿入するときに、動的に生成された職務IDと職務名のリストを「ジョブ」フィールドに表示します。
これを行うには、REF CURSORを使用して、職務の結果セットをJobs表から取得するデータベース・ファンクションGET_JOBSを作成します。新しいJavaメソッドgetJobsは、このデータベース・ファンクションをコールして結果セットを取得します。
次の手順では、データベースで新しいパッケージを作成して、REF CURSOR宣言を保持します。
JOBSPKGと入力します。「OK」をクリックします。パッケージの定義がソース・エディタに表示されます。
REF CURSORを宣言します。
TYPE ref_cursor IS REF CURSOR;
例6-10に、パッケージのコードを示します。
PACKAGE "JOBSPKG" AS TYPE ref_cursor IS REF CURSOR; END;
次の手順では、REF CURSORを使用して、職務の結果セットをJobs表から取得するデータベース・ファンクションGET_JOBSを作成します。
HRデータベースのオブジェクトを表示します。「ファンクション」を右クリックし、ショートカット・メニューで「新規PL/SQLファンクション」を選択します。
GET_JOBSと入力します。「OK」をクリックします。GET_JOBSファンクションの定義がソース・エディタに表示されます。
VARCHAR2のかわりに、戻り値としてJobsPkg.ref_cursorを指定します。
ASキーワードの後に、次のように入力します。
jobs_cursor JobsPkg.ref_cursor;
BEGINブロックで、次のコードを入力して現在の内容を置き換えます。
OPEN jobs_cursor FOR SELECT job_id, job_title FROM jobs; RETURN jobs_cursor;
例6-11に、ファンクションのコードを示します。
FUNCTION "GET_JOBS" RETURN JobsPkg.ref_cursor AS jobs_cursor JobsPkg.ref_cursor; BEGIN OPEN jobs_cursor FOR SELECT job_id, job_title FROM jobs; RETURN jobs_cursor; END;
次の手順では、GET_JOBSファンクションをコールして結果セットを取得するJavaメソッドgetJobsをDataHandlerクラスに作成します。
DataHandler.javaがまだ開いていない場合は、ダブルクリックしてソース・エディタで開きます。
public ResultSet getJobs() throws SQLException { }
getDBConnection();
jobqueryを宣言します。
String jobquery = "begin ? := get_jobs; end;";
prepareCallメソッドを使用して、CallableStatementを作成します。
CallableStatement callStmt = conn.prepareCall(jobquery);
OUTパラメータの型を登録します。
callStmt.registerOutParameter(1, OracleTypes.CURSOR);
oracle.jdbc.OracleTypesをインポートすることを求めるメッセージが表示されます。[Alt]+[Enter]キーを押した後、表示されるリストでOracleTypes(oracle.jdbc)を選択します。
callStmt.execute(); rset = (ResultSet)callStmt.getObject(1);
tryブロックで囲みます。
catch ( SQLException ex ) { logException( ex ); }
catchブロックを閉じた後で、結果セットを返します。
return rset;
getJobsメソッドのコードを次に示します。
public ResultSet getJobs() throws SQLException { try { getDBConnection(); String jobquery = "begin ? := get_jobs; end;"; CallableStatement callStmt = conn.prepareCall(jobquery); callStmt.registerOutParameter(1, OracleTypes.CURSOR); callStmt.execute(); rset = (ResultSet)callStmt.getObject(1); } catch ( SQLException ex ) { logException( ex ); } return rset; }
職務IDと職務名のリストを表示するドロップダウン・リストを挿入ページに作成するために、職務IDと職務名をハードコードしました。次の手順では、これを動的に生成されたリストに置き換えます。このリストは、前の項でREF CURSORによって作成したリストです。
insert.jspが開いていない場合は、アプリケーション・ナビゲータのビジュアル・エディタでダブルクリックして開きます。
useBeanタグの右側のページにドラッグします。「Page Directiveの挿入」ダイアログ・ボックスで、「言語」にJavaを入力し、「インポート」フィールドでjava.sql.ResultSetを参照して選択します。「OK」をクリックします。
getJobsメソッドを実行して、職務のリストを含む結果セットを返します。
ResultSet rset = empsbean.getJobs();
while (rset.next ()) { out.println("<option value=" + rset.getString("job_id") + ">" + rset.getString("job_title") + "</option> " ); }
ListBoxコンポーネントが選択された状態のまま、「構造」ウィンドウで「ジョブ」フィールドにスクロールします。selectキーワードの下のハードコードされたオプションのリストを確認します。各オプションを削除しますが、スクリプトレットは保持されることを確認します。
ここで、アプリケーションを実行し、クリックして新しい従業員を挿入し、リストを使用して選択可能な職務のリストを表示します。図6-4に、ブラウザでの動的な職務のリストを示します。
|
![]() Copyright © 2007 Oracle Corporation. All Rights Reserved. |
|