SQLJ での静的 SQL ステートメントは、 SQLJ 文節 に表示されます。 SQLJ 文節は、Java プログラム内の SQL ステートメントがデータベースと通信するための機構です。
SQLJ 変換プログラムは以下のような構造になっているため、 SQLJ 文節および SQL ステートメントを認識します。
最も単純な SQLJ 文節は、 実行可能文節 であり、トークン #sql の後に、括弧で囲まれた SQL ステートメントが続いた形で構成されます。たとえば、Java ステートメントが正常に表示されるところでは、必ず以下の SQLJ 文節が表示されます。これは、TAB という名前の表のすべての行を削除することを目的としています。
#sql { DELETE FROM TAB };
SQLJ 実行可能文節では、括弧の中に表示されるトークンは、ホスト変数を除けば SQL トークンです。ホスト変数はすべてコロン文字で区別されているため、変換プログラムはこれを識別できます。 SQL トークンは、SQLJ 実行可能文節の括弧の外側には表示されません。たとえば、以下の Java メソッドでは SQL 表に引き数を挿入します。メソッド本体は、ホスト変数 x、y、および z を含む SQLJ 実行可能文節で構成されています。
void m (int x, String y, float z) throws SQLException { #sql { INSERT INTO TAB1 VALUES (:x, :y, :z) }; }
一般に、SQL トークンは大文字小文字を区別しないため (ただし、二重引用符で区切られた識別子を除く)、大文字だけ、小文字だけ、または大文字と小文字を混ぜて使用することができます。一方、Java トークンは大文字小文字を区別します。違いをわかりやすくするために、例では、大文字小文字を区別しない SQL トークンは大文字で、 Java トークンは小文字または大文字と小文字の混合で表示します。この章全体では、小文字の null は Java の "null" 値を指し、大文字の NULL は SQL の null 値を指します。
データを表から検索する SQL ステートメントとは異なり位置指定された UPDATE および DELETE 操作を実行するアプリケーションや、 holdability または returnability 属性を指定したイテレーターを使用するアプリケーションには、 2 つの Java ソース・ファイルが必要です。 1 つのソース・ファイルでイテレーターを public と宣言します。適当であれば、with および implements 節を付加します。
holdability または returnability 属性の値を設定するには、対応する属性用の with 節を使用してイテレーターを宣言する必要があります。以下の例では、holdability 属性を、イテレーター WithHoldCurs に応じて true に設定します。
#sql public iterator WithHoldCurs with (holdability=true) (String EmpNo);
位置指定した更新を実行するイテレーターには、 sqlj.runtime.ForUpdate インターフェースをインプリメントする implements 節が必要です。たとえば、次のようなイテレーター DelByName を file1.sqlj で宣言するとします。
#sql public iterator DelByName implements sqlj.runtime.ForUpdate(String EmpNo);
すると、変換しコンパイルされたイテレーターを別のソース・ファイルで使用することができます。イテレーターを使用するには、次のようにします。
file2.sqlj の位置指定された DELETE に DelByName を使用するには、 位置指定されたイテレーターを使用して行を削除するにあるようなステートメントを実行してください。
{ DelByName deliter; // Declare object of DelByName class String enum; (1) #sql deliter = { SELECT EMPNO FROM EMP WHERE WORKDEPT='D11'}; while (deliter.next()) { (2) enum = deliter.EmpNo(); // Get value from result table (3) #sql { DELETE WHERE CURRENT OF :deliter }; // Delete row where cursor is positioned } }
注:
次の SQLJ アプリケーション、 App.sqlj では、静的 SQL を使用して DB2 サンプル・データベースの EMPLOYEE 表からデータを検索および更新します。
import java.sql.*; import sqlj.runtime.*; import sqlj.runtime.ref.*; #sql iterator App_Cursor1 (String empno, String firstnme) ; (1) #sql iterator App_Cursor2 (String) ; class App { /********************** ** Register Driver ** **********************/ static { try { Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance(); } catch (Exception e) { e.printStackTrace(); } } /******************** ** Main ** ********************/ public static void main(String argv[]) { try { App_Cursor1 cursor1; App_Cursor2 cursor2; String str1 = null; String str2 = null; long count1; // URL is jdbc:db2:dbname String url = "jdbc:db2:sample"; DefaultContext ctx = DefaultContext.getDefaultContext(); if (ctx == null) { try { // connect with default id/password Connection con = DriverManager.getConnection(url); con.setAutoCommit(false); ctx = new DefaultContext(con); } catch (SQLException e) { System.out.println("Error: could not get a default context"); System.err.println(e) ; System.exit(1); } DefaultContext.setDefaultContext(ctx); } // retrieve data from the database System.out.println("Retrieve some data from the database."); #sql cursor1 = {SELECT empno, firstnme FROM employee}; (2) // display the result set // cursor1.next() returns false when there are no more rows System.out.println("Received results:"); while (cursor1.next()) (3) { str1 = cursor1.empno(); (4) str2 = cursor1.firstnme(); System.out.print (" empno= " + str1); System.out.print (" firstname= " + str2); System.out.print (""); } cursor1.close(); (9) // retrieve number of employee from the database #sql { SELECT count(*) into :count1 FROM employee }; (5) if (1 == count1) System.out.println ("There is 1 row in employee table"); else System.out.println ("There are " + count1 + " rows in employee table"); // update the database System.out.println("Update the database. "); #sql { UPDATE employee SET firstnme = 'SHILI' WHERE empno = '000010' }; // retrieve the updated data from the database System.out.println("Retrieve the updated data from the database."); str1 = "000010"; #sql cursor2 = {SELECT firstnme FROM employee WHERE empno = :str1}; (6) // display the result set // cursor2.next() returns false when there are no more rows System.out.println("Received results:"); while (true) { #sql { FETCH :cursor2 INTO :str2 }; (7) if (cursor2.endFetch()) break; (8) System.out.print (" empno= " + str1); System.out.print (" firstname= " + str2); System.out.print (""); } cursor2.close(); (9) // rollback the update System.out.println("Rollback the update."); #sql { ROLLBACK work }; System.out.println("Rollback done."); } catch( Exception e ) { e.printStackTrace(); } } }
組み込み SQL ステートメントへの引き数は、 ホスト変数 を介して渡されます。ホスト変数とは、SQL ステートメントに表示されるホスト言語の変数です。ホスト変数は、次の 3 つの部分に分けられます。
Java プログラムでは Java 識別子を評価しても副次作用がないため、 SQLJ 文節を置換するために生成される Java コードに Java 識別子が何度も表示されることがあります。
次の照会には、ホスト変数 :x が含まれています。これは、照会を含む効力範囲に表示される Java 変数、フィールド、またはパラメーター x です。
SELECT COL1, COL2 FROM TABLE1 WHERE :x > COL3
複合 SQL で指定されるホスト変数はすべて、省略時の入力ホスト変数です。これを出力ホスト変数としてマークするには、ホスト変数の前にパラメーター・モード識別子 OUT または INOUT を指定する必要があります。以下はその例です。
#sql {begin compound atomic static select count(*) into :OUT count1 from employee; end compound}
データベースには、ストアード・プロシージャー、ユーザー定義関数、およびユーザー定義メソッド が入っています。ストアード・プロシージャー、ユーザー定義関数、およびユーザー定義メソッドは、データベースで実行する名前付きスキーマです。 Java ステートメントとして表示される SQLJ 実行可能文節は、以下のようにして CALL ステートメントを使用してストアード・プロシージャーを呼び出します。
#sql { CALL SOME_PROC(:INOUT myarg) };
ストアード・プロシージャーには、IN、OUT、または INOUT パラメーターがあります。上記の場合、ホスト変数 myarg の値は、その文節を実行すると変更されます。 SQLJ 実行可能節は、SQL VALUES 構成を用いて関数を呼び出します。たとえば、整数を戻す関数 F があるとします。以下の例では、関数を呼び出して、その結果を Java ローカル変数 x に割り当てます。
{ int x; #sql x = { VALUES( F(34) ) }; }
プログラム名 MyClass の SQLJ プログラムを実行するには、以下のようにします。
sqlj MyClass.sqlj
sqlj.properties ファイルを指定せずに SQLJ 変換プログラムを使用すると、変換プログラムは以下の値を使用します。
sqlj.url=jdbc:db2:sample sqlj.driver=COM.ibm.db2.jdbc.app.DB2Driver sqlj.online=sqlj.semantics.JdbcChecker sqlj.offline=sqlj.semantics.OfflineChecker
sqlj.properties ファイルを指定する場合は、以下のオプションが設定されていることを確認してください。
sqlj.url=jdbc:db2:dbname sqlj.driver=COM.ibm.db2.jdbc.app.DB2Driver sqlj.online=sqlj.semantics.JdbcChecker sqlj.offline=sqlj.semantics.OfflineChecker
ここで、dbname はデータベースの名前です。また、コマンド行でこれらのオプションを指定することもできます。たとえば、MyClass の変換時にデータベース mydata を指定するには、次のコマンドを発行できます。
sqlj -url=jdbc:db2:mydata MyClass.sqlj
-compile=false 節でコンパイル・オプションの設定を明示的にオフにしないかぎり、 SQLJ 変換プログラムは変換されたソース・コードをクラス・ファイルに自動的にコンパイルします。
db2profc -user=user-name -password=user-password -url=jdbc:db2:dbname -prepoptions="bindfile using MyClass0.bnd package using MyClass0" MyClass_SJProfile0.ser db2profc -user=user-name -password=user-password -url=jdbc:db2:dbname -prepoptions="bindfile using MyClass1.bnd package using MyClass1" MyClass_SJProfile1.ser ...
java MyClass
変換プログラムは、 SQLJ プロファイルがカスタマイズされるデータベースの SQL 構文を生成します。たとえば、以下のようにします。
i = { VALUES ( F(:x) ) };
が SQLJ 変換プログラムによって変換され、
? = VALUES (F (?))
として、生成されるプロファイルに保管されます。 DB2 ユニバーサル・データベースに接続すると、 DB2 は、VALUE ステートメントを次のようにカスタマイズします。
VALUES(F(?)) INTO ?
一方、DB2 ユニバーサル・データベース (OS/390 版) データベースに接続すると、 DB2 は VALUE ステートメントを次のようにカスタマイズします。
SELECT F(?) INTO ? FROM SYSIBM.SYSDUMMY1
DB2 ユニバーサル・データベースに対して DB2 SQLJ プロファイル・カスタマイザー db2profc を実行して、バインド・ファイルを生成する場合、バインド・ファイルに VALUES 文節があるときには、バインド・ファイルを使用して DB2 (OS/390 版) データベースにバインドすることはできません。これは、DB2 (OS/390 版) データベースに対してバインド・ファイルを生成し、これを DB2 ユニバーサル・データベースにバインドしようとする場合にも適用されます。
DB2 SQLJ プログラムの作成および実行の詳細については、 アプリケーション構築の手引き を参照してください。
SQLJ 変換プログラムは、 DB2 PRECOMPILE コマンドと同じプリコンパイル・オプションをサポートします。ただし、以下のものは例外です。
CONNECT DISCONNECT DYNAMICRULES NOLINEMACRO OPTLEVEL OUTPUT SQLCA SQLFLAG SQLRULES SYNCPOINT TARGET WCHARTYPE
SQLJ 変換プログラムによって生成されたプロファイルの目次を平文で印刷するには、次のようにして profp ユーティリティーを使用します。
profp MyClass_SJProfile0.ser profp MyClass_SJProfile1.ser ...
DB2 でカスタマイズされたプロファイルのバージョンの目次を平文で印刷するには、次のようにして db2profp ユーティリティーを使用します。ここで、dbname はデータベースの名前です。
db2profp -user=user-name -password=user-password -url=jdbc:db2:dbname MyClass_SJProfile0.ser db2profp -user=user-name -password=user-password -url=jdbc:db2:dbname MyClass_SJProfile1.ser ...