アプリケーション開発の手引き

Java での SQL ステートメントの組み込み

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 表に引き数を挿入します。メソッド本体は、ホスト変数 xy、および 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 値を指します。

SQLJ におけるイテレーターの動作の宣言

データを表から検索する 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);

すると、変換しコンパイルされたイテレーターを別のソース・ファイルで使用することができます。イテレーターを使用するには、次のようにします。

  1. 生成されたイテレーター・クラスのインスタンスを宣言する
  2. 位置指定された UPDATE または DELETE の SELECT ステートメントをイテレーター・インスタンスに割り当てる
  3. イテレーターを使用して、位置指定された UPDATE または DELETE ステートメントを実行する

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
      }
   }

注:

  1. (1) この SQLJ 文節は SELECT ステートメントを実行し、 SELECT ステートメントの結果テーブルを含むイテレーター・オブジェクトを構成してから、変数 deliter にイテレーター・オブジェクトを割り当てます。

  2. (2) このステートメントは、次に削除される行にイテレーターを位置指定します。

  3. (3) この SQLJ 文節は、位置指定された DELETE を実行します。

SQLJ の例: App.sqlj

次の SQLJ アプリケーション、 App.sqlj では、静的 SQL を使用して DB2 サンプル・データベースの EMPLOYEE 表からデータを検索および更新します。

  1. イテレーターを宣言する。 この節では、次の 2 つのタイプのイテレーターを宣言します。

    App_Cursor1
    列データ・タイプおよび名前を宣言し、列名に従って列の値を戻します (列名指定バインド)。

    App_Cursor2
    列データ・タイプおよび名前を宣言し、列位置ごとに列の値を戻します (列位置指定バインド)。
  2. イテレーターを初期化する。 イテレーター・オブジェクト cursor1 は、照会の結果を使用して初期化されます。照会の結果は、cursor1 に保管されます。
  3. イテレーターを次の行に拡張する。 cursor1.next() メソッドは、検索する行がなくなると、ブール false を戻します。
  4. データを移動する。 指定されたアクセス機構メソッド empno() は、現在の行に empno という列の値を戻します。指定されたアクセス機構メソッド firstnme() は、現在の行に firstnme という列の値を戻します。
  5. ホスト変数に渡すデータを SELECT する。 SELECT ステートメントは、表の行数をホスト変数 count1 に渡します。
  6. イテレーターを初期化する。 イテレーター・オブジェクト cursor2 は、照会の結果を使用して初期化されます。照会の結果は、cursor2 に保管されます。
  7. データを取り出す。 FETCH ステートメントは、 ByPos カーソルで宣言された最初の列の現行の値を結果テーブルからホスト変数 str2 に戻します。
  8. FETCH..INTO ステートメントが正常に実行されたことを検査する。 イテレーターが行に位置指定されない場合、つまり行の取り出しの最後の試みが失敗した場合、 endFetch() メソッドはブール true を戻します。行の取り出しの最後の試みが成功した場合には、 endFetch() メソッドは false を戻します。 next() メソッドが呼び出されると、 DB2 は行を取り出そうとします。 FETCH...INTO ステートメントは、暗黙的に next() メソッドを呼び出します。
  9. イテレーターをクローズする。 close() メソッドは、イテレーターによって保留にされているリソースを解放します。システム・リソースが適宜解放されるようにするためには、イテレーターを明示的にクローズする必要があります。

JDBC の例: App.sqlj
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();
   }
 }
}

Java のホスト変数

組み込み 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}

SQLJ におけるストアード・プロシージャーおよび関数への呼び出し

データベースには、ストアード・プロシージャー、ユーザー定義関数、およびユーザー定義メソッド が入っています。ストアード・プロシージャー、ユーザー定義関数、およびユーザー定義メソッドは、データベースで実行する名前付きスキーマです。 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) ) };
       }

SQLJ プログラムのコンパイルと実行

プログラム名 MyClass の SQLJ プログラムを実行するには、以下のようにします。

  1. Java ソース・コードを組み込み SQL で変換して、 Java ソース・コード MyClass.java およびプロファイル MyClass_SJProfile0.serMyClass_SJProfile1.ser、... (それぞれの接続コンテキストごとに 1 つのプロファイル) を生成します。
       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 変換プログラムは変換されたソース・コードをクラス・ファイルに自動的にコンパイルします。

  2. 生成されたプロファイルに DB2 SQLJ カスタマイザーをインストールし、 DB2 データベース dbname に DB2 パッケージを作成します。
       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
       ...
    
  3. SQLJ プログラムを実行します。
       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 変換プログラム・オプション

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
       ...


[ ページのトップ | 前ページ | 次ページ | 目次 | 索引 ]