8 高度な言語機能

この章では、アプリケーションのコーディングで利用できるSQLJ言語の高度な機能について説明します。基本事項については、「基本的な言語機能」を参照してください。

次の内容について説明します。

接続コンテキスト

SQLJでは接続コンテキストの概念がサポートされているため、様々なSQLエンティティ・セットで強い型指定の接続を使用できます。接続コンテキストは、特定のSQLエンティティ・セット(表、ビュー、ストアド・プロシージャなど)に関連付けられているといえます。SQLJでは、追加の接続コンテキスト・クラスを宣言することにより、特定のSQLエンティティ・セットを使用する接続で各クラスを使用できるようになります。1つの接続コンテキスト・クラスに対してインスタンスがいくつか存在する場合、これらのインスタンスは、同じ物理エンティティを使用したり、同じスキーマに接続する必要はありませんが、少なくとも名前およびデータ型が同じエンティティのセットを使用します。

関連項目:

1つのSQLエンティティ・セットと1つの接続コンテキスト・クラスのみを使用する場合に重点を置いた、接続の基本に関する概要は、「接続の際の考慮事項」を参照してください。

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

接続コンテキストの概要

アプリケーションで様々なSQLエンティティ・セットを使用する場合は、通常、1つ以上の追加の接続コンテキスト・クラスを宣言して使用することになります(「SQLJ宣言の概要」を参照)。各接続コンテキスト・クラスは、相互に関連するSQLエンティティの特定のセットに使用可能であり、これは特定の接続コンテキスト・クラスを使用して定義した接続はすべて、同じ名前および同じデータ型を持つ表、ビュー、ストアド・プロシージャなどを使用することを意味します。

たとえば、SQLエンティティ・セットの例として、人事(HR)部で使用する表とストアド・プロシージャのセットがあるとします。EMPLOYEESおよびDEPARTMENTS表と、CHANGE_DEPTおよびUPDATE_HEALTH_PLANストアド・プロシージャが使用されることが考えられます。別のSQLエンティティ・セットとして、財務部で使用する表およびプロシージャのセットがあるとすると、この場合は、EMPS表(人事部で使用する従業員表とは異なる表)、およびGIVE_RAISEストアド・プロシージャとCHANGE_WITHHOLDINGストアド・プロシージャで構成されると考えられます。

SQLエンティティへの接続コンテキスト・クラスをカスタマイズすると、オンラインのセマンティクス・チェックの精度が上がります。オンライン・チェックでは、SQLJ文中にあるSQLエンティティのうち、指定した接続コンテキスト・クラスを使用する各エンティティが、基本スキーマにある変換用SQLエンティティと同じかどうかが検証されます。基本スキーマは、SQLJの接続先データベース・アカウントの1つで、特定の接続コンテキスト・クラスを使用したSQLJ文に対するオンライン・チェックに使用されます。トランスレータへの基本スキーマを指定するには、SQLJコマンドラインから-user-passwordおよび-urlオプションを使用します。基本スキーマは、実行時にアプリケーションで使用されるアカウントと同じにしてもしなくてもかまいません。

関連性が薄いと思われる幅広いSQLエンティティの1つのグループを複数のSQLJ文で使用し、これらのSQL文に対して1つの接続コンテキスト・クラスのみを使用する場合は、汎用性の高い基本スキーマを作成する必要があります。この基本スキーマには、これら文すべてで使用される表、ビューおよびストアド・プロシージャが、すべて含まれる必要があります。これに対し、特定の接続コンテキスト・クラスを使用するすべてのSQLJ文で、相互に関連すると思われる限られたSQLエンティティのセットを使用する場合は、より限定的な特定の基本スキーマを作成できます。これによって、より詳細で有効なセマンティクス・チェックが可能になります。

注意:

  • 接続コンテキスト・クラスの宣言では、宣言される接続コンテキスト・クラスで使用するSQLエンティティのセットは定義されないため、異なる関連しないエンティティのセットを使用する接続に対しても、同じ接続コンテキスト・クラスが使用可能になる点に注意してください。接続コンテキスト・クラスをどのように使用するかは自由に決められます。特定の接続コンテキスト・クラスで使用可能なSQLエンティティを制限するのは、基本スキーマで使用可能なエンティティのセット(変換時にオンライン・セマンティクス・チェックを使用する場合)、および実行時の接続先のスキーマで使用可能なエンティティのセット(その接続コンテキスト・クラスのインスタンスを使用)です。

  • アプリケーションで使用するSQL修飾名(HR.EMPLOYEESなど)に、エンティティが存在するスキーマを指定した場合、基本スキーマ(オンライン・チェックを使用する場合)およびランタイム・スキーマからリソースへのアクセスには、完全修飾名を指定する必要があります。

  • ベンダー各社のデータベースへの接続にも、接続コンテキスト・クラスを使用可能です。ただし、同じ名前や互換性のあるデータ型を使用して、接続先スキーマのエンティティにアクセスできる場合に限られます。

接続コンテキストのロジスティクス

接続コンテキスト・クラスを宣言すると、SQLJトランスレータによって生成されたコードにクラスが定義されます。宣言した接続コンテキスト・クラスの他に、デフォルトの接続コンテキスト・クラスが定義されます。

sqlj.runtime.ref.DefaultContext

接続コンテキスト・インスタンスを作成する場合は、特定のスキーマおよびSQL操作が実行される特定のセッションとトランザクションを指定します。その場合、接続コンテキスト・クラスのコンストラクタへの入力パラメータとして、ユーザー名、パスワードおよびデータベースURLを指定します。接続コンテキスト・インスタンスでは、そのセッションで実行されたSQL操作が管理されます。

SQLJ文ごとに、使用する接続コンテキスト・インスタンスを指定します。次の例では、2つの異種スキーマに接続するための接続コンテキスト・クラスMyContextの基本的な宣言と使用方法を示します。通常の使用では、この両スキーマのSQLエンティティは、それぞれ名前とデータ型が同じであることが前提になります。

#sql context MyContext;

...
MyContext mctx1 = new MyContext
  ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", false);
MyContext mctx2 =  new MyContext
  ("jdbc:oracle:thin@localhost:5221/myservice", "brian", "mypasswd", false);

接続コンテキスト・クラスのコンストラクタは、ブール型の自動コミット・パラメータを指定する点に注意してください。また、同じスキーマに、異なる接続コンテキスト・インスタンスで接続できる点にも注意してください。前の例では、必要であれば、mctx1mctx2の両方でHR/hrを指定できます。ただし、実行時には、一方の接続コンテキスト・インスタンスは、他方のインスタンスが行ったデータベースの変更を、それがコミットされるまで認識することができません。ただし、両方の接続コンテキスト・インスタンスが同一の基本Java Database Connectivity (JDBC)接続インスタンスから作成されている場合は、例外です。どの接続コンテキスト・クラスでも、そのコンストラクタの1つは入力としてJDBC接続インスタンスを使用します。

接続コンテキスト・クラスの宣言と使用方法

ここでは、接続コンテキスト・クラスを宣言し、クラスのインスタンスを使用してデータベース接続を定義する方法について、例を示して説明します。

接続コンテキスト・クラスのコンストラクタは、次の入力パラメータ・セットのいずれかをとる場合、データベース・スキーマへの接続を開きます(DefaultContextクラスの場合)。

  • URL (String)、ユーザー名(String)、パスワード(String)、自動コミット(boolean)

  • URL(String)、java.util.Propertiesオブジェクト、自動コミット(boolean)

  • URL(ユーザー名やパスワードなど、接続の詳細を指定するString)、自動コミットの設定(boolean)

  • JDBC接続オブジェクト(Connection)

  • SQLJ接続コンテキスト・オブジェクト

注意:

  • JDBC接続オブジェクトを引数とするコンストラクタを使用する場合は、接続コンテキスト・インスタンスをNULL JDBCで初期化しないでください。

  • 自動コミットの設定では、SQL操作が自動的にコミットされるかどうかが定義されます。詳細は、「基本的なトランザクション制御」を参照してください。

  • 接続コンテキスト・クラスがデータ・ソースwith句で宣言されている場合は、別の種類のコンストラクタが実装されます。詳細は、「標準データ・ソースのサポート」を参照してください。

接続コンテキスト・クラスの宣言

次に示す宣言では、接続コンテキスト・クラスが生成されます。

#sql context OrderEntryCtx <implements_clause> <with_clause>; 

これを実行すると、SQLJトランスレータで、sqlj.runtime.ConnectionContextインタフェースを実装し、ベース・クラス(おそらく抽象クラスで、ConnectionContextインタフェースを実装する)を拡張するクラスが生成されます。このベース・クラスは、使用する特定のSQLJ実装の1つの機能となるものです。implements句とwith句の使用は任意で、前者は実装する追加のインタフェースを指定し、後者は定義および初期化する変数を指定します。

次は、SQLJトランスレータによって生成される内容です(メソッドの実装は省略してあります)。

class OrderEntryCtx implements sqlj.runtime.ConnectionContext 
      extends ...
{ 
   public OrderEntryCtx(String url, Properties info, boolean autocommit)
          throws SQLException {...} 
   public OrderEntryCtx(String url, boolean autocommit) 
          throws SQLException {...}   
   public OrderEntryCtx(String url, String user, String password, 
          boolean autocommit) throws SQLException {...} 
   public OrderEntryCtx(Connection conn) throws SQLException {...} 
   public OrderEntryCtx(ConnectionContext other) throws SQLException {...} 

   public static OrderEntryCtx getDefaultContext() {...} 
   public static void setDefaultContext(OrderEntryCtx ctx) {...} 
} 

接続コンテキスト・インスタンスの作成

前述の例を使用して、次の構文でOrderEntryCtxクラスをインスタンス化します。

OrderEntryCtx myOrderConn = new OrderEntryCtx
                            (url, username, password, autocommit);

次に例を示します。

OrderEntryCtx myOrderConn = new OrderEntryCtx
  ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", true);

このためには、DefaultContextクラスのインスタンス化と同じ方法を使用します。DefaultContextを含むすべての接続コンテキスト・クラスでは、それぞれコンストラクタのシグネチャが同じです。

注意:

SQLJ句での接続コンテキスト・インスタンスの指定

次は、基本的なSQLJ文の構文です。

#sql <[<conn><, ><exec>]> { SQL operation };

接続コンテキスト・インスタンスは、#sqlトークンの後ろの大カッコの内側に指定します。次のSQLJ文の例では、前述の例で使用したmyOrderConnを接続コンテキスト・インスタンスとして使用します。

#sql [myOrderConn] { UPDATE TAB2 SET COL1 = :w WHERE :v < COL2 };

DefaultContextクラスや宣言済の接続コンテキスト・クラスのインスタンスは、このようにして指定します。

接続コンテキスト・インスタンスの終了

処理が終了したら、すべての接続コンテキスト・インスタンスを閉じることをお薦めします。「接続の終了」DefaultContextクラスの説明にもあるように、各接続コンテキスト・クラスにはclose()メソッドが含まれます。

接続コンテキスト・インスタンスのうち、元の接続を他の接続インスタンスと共有するものについては、元の接続を開いたままにしておく必要があります。

複数の接続コンテキストの例

次は、複数の接続コンテキストを使用するSQLJアプリケーションの例です。この例では、一方のSQLエンティティにはDefaultContextクラスのインスタンスを暗黙的に使用し、もう一方のSQLエンティティには、宣言済のDeptContext接続コンテキスト・クラスのインスタンスを使用しています。

この例では、静的なOracle.connect()メソッドを使用してデフォルト接続を確立し、静的なOracle.getConnection()メソッドを使用して追加の接続を作成して、別のDefaultContextインスタンスをDeptContextコンストラクタに渡します。前述のとおり、これは、SQLJ接続コンテキスト・インスタンスを作成できるいくつかの方法のうちの1つです。

import java.sql.SQLException;
import oracle.sqlj.runtime.Oracle;

// declare a new context class for obtaining departments
#sql context DeptContext;

#sql iterator Employees (String ename, int deptno);

class MultiSchemaDemo 
{
  public static void main(String[] args) throws SQLException 
  {
    // set the default connection to the URL, user, and password
    // specified in your connect.properties file
    Oracle.connect(MultiSchemaDemo.class, "connect.properties");

    // create a context for querying department info using
    // a second connection
    DeptContext deptCtx = 
      new DeptContext(Oracle.getConnection(MultiSchemaDemo.class, 
                     "connect.properties"));

    new MultiSchemaDemo().printEmployees(deptCtx);
    deptCtx.close();
  }

  // performs a join on deptno field of two tables accessed from
  // different connections. 
  void printEmployees(DeptContext deptCtx) throws SQLException
  {
    // obtain the employees from the default context
    Employees emps;
    #sql emps = { SELECT first_name, department_id FROM employees }; 

    // for each employee, obtain the department name
    // using the dept table connection context
    while (emps.next()) {
      String dname;
      int deptno = emps.deptno();
      #sql [deptCtx] { 
        SELECT dname INTO :dname FROM departments WHERE department_id = :deptno
      };
      System.out.println("employee: " +emps.ename() +
                         ", department: " + dname);
    }
    emps.close();
  }
}

接続コンテキスト・クラスの実装と機能

ここでは、SQLJで接続コンテキスト・クラス(DefaultContextクラスなど)を実装する方法と、これらのクラスに含まれる主なメソッドについて説明します。前述のとおり、DefaultContextクラスおよび生成されるすべての接続コンテキスト・クラスは、ConnectionContextインタフェースを実装します。

注意:

接続コンテキスト・クラスの拡張はSQLJ仕様で許可されていないので、Oracle SQLJ実装での拡張はサポートされていません。

ConnectionContextインタフェース

各接続コンテキスト・クラスには、sqlj.runtime.ConnectionContextインタフェースが実装されています。

このインタフェースで指定される基本メソッドは、次のとおりです。

  • close(boolean CLOSE_CONNECTION/KEEP_CONNECTION): その接続を保持するために使用されているすべてのリソースを解放し、オープンされている接続プロファイルをすべてクローズするメソッド。基になるJDBC接続を閉じるかどうかは、CLOSE_CONNECTIONまたはKEEP_CONNECTIONが指定されているかによります。これらは、ConnectionContextインタフェース特有のstaticブール定数です。

  • getConnection(): その接続コンテキスト・インスタンスの基になるJDBC接続オブジェクトを戻すメソッド。

  • getExecutionContext(): その接続コンテキスト・インスタンスのデフォルトのExecutionContextインスタンスを戻すメソッド。

接続コンテキスト・クラスの追加メソッド

ConnectionContextインスタンスで定義されているメソッドの他に、次のメソッドが各接続コンテキスト・クラスで定義されています。

  • YourCtxClass getDefaultContext(): これは静的メソッドで、特定の接続コンテキスト・クラスのデフォルトの接続コンテキスト・インスタンスを戻します。

  • setDefaultContext(YourCtxClass connctxinstance): これはstaticメソッドで、クラスのデフォルトの接続コンテキスト・インスタンスとして、任意の接続コンテキスト・インスタンスを定義します。

デフォルト接続として、DefaultContextクラスのみのインスタンスを使用することも可能ですが、setDefaultContext()メソッドでクラスのデフォルト・コンテキストとして宣言された接続コンテキスト・クラスのインスタンスを指定するのも便利です。このようにすると、特定のクラスのgetDefaultContext()メソッドを使用して、便利に取得することができます。たとえば、SQLJ実行文の接続コンテキスト・インタフェースを次のように指定できます。

#sql context MyContext;

...
MyContext myctx1 = new MyContext(url, user, password, autocommit);
...
MyContext.setDefaultContext(myctx1);
...
#sql [MyContext.getDefaultContext()] { SQL operations };
...

さらに、各接続コンテキスト・クラスでは、SQLJ文のキャッシングを制御するメソッドを定義します。次に示すstaticメソッドです。

  • setDefaultStmtCacheSize(int)

  • int getDefaultStmtCacheSize()

次のインスタンス・メソッドがあります。

  • setStmtCacheSize(int)

  • int getStmtCacheSize()

デフォルトで、文のキャッシングを使用できます。

接続コンテキスト宣言でのIMPLEMENTS句の使用

接続コンテキスト宣言にインタフェースを実装する場合があります。たとえば、接続コンテキスト・クラスの機能のサブセットのみを公開するインタフェースを定義する必要があるとします。さらに具体的には、クラスで必要なのはgetConnection()の機能のみで、接続コンテキスト・クラスの他の機能は不要だとします。

例として、HasConnectionというインタフェースを作成し、getConnection()メソッドは指定しますが、接続コンテキスト・クラスに一般にみられる他のメソッドは指定しません。次に、接続コンテキスト・クラスを宣言し、getConnection()機能のみを公開することができますが、それには、接続コンテキスト・インスタンスを、宣言済の接続コンテキスト・クラスの型を持つ変数ではなく、HasConnection型の変数に割り当てます。

HasConnectionmypackageパッケージにあると想定して、次のように宣言します。

#sql public context MyContext implements mypackage.HasConnection;

次のように、接続インスタンスをインスタンス化します。

HasConnection myConn = new MyContext (url, username, password, autocommit);

次に例を示します。

HasConnection myConn = new MyContext 
   ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", true);

接続コンテキストのセマンティクス・チェック

SQLJの優れた機能の1つは接続の強い型指定であり、通常は、それぞれの接続コンテキスト・クラスが、特定の相互に関連するSQLエンティティ・セットの操作に使用されます。これは、1つのクラスのすべての接続インスタンスが同じ物理エンティティを使用するという意味ではありません。そうではなく、同じプロパティを持つエンティティ、つまり表およびビューに関連付けられる名前と権限、その行のデータ型、ストアド・プロシージャの名前と定義などが同じエンティティを使用するということです。この強い型指定によって、SQLJセマンティクス・チェックは、使用中のデータベース接続に関して、SQL操作が正しく使用されていることを変換時に検証できます。

変換時にオンライン・セマンティクス・チェックを使用するには、接続コンテキスト・クラスごとに、適切なSQLエンティティ・セットを含むサンプル・スキーマを提供します。これらのサンプル・スキーマは、基本スキーマと呼ばれます。SQLJ -user-passwordおよび-urlオプションを適切に組み合わせて、基本スキーマを提供します。次に示す2つの例のうち、1つはDefaultContextクラスの場合、もう1つは宣言済の接続コンテキスト・クラスで、-userオプションを使用してユーザー、パスワードおよびURLがすべて指定されている場合です。

-user=HR/hr@jdbc:oracle:oci:@
-user@MyContext=HR/hr@jdbc:oracle:oci:@

トランスレータは、セマンティクス・チェック時に、特定の接続コンテキスト・クラスを指定された基本スキーマに接続し、次の処理を行います。

  • 接続コンテキスト・クラスのインスタンスを指定するコード中の各SQLJ文を検証し、そのSQL操作(どの表にアクセスし、どのストアド・プロシージャを使用するのか、など)をチェックします。

  • SQL操作におけるエンティティが基本スキーマにある既存のエンティティと一致することを検証します。

ユーザーは、適切なランタイム・スキーマを基本スキーマとして選択する必要があります。具体的には、基本スキーマの表、ビュー、ストアド・ファンクションおよびストアド・プロシージャの各名前とデータ型を、SQL操作で使用されているものと同一にし、各権限を正しく設定する必要があります。

変換時にいずれかの接続コンテキスト・クラスに適切な基本スキーマを使用できない場合は、その接続コンテキスト・クラスにSQLJトランスレータ・オプションを指定する必要はありません。その場合、その接続コンテキスト・クラスの接続オブジェクトを指定するSQLJ文では、可能な範囲でセマンティクス・チェックが行われます。

注意:

トランスレータ・オプション設定に指定する基本スキーマでは、実行時に使用されるスキーマは指定されません。基本スキーマからトランスレータに渡されるのは、SQLJ実行文で使用するエンティティと照合するための一組のSQLエンティティのみです。

標準データ・ソースのサポート

JDBC 2.0拡張Application Programming Interface(API)では、DriverManagerにかわって、データ・ソースおよびJava Naming and Directory Interface(JNDI)を使用したJDBC接続の取得が仕様化されました。JNDI名を検索することによって、データベース接続を確立できます。この名前は、通常、Graphical User Interface(GUI)のJavaBeansデプロイメント・ツールを通してインストールされるjavax.sql.DataSourceオブジェクトを通して、プログラム・ランタイムより前に特定のデータベースおよびスキーマに対してバインドされます。この名前は、ディレクトリ・サービスに名前を再バインドするのみでソース・コードを変更せずに、別の物理的接続にバインドされる場合もあります。

SQLJでは、柔軟で移植可能な方法で接続コンテキスト・インスタンスを作成するため、同じメカニズムを使用します。データ・ソースは、JDBC 2.0拡張APIで定義したように、接続プールまたは分散トランザクション・サービスを使用して実装されます。

接続コンテキストとデータ・ソースの関連付け

SQLJでは、通常、データ・ソース名がJDBC接続の象徴名となるのと同じように、接続コンテキスト・クラスを論理的スキーマに関連付けます。データ・ソース名を接続コンテキストの宣言に追加することにより、両方のコンセプトを結合します。次に例を示します。

#sql context EmpCtx with (dataSource="jdbc/EmpDB");

dataSourceプロパティで宣言した接続コンテキスト・クラスには、必ず追加のコンストラクタがあります。このEmpCtxの例では、次のコンストラクタがあります。

  • EmpCtx(): jdbc/EmpDBのデータ・ソースを検索し、接続を取得するためにデータ・ソースでgetConnection()メソッドをコールします。

  • EmpCtx(String user, String password): jdbc/EmpDBのデータ・ソースを検索し、接続を取得するためにデータ・ソースでgetConnection(user,password)メソッドをコールします。

  • EmpCtx(ConnectionContext ctx): 接続を取得するためにctxへ委任します。

dataSourceプロパティで宣言した接続コンテキスト・クラスでは、DriverManagerベースのコンストラクタがいくつか削除されます。このEmpCtxの例では、次のコンストラクタが削除されます。

  • EmpCtx(Connection conn)

  • EmpCtx(String url, String user, String password, boolean autoCommit)

  • EmpCtx(String url, boolean autoCommit)

  • EmpCtx(String url, java.util.Properties info, boolean autoCommit)

  • EmpCtx(String url, boolean autoCommit)

データ・ソース接続の自動コミット・モード

DriverManagerベースのコンストラクタとは異なり、データ・ソース・ベースのコンストラクタには、明示的な自動コミット・パラメータは含まれていません。常に、データ・ソースで定義された自動コミット・モードを使用します。

データ・ソースは、デプロイメント例によってはデフォルトの自動コミット・モードを持つように設定されています。たとえば、通常、サーバーおよび中間層のデータ・ソースでは自動コミットをオフにしています。クライアント上のデータ・ソースは自動コミットをオンにしています。ただし、特定の自動コミット設定を使用してデータ・ソースを設定することも可能です。これで、データ・ソースを特定のアプリケーションやデプロイメント例にあわせて設定できます。これを単一のデータベース/ドライバ設定のみを指定するJDBC URLと比較してください。

プログラムでは、接続コンテキスト・インスタンスの基になるJDBC接続を使用して、既存の自動コミット設定を確認および上書きできます。

注意:

確立する接続の自動コミット状態について次の点に留意してください。

  • Oracleクラスを使用する場合、明示的に指定しないと、自動コミットはオフになります。

  • DefaultContextまたはDriverManager形式のコンストラクタを指定した接続コンテキストを使用する場合、自動コミット設定は、常に明示的に指定する必要があります。

  • データ・ソースのメカニズムを使用する場合、自動コミット設定が基になるデータ・ソースから継承されます。ほとんどの環境では、データ・ソース・オブジェクトはJDBCに基づいており、自動コミット・オプションはオンになっています。予期せぬ動作を防ぐために、常に自動コミット設定を確認してください。

データ・ソースとデフォルト・コンテキストの関連付け

SQLJプログラムがデフォルトの接続コンテキストにアクセスし、かつデフォルトのコンテキストが設定されていない場合、SQLJランタイムは接続を確立するために、デフォルトのSQLJデータ・ソースを使用します。デフォルトのSQLJデータ・ソースは、JNDI名jdbc/defaultDataSourceにバインドされます。

このメカニズムでは、デフォルトのSQLJ接続コンテキストに対するデフォルトのJDBC接続を定義し、インストールするための移植性のある方法を提供します。

データ・ソースのサポートの要件

プログラムでデータ・ソースを使用するには、Java環境でjavax.sql.*パッケージ、javax.naming.*パッケージおよびInitialContextプロバイダを指定する必要があります。後者は、SQLJランタイムがデータ・ソース・オブジェクトを検索できるJNDIコンテキストを取得する際に必要となります。

Oracleで提供するSQLJランタイムはすべて、データ・ソースをサポートしています。ただし、runtime12eeライブラリを使用する場合、CLASSPATH内にjavax.sql.*およびjavax.naming.*がなければ、ランタイムはロードされません。反対に、他のランタイム・ライブラリでは、リフレクションを使用してDataSourceオブジェクトが取得されます。

SQLJ固有のデータ・ソース

Oracle SQLJ実装によるSQLJ固有のデータ・ソースのサポートがruntime12eeライブラリで提供されます。現在、SQLJ固有のデータ・ソースは、サーバー内部ではなく、クライアント側または中間層アプリケーションで使用できます。

SQLJ固有のデータ・ソースは、SQLJ接続コンテキスト・インスタンスを戻すメソッドを使用して、JDBCデータ・ソース機能を拡張します。これによって、JDBC開発者が接続を管理するように、SQLJ開発者が接続コンテキストを管理できるようになります。一般に、SQLJ固有のデータ・ソースの各インタフェースまたは各クラスは、対応する標準JDBCデータ・ソースのインタフェースまたはOracleデータ・ソース・クラスに基づいています。

SQLJデータ・ソースのインタフェース

sqlj.runtime.ConnectionContextFactoryインタフェースは、SQLJデータ・ソース機能の基本インタフェースとして動作します。このインタフェースは、Oracleデータ・ソース専用の一連のインタフェースによって実装され、接続プーリング、接続キャッシングまたは分散トランザクションなどの機能に対するサポートを追加します。

ConnectionContextFactoryインタフェースは、次に示すメソッドを指定して、SQLJ接続コンテキスト・インスタンスを戻します。

  • DefaultContext getDefaultContext()

  • DefaultContext getDefaultContext(boolean autoCommit)

  • DefaultContext getDefaultContext(String user, String password)

  • DefaultContext getDefaultContext(String user, String password, boolean autoCommit)

  • ConnectionContext getContext(Class aContextClass)

  • ConnectionContext getContext(Class aContextClass, boolean autoCommit)

  • ConnectionContext getContext(Class aContextClass, String user, String password)

  • ConnectionContext getContext(Class aContextClass, String user, String password, boolean autoCommit)

getDefaultContextメソッドは、SQLJデフォルト・コンテキストのsqlj.runtime.ref.DefaultContextインタフェースを戻します。getContext()メソッドは、sqlj.runtime.ConnectionContextインスタンスを戻します。具体的には、メソッドのコールに指定されるユーザー宣言の接続コンテキスト・クラスのインスタンスを戻します。

getDefaultContext()およびgetContext()には、接続コンテキスト・インスタンスの基になるJDBC接続に対する接続パラメータ(自動コミット設定、ユーザー設定とパスワード設定、または3つの設定すべて)を指定できるシグネチャがあります。ユーザーとパスワードが未指定の場合は、接続を生成する基となるデータ・ソースからユーザーとパスワードが取得されます。自動コミット設定が未指定の場合は、基になるデータ・ソースの設定が明示的にtrueに指定されていないかぎり、デフォルトはfalseです。

ConnectionContextFactoryを実装する各Oracleデータ・ソース・インタフェースは、基本データ・ソース、接続プーリング・データ・ソース、分散トランザクション(XA)データ・ソースなどに適した機能のメソッドを指定するために、標準のJDBCデータ・ソース・インタフェースも実装しています。SqljDataSourceSqljConnectionPoolDataSourceおよびSqljXADataSourceインタフェースは、sqlj.runtimeパッケージに実装済であり、次のように指定されています。

  • interface SqljDataSource extends javax.sql.DataSource, ConnectionContextFactory { }

  • interface SqljDataSource extends javax.sql.ConnectionPoolDataSource, ConnectionContextFactory { }

  • interface SqljXADataSource extends javax.sql.XADataSource, ConnectionContextFactory { }

SQLJデータ・ソースのクラス

Oracleでは、OracleDataSourceOracleConnectionPoolDataSourceOracleXADataSourceOracleConnectionCacheImplOracleXAConnectionCacheImplおよびOracleOCIConnectionPoolのJDBCデータ・ソースの各クラスに対応するSQLJ固有のクラスを提供しています。

Oracle SQLJ固有のデータ・ソース・クラスは、oracle.sqlj.runtimeおよびoracle.sqlj.runtime.clientの2つのパッケージに格納されています。

oracle.sqlj.runtimeパッケージには、次のクラスが含まれています。

  • class OracleSqljDataSource extends oracle.jdbc.pool.OracleDataSource implements ConnectionContextFactory

注意:

OracleSqljDataSourceクラスはjava.io.Serializableインタフェースを実装します。したがって、このクラスはシリアライズ可能で、Oracle9i Application Server Containers for J2EE(OC4J)などのクラスタ環境で使用できます。

  • class OracleSqljConnectionPoolDataSource extends oracle.jdbc.pool.OracleConnectionPoolDataSource implements ConnectionContextFactory

  • abstract class OracleSqljXADataSource extends oracle.jdbc.xa.OracleXADataSource implements ConnectionContextFactory

  • class OracleSqljConnectionCacheImpl extends oracle.jdbc.pool.OracleConnectonCacheImpl implements ConnectionContextFactory

  • class OracleSqljXAConnectionCacheImpl extends oracle.jdbc.pool.OracleXAConnectonCacheImpl implements ConnectionContextFactory

  • class OracleSqljOCIConnectionPool extends oracle.jdbc.pool.OracleOCIConnectionPool implements ConnectionContextFactory

注意:

  • OracleSqljConnectionCacheImplを使用している場合は、OracleSqljDataSourceに置き換える必要があります。

  • OracleSqljXAConnectionCacheImplを使用している場合は、OracleSqljXADataSourceに置き換える必要があります。

oracle.sqlj.runtime.clientパッケージには、次のクラスが含まれています。

  • class OracleSqljXADataSource extends oracle.jdbc.xa.client.OracleXADataSource implements ConnectionContextFactory

対応するJDBCクラスのかわりに、拡張されたこれらのクラスを使用できます。これらのクラスには、getDefaultContext()およびgetContext()メソッドが含まれています。これらのメソッドをコールする場合は、次の手順が発生します。

  1. 現行のデータ・ソースから新しい論理JDBC接続が取得されます。

  2. 論理接続から接続コンテキスト・インスタンスが作成され、戻されます。

例: SQLJデータ・ソースの使用

中間層の環境で使用される場合、SQLJ固有のデータ・ソースは、JDBCデータ・ソースのようにJNDIの位置にバインドされます。次の例のように、明示的にバインドできます。

//Initialize the data source 
SqljXADataSource sqljDS = new OracleSqljXADataSource(); 
sqljDS.setUser("HR"); 
sqljDS.setPassword("hr"); 
sqljDS.setServerName("myserver"); 
sqljDS.setDatabaseName("orcl"); 
sqljDS.setDataSourceName("jdbc/OracleSqljXADS"); 

//Bind the data source to JNDI 
Context ctx = new InitialContext(); 
ctx.bind("jdbc/OracleSqljXADS");

中間層のOC4J環境の場合、もう1つの方法として、j2ee/home/config/data-sources.xmlファイルの設定によってデータ・ソースをインスタンス化し、JNDIにバインドする方法もあります。たとえば、ファイル内の次の<data-source>要素は、OracleSqljXADataSourceインスタンスを作成し、それをJNDIの位置jdbc/OracleSqljXADSにバインドします。

<data-source 
     class="oracle.sqlj.runtime.OracleSqljXADataSource" 
     name="jdbc/OracleSqljXADS" 
     location="jdbc/OracleSqljXADS" 
     xa-location="jdbc/OracleSqljXADS/xa" 
     username="HR" 
     password="hr" 
     url="jdbc:oracle:thin:@myhost:5221/myservice" 
/>

JNDIの位置にバインドされたSQLJ固有のデータ・ソースは、接続コンテキスト・インスタンスの作成時に検索および使用されます。次のコードは、前述の<data-source>要素の情報を使用して、接続コンテキスト・インスタンス(それぞれDefaultContextインスタンスとユーザー宣言MyCtxクラスのインスタンス)を作成します。

sqlj.runtime.SqljDataSource sqljDS; 
InitialContext initCtx = new InitialContext(); 
sqljDS = (sqlj.runtime.SqljDataSource)initCtx.lookup("jdbc/OracleSqljXADS"); 
// getDefaultContext
DefaultContext ctx = sqljDS.getDefaultContext(); 
// getContext
/* Declare MyCtx connection context class. You could optionally use a "with"
   clause to specify  any desired connection parameters not available 
   through the underlying data source.
*/
#sql public static context MyCtx; 
MyCtx ctx = (MyCtx) sqljDS.getContext(MyCtx.class);

JavaServer Pages用のSQLJ固有の接続JavaBeans

Oracleでは、Java Server Pages(JSP)ページ内部からのデータベース接続用に一連のJavaBeansが実装されました。

Oracle SQLJ実装には、SQLJ JSPページで使用するruntime12eeライブラリのこれらのJavaBeansについて、次の拡張機能が用意されています。

  • oracle.sqlj.runtime.SqljConnBean

  • oracle.sqlj.runtime.SqljConnCacheBean

ConnBeanおよびConnCacheBeanには、JDBC接続オブジェクトを戻すメソッドが含まれています。SqljConnBeanおよびSqljConnCacheBeanは、String型のContextClassと呼ばれるBeanプロパティをサポートし、SQLJ接続コンテキスト・インスタンスを戻すように機能が拡張されています。

注意:

SqljConnBeanクラスはjava.io.Serializableインタフェースを実装します。したがって、このクラスはシリアライズ可能で、OC4Jなどのクラスタ環境で使用できます。

SqljConnBeanおよびSqljConnCacheBeanには、次のメソッドが用意されています。

  • void setContextClass(String contextClassName)

  • String getContextClass()

  • DefaultContext getDefaultContext()

  • ConnectionContext getContext()

ContextClassプロパティは、DefaultContextを使用していない場合に、ユーザー宣言の接続コンテキスト・クラスの名前を指定します。setContextClass()メソッドを介して、このプロパティを設定できます。

接続コンテキスト・インスタンスを取得するには、必要に応じてgetDefaultContext()またはgetContext()を使用します。前者はsqlj.runtime.ref.DefaultContextインスタンスを戻し、後者はsqlj.runtime.ConnectionContextインスタンス、特にContextClassプロパティで指定されたクラスのインスタンス(デフォルトではDefaultContext)を戻します。

ただし、getDefaultContext()メソッドとgetContext()メソッドは、SqljConnBeanSqljConnCacheBeanでは実装が異なることに注意してください。

SqljConnBeanの動作(単純な接続の場合)

SqljConnBeanインスタンスは、常に1つの論理JDBC接続と1つのSQLJ接続コンテキスト・インスタンスのみをラッピングできます。

最初のgetDefaultContext()またはgetContext()メソッドのコールによって、基礎となるJDBC接続に基づいて接続コンテキスト・インスタンスが作成され、戻されます。この接続コンテキスト・インスタンスもSqljConnBeanインスタンスに格納されます。

接続コンテキスト・インスタンスが作成および格納されると、後続のgetDefaultContext()またはgetContext()コールの動作は、格納された接続コンテキストの型によって異なります。また、getContext()の場合は、次のようにContextClassプロパティに指定されている接続コンテキスト型にも依存します。

  • 後続のgetDefaultContext()コールに対して

    • 格納された接続コンテキスト・インスタンスがDefaultContextインスタンスの場合、メソッドはそのインスタンスを戻し続けます。

    • 格納された接続コンテキスト・インスタンスが、DefaultContextインスタンスではない場合、このメソッドは格納された接続コンテキスト・インスタンスを終了し、基になるJDBC接続を再利用して、新しい接続コンテキストをDefaultContextインスタンスとして戻します(前回の接続コンテキスト・タイプに関係なく)。これによって、新規接続コンテキスト・インスタンスがSqljConnBeanインスタンスに格納されます。

  • 後続のgetContext()コールに対して

    • 格納された接続コンテキスト・インスタンスが、ContextClassプロパティの指定と同じ型である場合、メソッドはそのインスタンスを戻し続けます。

    • 格納された接続コンテキスト・インスタンスがContextClassの指定と同じ型ではない場合、このメソッドは格納された接続コンテキスト・インスタンスを終了し、基になるJDBC接続を再利用して、新しい接続コンテキスト・インスタンス(ContextClassで指定したもののインスタンス)を戻します。これによって、新規接続コンテキスト・インスタンスがSqljConnBeanインスタンスに格納されます。

注意:

SqljConnBeanで接続コンテキスト・インスタンスをクローズする場合、KEEP_CONNECTION設定を使用してクローズする、基礎となるJDBC接続はそのまま残ります。

SqljConnCacheBeanの動作(接続キャッシング)

SqljConnBeanとは異なり、SqljConnCacheBean JavaBeanは、getDefaultContext()またはgetContext()の各呼出しに対して、新規論理JDBC接続に基づいて新規接続コンテキスト・インスタンスを作成し、戻します。接続コンテキスト型は、getDefaultContext()コールの場合はDefaultContextgetContext()コールの場合はContextClassプロパティに指定されている型になります。

SqljConnCacheBeanでは、作成した接続コンテキスト・インスタンスを格納しません。

例: SqljConnCacheBeanを使用するSQLJ JSPページ

次のプログラムSQLJSelectInto.sqljspは、SqljConnCacheBeanと、そのContextClass BeanプロパティおよびそのgetContext()メソッドの使用方法の一例です。

<%@ page language="sqlj" 
         import="java.sql.*, oracle.sqlj.runtime.SqljConnCacheBean" %> 
<jsp:useBean id="cbean" class="oracle.sqlj.runtime.SqljConnCacheBean"
             scope="session"> 
     <jsp:setProperty name="cbean" property="User" value="HR"/> 
     <jsp:setProperty name="cbean" property="Password" value="hr"/> 
     <jsp:setProperty name="cbean" property="URL"
                      value="jdbc:oracle:thin:@myhost:5221/myservice"/> 
     <jsp:setProperty name="cbean" property="ContextClass"
                      value="sqlj.runtime.ref.DefaultContext"/> 
</jsp:useBean> 
<HTML> 
<HEAD> <TITLE> The SQLJSelectInto JSP  </TITLE> </HEAD> 
<BODY BGCOLOR=white> 
<% String empno = request.getParameter("employee_id"); 
   if (empno != null) { %> 
      <H3> Employee # <%=empno %> Details: </H3> 
      <% String ename = null;  double sal = 0.0;  String hireDate = null; 
         StringBuffer sb = new StringBuffer(); 
         sqlj.runtime.ref.DefaultContext ctx=null; 
         try { 
           // Make the Connection 
           ctx = (sqlj.runtime.ref.DefaultContext) cbean.getContext(); 
         } catch (SQLException e) { 
         } 
          try { 
             #sql [ctx] { SELECT first_name, salary, TO_CHAR(hire_date, 'DD-MON-YYYY') 
                           INTO :ename, :sal, :hireDate 
                           FROM HR.employees WHERE UPPER(employee_id) = UPPER(:empno) 
             }; 
             sb.append("<BLOCKQUOTE><BIG><B><PRE>\n"); 
             sb.append("Name       : " + ename + "\n"); 
             sb.append("Salary     : " + sal + "\n"); 
             sb.append("Date hired : " + hireDate); 
             sb.append("</PRE></B></BIG></BLOCKQUOTE>"); 
          } catch (java.sql.SQLException e) { 
              sb.append("<P> SQL error: <PRE> " + e + " </PRE> </P>\n"); 
          } finally { 
              if (ctx!= null) ctx.close(); 
          } 
      %> 
     <H3><%=sb.toString()%></H3> 
<%} 
%> 
<B>Enter an employee number:</B> 
<FORM METHOD=get> 
<INPUT TYPE="text" NAME="empno" SIZE=10> 
<INPUT TYPE="submit" VALUE="Ask Oracle"); 
</FORM> 
</BODY> 
</HTML> 

注意:

この例では、説明のためContextClassプロパティを使用しています。ただし、デフォルト値はDefaultContextであり、DefaultContextを使用する場合に、getContext()ではなくgetDefaultContext()を使用すると、ContextClassの値は使用されません。

グローバル・トランザクションのSQLJサポート

分散トランザクション(グローバル・トランザクションとも呼ばれこともある)は、複数の関連トランザクションの集合である、調整して管理する必要があります。分散トランザクションを構成するトランザクションは、同じデータベース内に存在することもありますが、通常は異なるデータベースに存在し、多くの場合は異なる場所に存在します。分散トランザクションを構成する各トランザクションをトランザクション・ブランチと呼びます。

X/Open分散トランザクション処理(DTP)アーキテクチャは、1つ以上のリソース・マネージャに属する複数の関連トランザクションが、1つの単位として動作するための標準アーキテクチャを定義します。これによって、アプリケーション・プログラム(AP)とリソース・マネージャ(RM)間の動作が調整され、グローバル・トランザクションになります。すべてのトランザクションは、コミットまたはロールバックされます。

Oracle XAライブラリは、Oracleサーバー以外のトランザクション・マネージャがグローバル・トランザクションを調整できるようにするための外部インタフェースです。XAライブラリを使用すると、分散トランザクションで、Oracle以外のリソース・マネージャがサポートされます。これは、複数のデータベースおよびリソース間のトランザクションで特に有効です。Oracle XAライブラリの実装は、X/Open Distributed Transaction Processing (DTP)ソフトウェア・アーキテクチャのXAインタフェース仕様に準拠しています。Oracle XAライブラリは、Oracle Database Enterprise Editionの一部としてインストールします。

注意:

  • JDBCはXAをサポートする複数のクラスおよびインタフェースを提供します。OracleXADataSourceは、XADataSourceインタフェースを実装します。OracleXADatasourceは、XA接続のファクトリです。詳細は、『Oracle Database JDBC開発者ガイド』を参照してください。

  • このドキュメントでは、XAアプリケーションで接続コンテキストを作成するためにSQLJでサポートされるメソッドを明確に示しています。接続コンテキストを作成するために、SQLJはOracleXADataSourceから作成されたJDBC接続を使用します。

図8-1 グローバル・トランザクション

図8-1の説明が続きます
「図8-1 グローバル・トランザクション」の説明

次に、分散トランザクション処理(DTP)モデルの例を示します。

トランザクション・マネージャは、Oracle Databaseの外部に存在する外部中間層コンポーネントです。これによって、トランザクションの境界を指定するAPIが提供され、コミットおよびリカバリが管理されます。TMは2フェーズのコミット・エンジンを実装して、分散RMにまたがるall-or-noneセマンティクスを提供します。

リソース・マネージャは、障害発生後に通常の状態に戻すことができる共有かつリカバリ可能なリソースを制御します。たとえば、Oracleはリソース・マネージャです。

javax.sql.XADataSourceインタフェースは、XAデータ・ソースの標準機能を示します。XAデータ・ソースはXA接続のファクトリです。Oracle JDBCは、OracleXADatasourceクラスを介してXADataSourceインタフェースを実装します。OracleXADatasourceクラスのgetConnection( )メソッドは、基礎となるデータ・ソースへのXA接続を戻します。SQLJでは、データベースへの接続は、DefaultContextクラスまたはConnectionContextクラスを介して取得できます。数種類のSQLエンティティを使用した接続の場合は、接続コンテキストの宣言によって、接続コンテキスト・クラスをさらに定義すると利便性が高まります。

コード例では、XADatasourceの作成方法に続いて、データ・ソースからのJDBC接続の作成方法を次の順に示します。

  • XAリソース1の開始

  • XAリソース2の開始

  • 最初の接続オブジェクトを使用したDML操作の実行

  • XAリソース1の終了

  • XAリソース2の終了

  • リソース1の準備

  • リソース2の準備

  • コミット1

  • コミット2

注意:

次のコードは完全な構文例ではありません。XADatasourceの作成および使用に関連するコードのみが含まれます。

例: XADatasourceの作成およびXADatasourceを使用したJDBC接続の作成

import javax.sql.*;
import javax.transaction.*;
import javax.transaction.xa.*;
...
import oracle.jdbc.driver.*;
import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.OracleXAException;
import oracle.jdbc.xa.client.*;
…………
#sql context MyContext;
#sql iterator Iterator2 (String job_id, String job_title);
#sql iterator Iterator3 (String region_id, String region_name);
…………
class XA3mod{
public static void main (String args [])throws SQLException{
try{
/*create an XADataSource instance*/
OracleXADataSource oxds = new OracleXADataSource();
oxds.setURL(url);
oxds.setUser("hr");
oxds.setPassword("hr");
/*get an XA connection to the underlying data source*/
javax.sql.XAConnection pc1  = oxds.getXAConnection();
/*use the same data source */
javax.sql.XAConnection pc2  = oxds.getXAConnection();
/*get the Physical Connections*/
java.sql.Connection conn1 = pc1.getConnection();
java.sql.Connection conn2 = pc2.getConnection();
/*an application may access data through multiple database connections. Each database connection is enlisted 
with the transaction manager as a transactional resource. The transaction manager obtains an XAResource
 for each connection participating in a global transaction */
XAResource oxar1 = pc1.getXAResource();
XAResource oxar2 = pc2.getXAResource();
/*create the Xids With the Same Global Ids. The Xid interface is a Java mapping of the X/Open transaction 
identifier XID structure*/
Xid xid1 = createXid(1);
Xid xid2 = createXid(2);
/*start the Resources. This would start work on behalf of a transaction branch specified in xid1 and xid2. 
The transaction manager uses the start method to associate the global transaction with the resource, 
and it uses the end method to disassociate the transaction from the resource */
oxar1.start (xid1, XAResource.TMNOFLAGS);
oxar2.start (xid2, XAResource.TMNOFLAGS);
/*Do something with conn1 */
DoSomeWork (conn1);
/*END both the branches */
xar1.end(xid1, XAResource.TMSUCCESS);
xar2.end(xid2, XAResource.TMSUCCESS);
/*Prepare the RMs. The Oracle XA library interface follows the two-phase commit protocol. Preparing the transactions 
is the first step in this protocol. The two phase commit protocol is explained in detail in the glossary section. */
int prp1 =  oxar1.prepare (xid1);
int prp2 =  oxar2.prepare (xid2);
boolean do_commit = true;
if(!((prp1==XAResource.XA_OK)||(prp1==XAResource.XA_RDONLY)))
            do_commit = false;
if(!((prp2==XAResource.XA_OK)||(prp2==XAResource.XA_RDONLY)))
            do_commit = false;
/*issue a commit on all transactions only if all the transactions completed without and errors. Rollback even 
if a single transaction failed.*/
if (prp1 == XAResource.XA_OK)
           if (do_commit)
           oxar1.commit (xid1, false);
           else
              oxar1.rollback (xid1);
if (prp2 == XAResource.XA_OK)
           if (do_commit)
              oxar2.commit (xid2, false);
           else
              oxar2.rollback (xid2);
/* close connections */
conn1.close(); conn1 = null;
conn2.close(); conn2 = null;
pc1.close();   pc1 = null;
pc2.close();   pc2 = null;
} catch (XAException xae){
if (xae instanceof OracleXAException) {
System.out.println("XA Error is " + ((OracleXAException)xae).getXAError());
System.out.println("SQL Error is " +((OracleXAException)xae).getOracleError());
}
}
} //end class

次の例に、OracleXADatasourceから取得したJDBC接続を使用する様々なSQLJメソッドを示します。

XA Datasourceから取得したJDBC接続を使用するOracle.connect( )メソッドの使用例

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
Oracle.connect(conn);
#sql  {insert into xa_test values (1,:chr)};
try{
     Iterator3 iter = null;
     #sql iter = {SELECT id,name FROM xa_test};
     while (iter.next( )){
     System.out.print(iter.id());
     System.out.print(" ");
     System.out.println(iter.name());
     }
}
catch (Exception e){
     System.out.println(e);
     e.printStackTrace();
     }
}

XA Datasourceから取得したJDBC接続を使用するOracle.getConnection( )メソッドの使用例

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
DefaultContext  ctx = Oracle.getConnection(conn);
#sql [ctx]  {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql [ctx] iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
}
}

XA Datasourceから取得したJDBC接続を使用するDefaultContextコンストラクタの使用例

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
DefaultContext  ctx = new DefaultContext(conn)
#sql [ctx]  {insert into xa_test values (1,:chr)};
try{
Iterator3 iter = null;
#sql [ctx] iter = {SELECT id,name FROM xa_test};
while (iter.next( )){
System.out.print(iter.id());
System.out.print(" ");
System.out.println(iter.name());
}
}
catch (Exception e){
System.out.println(e);
e.printStackTrace();
}
}

ConnectionContextを渡すことによるDefaultContextコンストラクタの使用例。ConnectionContextは、XA Datasourceから取得したJDBC接続を介して作成されます。

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
MyContext myctx1= new MyContext (conn);
DefaultContext  ctx = new DefaultContext(myctx1);
#sql [ctx]  {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql [ctx] iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
}
}

ConnectionContextを渡すことによるOracle.connect( )メソッドの使用例。ConnectionContextは、XA Datasourceから取得したJDBC接続を介して作成されます。

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
MyContext myctx1= new MyContext (conn);
Oracle.connect(myctx1);
#sql {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
    }
}

ConnectionContextを渡すことによるOracle. getConnection( )メソッドの使用例。ConnectionContextは、XA Datasourceから取得したJDBC接続を介して作成されます。

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
MyContext myctx1= new MyContext (conn);
DefaultContext  ctx = Oracle.getConnection(myctx1);
#sql [ctx]  {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql [ctx] iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
}
}

DefaultContextクラスのsetDefaultContext( )メソッドは、XA Datasourceから取得したJDBC接続を介して作成されたコンテキストの設定にも使用されます。

DefaultContext.setDefaultContext(ctx);

XA Datasourceから取得したJDBC接続を渡すことによるConnectionContextコンストラクタの使用例

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
			MyContext myctx1= new MyContext (conn);
#sql [myctx1]  {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql [myctx1] iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
}
}

ConnectionContextを渡すことによるConnectionContextコンストラクタの使用例。ConnectionContextは、XA Datasourceから取得したJDBC接続を介して作成されます。

private static void DoSomeWork (java.sql.Connection conn) throws SQLException{
String chr = "XA_CERT";
MyContext myctx= new MyContext (conn);
MyContext myctx1= new MyContext (myctx);
#sql [myctx1]  {insert into xa_test values (1,:chr)};
try{
    Iterator3 iter = null;
    #sql [myctx1] iter = {SELECT id,name FROM xa_test};
    while (iter.next( )){
    System.out.print(iter.id());
    System.out.print(" ");
    System.out.println(iter.name());
    }
}
catch (Exception e){
    System.out.println(e);
    e.printStackTrace();
}
}

PDBへの接続

プラガブル・データベース(PDB)を使用すると、移植可能なスキーマのコレクション、スキーマ・オブジェクト、およびOracleクライアントに表示される非スキーマ・オブジェクトを別のデータベースとして、Oracle Databaseに含めることができます。マルチテナント・コンテナ・データベース(CDB)は、1つ以上のPDBを含むOracle Databaseです。SQLJアプリケーションでは、PLUGGABLE DATABASEプロパティが関連PDBに設定されているサービスを使用して、PDBに接続できます。

関連項目:

様々なプラガブル・データベースに接続するためのサービスの構成の詳細は、『Oracle Databaseバックアップおよびリカバリ・ユーザーズ・ガイド』を参照してください

実行コンテキスト

実行コンテキストはsqlj.runtime.ExecutionContextクラスのインスタンスで、SQL操作が実行されるコンテキストを提供します。実行コンテキスト・インスタンスは、SQLJアプリケーションの各SQL操作に暗黙的にまたは明示的に対応付けられています。

ExecutionContextクラスには、次の機能に対するメソッドが含まれています。

  • 実行制御操作は、後続のSQL操作のセマンティクスを変更します。

  • 実行ステータス操作は、最新のSQL操作の結果を示します。

  • 実行取消し操作は、現在実行しているSQL操作を終了します。

  • バッチ更新操作は、バッチの有効化と無効化、バッチ制限の設定、および更新カウントの取得を行います。

  • セーブポイント操作は、セーブポイントの設定、セーブポイントのロールバック、およびセーブポイントの解放を行います。

  • 閉包操作は、実行コンテキスト・インスタンスを終了してリソースのリークを回避します。

注意:

必要に応じて追加のクラスを宣言する接続コンテキスト・クラスとは異なり、実行コンテキスト・クラスは1つだけです。実行コンテキストはすべてExecutionContextクラスのインスタンスです。接続コンテキストという用語が通常は宣言済のクラスを指すのに対し、実行コンテキストという用語は常にExecutionContextクラスのインスタンスを指します。このドキュメントでは、混乱を避けるために、接続コンテキスト・クラス、接続コンテキスト・インスタンス、および実行コンテキスト・インスタンスと明記しています。

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

実行コンテキストと接続コンテキストとの関係

各接続コンテキスト・インスタンスには、暗黙的にデフォルトの実行コンテキスト・インスタンスが対応付けられています。このインスタンスを取得するには、接続コンテキスト・インスタンスのgetExecutionContext()メソッドを使用します。

1つの接続コンテキスト・インスタンスに必要な実行コンテキスト・インスタンスは、1つのみです。ただし、次の場合を除きます。

  • 1つの接続コンテキスト・インスタンスで複数のスレッドを使用している場合

    マルチスレッドを使用している場合は、各スレッドにその実行コンテキスト・インスタンスが必要です。

  • 同じコンテキスト・インスタンスを使用する複数のSQLJ文で、SQL実行制御の異なる操作を指定する場合

  • 同じ接続コンテキスト・インスタンスを使用する複数のSQL操作から、異なるSQLのステータス情報を保持する場合

    同じ実行コンテキスト・インスタンスを使用する一連のSQL操作を実行すると、各操作からのステータス情報によって前の操作からのステータス情報が上書きされます。

実行コンテキスト・インスタンスは接続コンテキスト・インスタンスと関連付けられているように見えますが(各接続コンテキスト・インスタンスがデフォルトの実行コンテキスト・インスタンスを持ち、特定のSQLJ文に対して接続コンテキスト・インスタンスおよび実行コンテキスト・インスタンスを一緒に指定可能な場合)、実際には独立して動作します。同じ接続コンテキスト・インスタンスを使用する文で異なる実行コンテキスト・インスタンスを使用可能であり、その逆も同様です。

たとえば、マルチスレッドによって、スレッドごとに別の実行コンテキスト・インスタンスを使用している場合、1つの接続コンテキスト・インスタンスで複数の実行コンテキスト・インスタンスを使用すると便利です。また、シングル・スレッド方式のプログラムを使用し、同じSQL制御パラメータをすべての接続コンテキスト・インスタンスに適用する場合は、1つの明示的な実行コンテキスト・インスタンスで複数の接続コンテキスト・インスタンスを使用できます。

複数の実行コンテキスト・インスタンスを1つの設定コンテキスト・インスタンスで使用するには、ExecutionContextクラスのインスタンスを追加し、SQLJ文で正しく指定する必要があります。

実行コンテキスト・インスタンスの作成と指定

デフォルト以外の実行コンテキスト・インスタンスを特定の接続コンテキスト・インスタンスで使用するには、別の実行コンテキスト・インスタンスを作成する必要があります。ExectionContextコンストラクタには、入力パラメータがありません。次に例を示します。

ExecutionContext myExecCtx = new ExecutionContext();

次に、特定のSQLJ文で使用するためにこの実行コンテキスト・インスタンスを指定できます(接続コンテキスト・インスタンスも指定します)。一般的な構文は次のとおです。

#sql [<conn_context><, ><exec_context>] { SQL operation };

たとえば、接続コンテキスト・クラスのMyConnCtxClassを宣言およびインスタンス化して、インスタンスmyConnCtxを作成した場合は、次の文を使用することもできます。

#sql [myConnCtx, myExecCtx] { DELETE FROM employees WHERE salary > 30000 };

myConnCtxに別の接続コンテキスト・インスタンスを使用したり、myExecCtxに別の実行コンテキスト・インスタンスを使用できます。

次のように、デフォルトの接続コンテキスト・インスタンスを使用して、実行コンテキスト・インスタンスを指定することも可能です。

#sql [myExecCtx] { DELETE FROM employees WHERE salary > 30000 };

注意:

  • 実行コンテキスト・インスタンスを指定しないで接続コンテキスト・インスタンスを指定した場合は、その接続コンテキスト・インスタンスのデフォルトの実行コンテキスト・インスタンスが使用されます。

  • 接続コンテキスト・インスタンスを指定しないで実行コンテキスト・インスタンスを指定した場合は、その実行コンテキスト・インスタンスには、アプリケーションのデフォルトの接続コンテキスト・インスタンスが使用されます。

  • 接続コンテキスト・インスタンスと実行コンテキスト・インスタンスの両方を指定しなかった場合は、SQLJではデフォルトの接続コンテキスト・インスタンスとデフォルトの実行コンテキスト・インスタンスが使用されます。

実行コンテキストの同期

ExecutionContextメソッドは、すべてsynchronizedメソッドです。そのため、ISO標準コード生成の場合は、ある文がすでに使用されている実行コンテキスト・インスタンスを使用しようとすると、最初の文の処理が終了するまで次の文がブロックされます。

これについては、クライアント・アプリケーションでのマルチスレッド化の状況などが関係します。別のスレッドで使用されている実行コンテキスト・インスタンスの使用を試みたスレッドは、ブロックされます。このようなブロックを避けるには、使用する各スレッドに対して、別の実行コンテキスト・インスタンスを指定する必要があります。

ただし、デフォルトのOracle固有コード生成には適用されません。パフォーマンス上の理由から、SQLJでは、Oracle固有生成コードのExecutionContextインスタンスに対する同期が追加実行されません。そのため、必ず、1つの実行コンテキスト・インスタンスを複数のスレッドで使用しないようにする必要があります。複数のスレッドで同じ実行コンテキストを使用すると、ブロック化ではなくてアプリケーションで、不正な結果やNullPointer例外などのエラーが発生します。

もう1つの例外はサーバーでのみ発生する再帰型の場合です。再帰的コールの場合、1つのスレッド内の複数のSQLJ文は、同時に同じ実行コンテキスト・インスタンスを使用できます。たとえば、SQLJストアド・プロシージャまたはストアド・ファンクションに、別のSQLJストアド・プロシージャまたはストアド・ファンクションへのコールが含まれている場合などです。この両方のストアド・プロシージャでデフォルトの実行コンテキスト・インスタンスが使用されている場合、最初のプロシージャでコールされるSQLJ文でその実行コンテキストが使用されている間も、2番目のプロシージャのSQLJ文でその実行コンテキストが使用されます。このことは許可されています。

実行コンテキストのメソッド

ここでは、ExecutionContextクラスのpublicメソッドについて説明し、例を示します。

状態メソッド

次に示す実行コンテキスト・インスタンスのメソッドを使用すると、そのインスタンスを使用して完了した最新のSQL操作に関するステータス情報を取得できます。

  • SQLWarning getWarnings(): java.sql.SQLWarningオブジェクトを戻します(この実行コンテキスト・インスタンスで完了した最新のSQL操作から報告された最初の警告が含まれています)。警告は連鎖的に戻されます。実行コンテキスト・インスタンスのgetWarnings()メソッドを使用して最初の警告を取得し、各SQLWarningオブジェクトのgetNextWarning()メソッドを使用して次の警告を取得します。この連鎖には、SQL操作の実行中に生成されるすべての警告が含まれています。

  • int getUpdateCount(): バッチ更新が有効な場合を除いて、これは、この実行コンテキスト・インスタンスで完了した最後のSQL操作で更新された行数を示すint値を戻します。最後のSQL操作がデータ操作言語(DML)文ではなかった場合は、ゼロ(0)を戻します。最後のSQL操作でイテレータまたは結果セットが生成された場合に、QUERY_COUNT定数を戻します。最後のSQL操作が実行完了前に終了された場合、またはこの実行コンテキスト・インスタンスで操作が試行されていない場合は、EXCEPTION_COUNT定数を戻します。

    バッチ可能なアプリケーションの場合、getUpdateCount()からの戻り値は、バッチ関連の定数値(NEW_BATCH_COUNTADD_BATCH_COUNTまたはEXEC_BATCH_COUNT)のいずれかになります。

制御メソッド

次に示す実行コンテキスト・インスタンスのメソッドを使用すると、そのインスタンスを使用して実行される以降のSQL操作(まだ開始されていない操作)を制御できます。

  • int getMaxFieldSize(): この実行コンテキスト・インスタンスを使用したSQL操作から戻される最大データ量(バイト単位)を示すint値を戻すメソッド。このメソッドが適用されるのは、BINARYVARBINARYLONGVARBINARYCHARVARCHARまたはLONGVARCHAR型の列のみです。

    デフォルトでは、このパラメータは0(ゼロ)に設定されます。これは、サイズに上限がないことを示します。

  • setMaxFieldSize(int): int値を入力パラメータとして取り、フィールド・サイズの上限を変更するメソッド。

  • int getMaxRows(): この実行コンテキスト・インスタンスを使用して作成されたJDBC結果セットまたはSQLJイテレータに格納できる最大行数を示すint値を戻すメソッド。上限を超えた場合、エラー・メッセージや警告は出力されずに、超えた分の行が削除されます。

    デフォルトでは、このパラメータは0(ゼロ)に設定されます。これは、行数に上限がないことを示します。

  • setMaxRows(int): int値を入力パラメータとして取り、最大行数を変更するメソッド。

  • int getQueryTimeout(): この実行コンテキスト・インスタンスを使用したSQL操作のタイムアウト時間(秒単位)を示すint値を戻すメソッド。SQL操作がこの期限を過ぎた場合、SQL例外が発生します。

    デフォルトでは、このパラメータは0(ゼロ)に設定されます。これは、問合せにタイムアウトの期限がないことを示します。

  • setQueryTimeout(int): int値を入力パラメータとして取り、問合せのタイムアウトの期限を変更するメソッド。

  • int getFetchSize(): このExecutionContextオブジェクトから生成されたイテレータ・オブジェクトに対して既存のフェッチ・サイズである行数を取得するメソッド。このExecutionContextオブジェクトに、setFetchSize()をコールしてフェッチ・サイズを設定する作業が行われていない場合、戻り値は0(ゼロ)となります。setFetchSize()メソッドをコールすることによって、このExecutionContextオブジェクトにより正のフェッチ・サイズが設定された場合、戻り値はsetFetchSize()で指定したフェッチ・サイズとなります。

  • setFetchSize(int): より多くの行が必要な場合にフェッチされる行数に関して、SQLJランタイムにヒントを与えるメソッド。指定した行数は、このExecutionContextオブジェクトを使用して作成したイテレータにのみ影響します。0(ゼロ)を指定すると、実装に依存したデフォルト値がフェッチ・サイズとして使用されます。

  • int getFetchDirection(): フェッチしているデータのデフォルトの方向と、このExecutionContextオブジェクトから生成されるスクロール可能なイテレータ・オブジェクトのデフォルトの方向を取得するメソッド。このExecutionContextオブジェクトにsetFetchDirection()をコールすることによってフェッチ方向が設定されていない場合、戻り値はFETCH_FORWARDとなります。

  • setFetchDirection(int): スクロール可能なイテレータ・オブジェクトの行が処理される方向に関して、SQLJランタイムにヒントを与えるメソッド。ヒントは、このExecutionContextオブジェクトを使用して作成されるスクロール可能なイテレータ・オブジェクトにのみ適用されます。デフォルト値は次のとおりです。

    sqlj.runtime.ResultSetIterator.FETCH_FORWARD.
    

    このメソッドは、与えられた方向がFETCH_FORWARDFETCH_REVERSEまたはFETCH_UNKNOWN(int定数)のいずれでもない場合に、SQLExceptionをスローします。

取消しメソッド

マルチスレッド環境でSQL操作を取り消す場合や、バッチ更新が有効化されているときに保留文を一括で取り消す場合には、次のメソッドを使用します。

  • cancel(): マルチスレッド環境において、あるスレッドで実行中のSQL操作を取り消すには、このメソッドを別のスレッドで操作します。このメソッドを使用すると、この実行コンテキスト・インスタンスを使用して開始した未完了の最終操作が取り消されます。この実行コンテキスト・インスタンスを使用した実行中の文が存在しない場合、このメソッドは機能しません。

    バッチ可能な環境で保留文を一括で取り消すには、このメソッドを使用します。バッチが空にされて、バッチ内の文は実行されません。既存のバッチを取り消した場合、次に出現したバッチ可能な文は、新規のバッチに追加されます。

バッチ更新メソッド

アプリケーションでパフォーマンス強化の機能を使用する場合は、次のメソッドを使用してバッチ更新を制御します。

  • int[] executeBatch(): 保留文を一括実行し、int型の更新カウントの配列を戻すメソッド。

  • int getBatchLimit(): その時点でのバッチ制限を示すint値を戻すメソッド。バッチ制限がある場合、一括されている文の数が上限に達したバッチは、暗黙的に実行されます。

    デフォルトのバッチ制限は、ExecutionContext静的定数値UNLIMITED_BATCH(バッチ制限なし)に設定されます。

  • int[] getBatchUpdateCounts(): 最後に実行されたバッチの更新カウントを示すint型の配列を戻すメソッド。このメソッドは、バッチを暗黙的に実行する場合に有効です。

  • boolean isBatching(): バッチ更新の実行可否を示すブール値を戻すメソッド。

    これは、現在未完了のバッチがあるかどうかを示しているのではありません。バッチが新規に作成、追加または実行されたかどうかを確認するには、getUpdateCount()メソッドを使用してください。

  • setBatching(boolean): バッチ更新を有効化するためのブール値を取るメソッド。

    バッチ更新機能は、デフォルトでは無効になります。

  • setBatchLimit(int): 現行のバッチ制限を設定するための入力として、0(ゼロ)でない正のint値を取るメソッド。代入可能な値としては、UNLIMITED_BATCH(制限なし)と、AUTO_BATCH(バッチ制限はSQLJランタイムで動的に判定される)の2つがあります。

セーブポイント・メソッド

Oracle SQLJ実装は、JDBC 3.0セーブポイントをサポートします。セーブポイントはExecutionContextインスタンスに格納され、SQLJのセーブポイント文をサポートするために次のpublicメソッドがあります。

  • Object oracleSetSavepoint(ConnectionContextImpl, String)

    セーブポイントを登録し、Objectインスタンスとして戻します。このメソッドでは、sqlj.runtime.ref.ConnectionContextImplクラスのインスタンスおよびセーブポイント名を指定する文字列として接続コンテキストを取ります。

    Oracle SQLJ実装は、java.sql.Savepointインタフェースを拡張するoracle.jdbc.OracleSavepointクラスのインスタンスとして、セーブポイントをインスタンス化します。

  • void oracleRollbackToSavepoint (ConnectionContextImpl, Object)

    指定されているセーブポイントまでの変更をロールバックします。このメソッドでは、接続コンテキストをConnectionContextImplのインスタンスとして取り、セーブポイントをObjectインスタンスとして取ります。

  • void oracleReleaseSavepoint(ConnectionContextImpl, Object)

    指定されているセーブポイントを解放します。このメソッドでは、接続コンテキストをConnectionContextImplのインスタンスとして取り、セーブポイントをObjectインスタンスとして取ります。

通常、これらのメソッドを直接使用するかわりに、SQLJのセーブポイント文を使用します。

クローズ・メソッド

Oracle SQLJ実装では、ExecutionContextクラスのclose()メソッドに拡張機能が用意されています。

  • close(): リソースのリークを回避するには、次のすべての状況に該当する場合にこのメソッドを使用します。

    • Oracle固有コード生成を使用している場合。

    • 接続コンテキスト・インスタンスを介して使用可能なデフォルトのインスタンスを使用するのではなく、ExecutionContextインスタンスを明示的に作成して使用している場合。

    • ExecutionContextインスタンスを使用してSQLJロールバック文またはコミット文を明示的に発行していない場合。

      #sql [ec] { COMMIT };
      #sql [ec] { ROLLBACK };
      
    • ExecutionContextインスタンスでexecuteBatch()をコールしていない場合。

    このような状況下では、ExecutionContextインスタンス上でバッチ可能文がオープンしたままとなる可能性があり、そのうちにデータベース・カーソルを使い果たすことがあります。これを回避するには、次の例のようにclose()メソッドを使用します。

    Execution Context ec = new ExecutionContext();
    ...
    try {
       ...
       #sql [ec] { SQL operation };
       ...
    } finally { ec.close(); }
    

注意:

明示的に宣言されるかわりに実行コンテキスト・インスタンスが接続コンテキスト・インスタンスに関連付けられている場合は、基になるJDBC接続のクローズと一緒に、または別に接続コンテキスト・インスタンスをクローズすると、実行コンテキスト・インスタンス上に残っているすべての文が自動的に終了されます。

例: ExecutionContextメソッドの使用

次のコードは、ExecutionContextメソッドの使用方法の一例です。

ExecutionContext execCtx =
   DefaultContext.getDefaultContext().getExecutionContext();

// Wait only 3 seconds for operations to complete
execCtx.setQueryTimeout(3);

// delete using execution context of default connection context
#sql { DELETE FROM employees WHERE salary > 10000 };

System.out.println
     ("removed " + execCtx.getUpdateCount() + " employees");

実行コンテキストとマルチスレッドの関係

1つの実行コンテキストで複数のスレッドを使用しないでください。複数のスレッドを使用した場合に、2つのSQLJ文で同じ実行コンテキストが同時に使用されると、最初の文の処理が完了するまで2番目の文がブロックされます。また、最初の操作からのステータス情報が、その情報が取り出される前に上書きされることがあります。

そのため、1つの接続コンテキスト・インスタンスで複数のスレッドを使用している場合は、次の手順で操作を行ってください。

  1. スレッドごとに一意の実行コンテキスト・インスタンスをインスタンス化します。

  2. #sql文で実行コンテキストを指定し、各スレッドで該当の実行コンテキストを使用するようにします。

スレッドごとに別の接続コンテキスト・インスタンスを使用している場合、実行コンテキスト・インスタンスをインスタンス化して、指定する必要はありません。接続コンテキスト・インスタンスごとに、そのインスタンスに対するデフォルトの実行コンテキスト・インスタンスが暗黙的に対応付けられているためです。

注意:

パフォーマンス上の理由から、SQLJでは、Oracle固有生成コードのExecutionContextインスタンスに対する同期が追加実行されません。そのため、必ず、1つの実行コンテキスト・インスタンスを複数のスレッドで使用しないようにする必要があります。複数のスレッドで同じ実行コンテキストを使用すると、ブロック化ではなくてアプリケーションで、不正な結果やNullPointer例外などのエラーが発生します。

SQLJでのマルチスレッド

ここでは、SQLJのサポート、マルチスレッドの要件、マルチスレッドと実行コンテキスト・インスタンスとの関係について説明します。

SQLJを使用して、マルチスレッド・アプリケーションを記述できます。ただし、SQLJアプリケーションでマルチスレッドを使用すると、JDBCドライバまたは専用のデータベース・アクセス用のデバイスになんらかの制限が発生します。この制限には、同期に関する制限も含まれています。

スレッドごとに異なる実行コンテキスト・インスタンスを使用してください。それには、次の2通りの方法があります。

  • SQLJ文に接続コンテキスト・インスタンスを指定し、スレッドごとに異なる接続コンテキスト・インスタンスが使用されるようにします。接続コンテキスト・インスタンスごとに、デフォルトの実行コンテキスト・インスタンスが自動的に対応付けられます。

  • 同じ接続コンテキスト・インスタンスで複数のスレッドを使用している場合は、実行コンテキスト・インスタンスを追加して宣言します。次に、スレッドごとに異なる実行コンテキスト・インスタンスが使用されるように、SQLJ文で実行コンテキスト・インスタンスを指定します。

Oracle JDBCドライバのいずれかを使用している場合は、必要に応じて、同じ接続コンテキスト・インスタンスを複数のスレッドで使用できますが、これは、異なる実行コンテキスト・インスタンスが指定されて、直接見える同期要求がない場合に限られます。ただし、データ・アクセスは順次処理される点に注意してください。データには、常に1つのスレッドのみがアクセスします。同期は、スレッドを通じて実行されるSQL操作の様々な段階の制御フローを指します。たとえば、各文で入力パラメータはバインドされて、文が実行され、出力パラメータをバインドできます。JDBCドライバの中には、これらの段階が入り混じらないように特別な配慮が必要がものもあります。

ISO標準コード生成の場合、あるスレッドが、別の操作で使用されている実行コンテキストを使用するSQL操作を実行すると、現行の操作が完了するまでスレッドがブロックされます。実行コンテキストが2つのスレッド間で共有されている場合、一方のスレッドで実行されたSQL操作の結果は、もう一方のスレッドで認識できます。両方のスレッドがSQL操作を実行している場合、競合状態が発生する可能性があります。つまり、一方のスレッドが元の結果を処理する前に、そのスレッドの実行結果がもう一方のスレッドの実行結果で上書きされる場合があります。このため、複数のスレッドが同じ実行コンテキスト・インスタンスを共有することは不可能です。

注意:

前述の説明は、デフォルトのOracle固有コード生成には適用されません。パフォーマンス上の理由から、SQLJでは、Oracle固有生成コードのExecutionContextインスタンスに対する同期が追加実行されません。そのため、必ず、1つの実行コンテキスト・インスタンスを複数のスレッドで使用しないようにする必要があります。複数のスレッドで同じ実行コンテキストを使用すると、ブロック化ではなくてアプリケーションで、不正な結果やNullPointer例外などのエラーが発生します。

マルチスレッド: MultiThreadDemo.sqlj

次は、マルチスレッドを使用したSQLJアプリケーションの例です。データが永続的に変更されるのを防ぐために、ROLLBACK操作の実行後に接続を終了します。

import java.sql.SQLException;
import java.util.Random;
import sqlj.runtime.ExecutionContext;
import oracle.sqlj.runtime.Oracle;
/**
  Each instance of MultiThreadDemo is a thread that gives all employees
  a raise of some ammount when run.  The main program creates two such 
  instances and computes the net raise after both threads have completed.
  **/
class MultiThreadDemo extends Thread
{
  double raise;
  static Random randomizer = new Random(); 
 
  public static void main (String args[]) 
  {
    try { 
      // set the default connection to the URL, user, and password
      // specified in your connect.properties file
      Oracle.connect(MultiThreadDemo.class, "connect.properties");
      double avgStart = calcAvgSal();
      MultiThreadDemo t1 = new MultiThreadDemo(250.50);
      MultiThreadDemo t2 = new MultiThreadDemo(150.50);
      t1.start();
      t2.start();
      t1.join();
      t2.join();
      double avgEnd = calcAvgSal();
      System.out.println("average salary change: " + (avgEnd - avgStart));
    } catch (Exception e) { 
      System.err.println("Error running the example: " + e);
    }
    try { #sql { ROLLBACK }; Oracle.close(); } catch (SQLException e) { }
  } 
  static double calcAvgSal() throws SQLException
  {
    double avg;
    #sql { SELECT AVG(salary) INTO :avg FROM employees };
    return avg;
  }
  MultiThreadDemo(double raise)
  {
    this.raise = raise;
  }
  public void run()
  {
    // Since all threads will be using the same default connection
    // context, each run uses an explicit execution context instance to
    // avoid conflict during execution
    try {
      delay();
      ExecutionContext execCtx = new ExecutionContext();
      #sql [execCtx] { UPDATE EMPLOYEES SET salary = salary + :raise };
      int updateCount = execCtx.getUpdateCount();
      System.out.println("Gave raise of " + raise + " to " + 
                          updateCount + " employees");
    } catch (SQLException e) {
      System.err.println("error updating employees: " + e);
    }
  }
  // delay is used to introduce some randomness into the execution order
  private void delay()
  {
    try {
      sleep((long)Math.abs(randomizer.nextInt()/10000000));
    } catch (InterruptedException e) {}
  }
}

イテレータ・クラスの実装と拡張機能

ここでは、イテレータ・クラスの実装方法、および基本メソッド以外に使用可能な追加機能について説明します。内容は次のとおりです。

イテレータ・クラスの実装と機能

宣言した名前付きイテレータ・クラスは、SQLJトランスレータによって生成され、sqlj.runtime.NamedIteratorインタフェースが実装されます。NamedIteratorインタフェースを実装しているクラスには、位置ではなく名前で、イテレータ列をデータベース列にマッピングする機能があります。

宣言した位置イテレータ・クラスは、SQLJトランスレータによって生成され、sqlj.runtime.PositionedIteratorインタフェースが実装されます。PositionedIteratorインタフェースを実装しているクラスには、名前ではなく位置で、イテレータ列をデータベース列にマッピングする機能があります。

NamedIteratorインタフェースとPositionedIteratorインタフェースの両方、そしてすべての生成されたSQLJイテレータ・クラスは、sqlj.runtime.ResultSetIteratorインタフェースを実装または拡張します。

すべてのSQLJイテレータでは、ResultSetIteratorインタフェースで次のメソッドが指定されます。

  • close(): イテレータを終了するメソッド

  • ResultSet getResultSet(): 基になるJDBC結果セットをイテレータから抽出するメソッド

  • boolean isClosed(): イテレータが終了しているかどうかを判定するメソッド

  • boolean next(): イテレータの次の行に移動するメソッド(移動できる有効な行がある場合、trueが戻り値)

PositionedIteratorインタフェースは、次のメソッド仕様部を位置イテレータに追加します。

  • boolean endFetch(): 位置イテレータの最後の行に到達したかどうかを判定するメソッド

next()メソッドを使用して名前付きイテレータおよびアクセッサ・メソッドの行を進んでデータを取得します。名前付きイテレータ・クラスのSQLJ生成では、各イテレータ列に対してアクセッサ・メソッドが定義されますが、各メソッドの名前は対応する列名と同じになります。たとえば、name列を宣言すると、name()メソッドが生成されます。

FETCH INTO文をendFetch()メソッドと一緒に使用して、位置イテレータの行を進んで、データを取得します。FETCH INTO文では、暗黙的にnext()メソッドがコールされます。特別なFETCH CURRENT構文を使用していない場合は、next()メソッドを位置イテレータで明示的に使用しないでください。このFETCH INTO文では、イテレータ列番号に従って名前付けされたアクセッサ・メソッドも暗黙的にコールされます。位置イテレータ・クラスのSQLJ生成では、各イテレータ列のアクセッサ・メソッドが定義されますが、各メソッドの名前は列の位置と対応します。

関連項目:

\

close()メソッドを使用して、操作の終了したイテレータを終了します。getResultSet()メソッドは、SQLJとJDBCの連係動作の中心となるメソッドです。

注意:

かわりに、ResultSetIteratorインスタンスまたはScrollableResultSetIteratorインスタンスを弱い型指定のイテレータとして直接使用できます。(ScrollableResultSetIteratorは、ResultSetIteratorを拡張します。)これは、JDBC結果セットに変換することのみが目的で、名前付きまたは位置イテレータの機能が不要である場合に、便利な方法です。また、SQLJのFETCH CURRENT構文でもアクセスできます。

イテレータ宣言でのIMPLEMENTS句の使用

イテレータ宣言でインタフェースを実装すると便利な場合があります。たとえば、イテレータ・クラスがあり、そのクラスで1つ以上の列へのアクセスを制限するとします。SQLJで生成された名前付きイテレータ・クラスには、イテレータの各列にアクセッサ・メソッドがあります。特定の列へのアクセスを制限する必要がある場合に、アクセッサ・メソッドのサブセットのみでインタフェースを作成できるときは、イテレータ・クラス型のインスタンスを公開するのではなく、そのインタフェース型のインスタンスをユーザーに公開します。

たとえば、従業員データの名前付きイテレータを作成し、それにENAME(従業員名)、EMPNO(従業員番号)およびSAL(給与)の各列を定義するとします。その場合、次のように指定します。

#sql iterator EmpIter (String ename, int empno, float sal);

この文を実行すると、ename()empno()およびsal()の各アクセッサ・メソッドが定義されたクラスEmpIterが生成されます。

SAL列にアクセスできないようにするとします。ename()メソッドおよびempno()メソッドはあるが、sal()メソッドはないEmpIterIntfcインタフェースを作成できます。前述の宣言のかわりに、次のイテレータ宣言の使用も可能です(EmpIterIntfcmypackageパッケージ内にあるとします)。

#sql iterator EmpIter implements mypackage.EmpIterIntfc 
     (String emame, int empno, float sal);

EmpIterIntfcインスタンスからしかユーザーがデータにアクセスできないようにアプリケーションをコーティングすると、SAL列にはアクセスできなくなります。

イテレータ・クラスの拡張のサポート

SQLJは、イテレータ・クラスの拡張機能をサポートしています。この拡張は、問合せおよび問合せの結果へ追加できる機能なので、非常に便利です。

イテレータ・サブクラスに関する主要な要件の1つは、sqlj.runtime.RTResultSetインスタンスを入力として取るpublicコンストラクタを宣言することです。SQLJランタイムでは、問合せ結果をサブクラスのインスタンスに割り当てる際にこのコンストラクタがコールされるためです。しかしもっと重要なのは、必要な機能を指定することです。

元のイテレータ・クラス(使用するサブクラスのスーパークラス)の機能を使用することも可能です。たとえば、各問合せ結果の中で次に進むときは、super.next()メソッドをコールすることも可能です。

結果セット・イテレータ

SQLJイテレータの強い型指定の機能が必要ない場合があります。

そのような場合は、sqlj.runtime.ResultSetIterator型のインスタンスを直接使用して、問合せデータを取得できるため、名前付きイテレータ・クラスや位置イテレータ・クラスを宣言する必要はありません。かわりに、ResultSetIteratorを拡張するsqlj.runtime.ScrollableResultSetIterator型を使用できます。これにより、SQLJのスクロール可能なイテレータ機能を使用できます。強い型指定のイテレータではなく、結果セット・イテレータを使用すると、イテレータ・クラスを宣言しなくて済むかわりに、SQLJ SELECT操作に対する厳密な分類のチェックを実行できなくなります。

すべての名前付きイテレータ・クラスと位置イテレータ・クラスは、ResultSetIteratorインタフェースに基づいており、このインタフェースでgetResultSet()メソッドおよびclose()メソッドが指定されます。SQLJを使用して結果セット・イテレータ・インスタンスを処理する場合は、ScrollableResultSetIteratorインスタンスおよびFETCH CURRENT構文を使用します。

JDBCを使用して結果セット・イテレータ・インスタンスを処理する場合は、そのgetResultSet()メソッドを使用して、基になる取得した結果セットを処理することができます。結果セット・イテレータをその基になる結果セットを介して処理する場合、操作の終了後に、結果セットではなく結果セット・イテレータを終了する必要があります。結果セット・イテレータを終了すると結果セットも終了しますが、結果セットを終了しても結果セット・イテレータは終了しません。

注意:

Oracle SQLJ実装では、結果セット・イテレータのホスト式としての使用と、FETCH文でのカーソル表現をサポートしています。この機能は、Oracle9i データベースより前のデータベースではサポートされていません。

スクロール可能なイテレータ

SQLJのISO規格では、スクロール可能なJDBC結果セットのためのJDBC 2.0仕様に合せて作成された機能によって、スクロール可能なイテレータをサポートしています。Oracle SQLJ実装では、この機能がサポートされています。

スクロール可能なイテレータの宣言

イテレータをスクロール可能として設定するには、イテレータの宣言に次の句を追加します。

implements sqlj.runtime.Scrollable

これにより、SQLJトランスレータでScrollableインタフェースを実装したイテレータが生成されます。次に、スクロール可能な名前付きイテレータ宣言の例を示します。

#sql public static MyScrIter implements sqlj.runtime.Scrollable
                             (String ename, int empno);

SQLJトランスレータによりMyScrIterクラスに対して生成されたコードは、スクロール可能なインタフェースのすべてのメソッドを自動的にサポートします。

スクロール可能なイテレータの更新検出

基になるデータへの変更を検出できるように、スクロール可能な結果セットと同様に、スクロール可能なイテレータを宣言できます。Oracle SQLJ実装のスクロール可能なイテレータのsensitivity設定は、デフォルトではINSENSITIVEであり、基になるデータにどのような変更があっても検出されません。ただし、with句を使用して、この設定を変更できます。次の例では、更新検出を指定して、前の例を拡張しています。

#sql public static MyScrIter implements sqlj.runtime.Scrollable
                             with (sensitivity=SENSITIVE) 
                             (String ename, int empno);

注意:

implements句はwith句の前に置く必要があります。

SQLJ規格ではASENSITIVEも設定可能であり、この設定はデータベースのデフォルトのsensitivityを受け入れることを意味します。ただし、OracleではsensitivityASENSITIVEに設定すると、デフォルト設定のINSENSITIVEが使用されることになります。

前述の宣言がある場合、MyScrIterインスタンスはデータ変更に反応し、フェッチ・サイズ・ウィンドウなどの要因によって変わります。

関連項目:

スクロール可能な結果セットの詳細は、『Oracle Database JDBC開発者ガイド』を参照してください。

スクロール可能なインタフェース

ここでは、sqlj.runtime.Scrollableインタフェースの重要なメソッドを説明します。

スクロール可能なインタフェースのフェッチ方向についてのヒントを提供します。次のメソッドは、実行コンテキスト上のみでなくスクロール可能なイテレータ上でも定義されます。ExecutionContextインスタンスを使用して、デフォルトの方向がスクロール可能なイテレータの生成で使用されるようにします。

  • setFetchDirection(int): 行が処理される方向に関して、SQLJランタイムにヒントを与えるメソッド。方向は、sqlj.runtime.ResultSetIterator.FETCH_FORWARDFETCH_REVERSEまたはFETCH_UNKNOWNのいずれかである必要があります。

    ExecutionContextで方向に対する値を指定しない場合、FETCH_FORWARDがデフォルトとして使用されます。

  • int getFetchDirection(): その時点でのデータ行をフェッチする方向(前述の整数の定数のいずれか)を取り出すメソッド。

また、基になる結果セットにイテレータ・オブジェクトの現在の位置情報を戻す、スクロール可能なイテレータのメソッドもいくつかあります。これらのメソッドはすべて、イテレータの基となる結果セットに行が含まれない場合は常にfalseを戻します。

  • boolean isBeforeFirst(): イテレータ・オブジェクトが結果セットの最初の行の前にあるかどうかを識別するメソッド。

  • boolean isFirst(): イテレータ・オブジェクトが結果セットの最初の行にあるかどうかを識別するメソッド。

  • boolean isLast(): イテレータ・オブジェクトが結果セットの最後の行にあるかどうかを識別するメソッド。isLast()メソッドをコールすると、負荷が高くなる場合がありますが、これは、JDBCドライバで現在の行が結果セットの最終行であるかどうかを判別するために、1つ先の行をフェッチする必要がある場合があるためです。

  • boolean isAfterLast(): イテレータ・オブジェクトが結果セットの最後の行より後にあるかどうかを識別するメソッド。

注意:

ナビゲーションのその他のメソッドも、Scrollableインタフェースで定義されており、同様に使用できます。

スクロール可能な名前付きイテレータ

名前付きイテレータでは、Scrollableインタフェースで定義されたナビゲーション・メソッドを使用して、結果セットの行を移動できます。前述のように、スクロールしないイテレータには、次のナビゲーション・メソッドのみがあります。

  • boolean next(): 結果セットの最終行にイテレータ・オブジェクトを移動するメソッド。

追加のナビゲーション・メソッドは、スクロール可能な名前付きイテレータで使用できます。これらのメソッド・ファンクションは、next()と類似した機能を実行します。その際、結果セットの実際の行上にイテレータを配置しようとします。イテレータが有効な行に配置された場合はtrueを、そうでない場合はfalseを戻します。さらに、結果セット内の最初の行の前または最終行の後にイテレータ・オブジェクトを配置すると、イテレータ・オブジェクトがそれぞれ最初の前、最後の後の位置に配置されます。

次のメソッドがサポートされます。

  • boolean previous(): 結果セットの以前の行にイテレータ・オブジェクトを移動するメソッド。

  • boolean first(): 結果セットの最初の行にイテレータ・オブジェクトを移動するメソッド。

  • boolean last(): 結果セットの最終行にイテレータ・オブジェクトを移動するメソッド。

  • boolean absolute(int): 結果セットの指定された行番号にイテレータ・オブジェクトを移動するメソッド。最初の行が行1、2番目が行2、などとします。行番号がマイナスの場合、イテレータ・オブジェクトは、結果セットの最終行に対する相対的な行位置に移動します。たとえば、absolute(-1)をコールすると、イテレータ・オブジェクトは最終行に配置され、absolute(-2)をコールすると最後から2番目の行に配置されることになります。

  • boolean relative(int): 現在の位置からプラスまたはマイナスのいずれかの相対行番号へイテレータ・オブジェクトを移動するメソッド。relative(0)のコールは有効ですが、イテレータの位置は変わりません。

  • void beforeFirst(): 結果セットの冒頭、つまり最初の行の前にイテレータ・オブジェクトを移動するメソッド。結果セットに行がない場合は、機能しません。

  • void afterLast(): 結果セットの最後、つまり最終行の後にイテレータ・オブジェクトを移動するメソッド。結果セットに行がない場合は、機能しません。

注意:

beforeFirst()メソッドおよびafterLast()メソッドは、イテレータ・オブジェクトを結果セット上の実際の行に配置しないため、voidを戻します。

スクロール可能な位置イテレータ

位置イテレータの汎用的なFETCH構文は、「位置イテレータの使用」を参照してください。次に例を示します。

#sql { FETCH :iter INTO :x, :y, :z };

これは、実際には次の構文の省略バージョンです。

#sql { FETCH NEXT FROM :iter INTO :x, :y, :z  };

結果セットの以前の行、最初の行または最後の行に移動する別のパターンです。ただし、移動メソッドがモデル化された後のJDBC 2.0ではprevious()が使用されています。SQLを基本としたFETCHではPRIORが採用されています。この矛盾を忘れた場合でも、Oracle Database 12c リリース1 (12.1) SQLJトランスレータではFETCH PREVIOUSも受け入れられます。

構文は次のとおりです。

#sql { FETCH PRIOR FROM :iter INTO :x, :y, :z  };
#sql { FETCH FIRST FROM :iter INTO :x, :y, :z  };
#sql { FETCH LAST FROM :iter INTO :x, :y, :z  };

数値を渡して絶対的または相対的な移動を行う構文もあります。特定の行(絶対行)に移動したり、その時点での位置から前または後ろに移動することができます。構文は次のとおりです。

#sql { FETCH ABSOLUTE :n FROM :iter INTO :x, :y, :z  };
#sql { FETCH RELATIVE :n FROM :iter INTO :x, :y, :z  };

注意:

このような構文を作成した場合、FETCHが有効な行へ移動できずに値を取得することに失敗した場合でも、イテレータのendFetch()メソッドは常にtrueを戻します。

必ずホスト式を使用して、移動を指定してください。数値の定数のみを使用することはできません。次の構文ではなく、

#sql { FETCH RELATIVE 0 FROM :iter INTO :x, :y, :z };

このように記述する必要があります。

#sql { FETCH RELATIVE :(0) FROM :iter INTO :x, :y, :z  };

このコマンドはイテレータの位置を変更しません。イテレータが有効な行にある場合、このコマンドは変数に代入します。

注意:

ナビゲーション・メソッドとFETCH CURRENT構文を組み合せることによって、スクロール可能な位置イテレータで移動できます。

FETCH CURRENT構文: JDBC結果セットからSQLJイテレータへ

既存のJDBCプログラムをできるだけ修正せずにSQLJで書き換えたい場合を想定します。

JDBC結果セットでは、next()previous()absolute()などの移動メソッドのみを使用します。名前付きイテレータを使用してSQLJ内でこれを迅速にモデル化できます。ただし、このためには、SQL結果セットのすべての列に適切な名前が必要です。実際は、結果セットの多くの(すべてではないにしても)列でエイリアス名が必要となります。この方法は、問合せテキストを加工しないでおく場合は、使用できません。

別の方法は、問合せソースを変更しないために、結果セットの位置イテレータ型を定義する方法です。ただし、このアプローチでは、プログラムのコントロールフロー・ロジックを変更する必要があります。次のJDBCコードのサンプルで確認します。

ResultSet rs = ... // execute ...query...;
while (rs.next()) {
   x := rs.getXxx(1); y:=rs.getXxx(2);
   ...process...
}

これで、次の行をSQLJに変換します。

MyIter iter;
#sql iter = { ...query... };
while(true) {
   #sql { FETCH :iter INTO :x, :y };
   if (iter.endFetch()) break;
   ...process...
}

スクロール可能なイテレータ上での任意の移動を想定した場合、プログラム・ロジックへの変換はより困難になります。位置イテレータは名前付きイテレータの移動コマンドをすべて実装しているため、これを利用し、イテレータから変数を代入させるためにRELATIVE :(0)を使用できます。

MyIter iter;
#sql iter = { ...query... };
while (iter.next()) {
   #sql { FETCH RELATIVE :(0) FROM :iter INTO :x, :y };
   ...process...
}

これで、元の問合せおよび元のプログラム・ロジックをそのまま利用できます。ただし、このアプローチにはまだデメリットが1つあります。つまり、このプロパティが実際に必要でない場合でも、MyIterイテレータ型には、Scrollableインタフェースを実装する必要があります。これに対処するため、Oracle SQLJ実装は次の構文拡張機能をサポートしています。

#sql { FETCH CURRENT FROM :iter INTO :x, :y, :z  };

この構文の場合、スクロール可能なイテレータと同様にスクロールしないイテレータに対してもJDBCの例をSQLJに書き換えられます。

AnyIterator ai;
#sql ai = { ...query... };
while (ai.next()) {
   #sql { FETCH CURRENT FROM :ai INTO :x, :y };
   ...process...
}

スクロール可能な結果セット・イテレータ

Oracle SQLJ実装での弱い型指定の結果セットへのサポートには、スクロール可能な結果セット・イテレータ型が含まれています。

package sqlj.runtime;
public interface ScrollableResultSetIterator
                 extends ResultSetIterator
                 implements Scrollable
{ }

この型はsqlj.runtime.ResultSetIteratorを拡張しているため、「結果セット・イテレータ」のメソッドがサポートされています。

また、sqlj.runtime.Scrollableインタフェースも実装されたため、「スクロール可能なイテレータ」に説明されているメソッドがサポートされています。

さらに、スクロール可能な結果セット・イテレータでは、「スクロール可能なイテレータ」に説明されているFETCH CURRENT構文がサポートされています。

次のJDBCコードを考えます。

Statement st = conn.createStatement("SELECT first_name, employee_id FROM employees");
ResultSet rs = st.executeQuery();
while (rs.next()) { 
   x = rs.getString(1); 
   y = rs.getInt(2);
}
rs.close();

SQLJ結果セット・イテレータを使用して、同じ内容を次のコードで記述できます。

sqlj.runtime.ResultSetIterator rsi;
#sql rsi = { SELECT first_name, employee_id FROM employees };
while (rsi.next()) {
   #sql { FETCH CURRENT FROM :rsi INTO :x, :y };
}
rsi.close();

スクロール機能を活用して、次のコードを記述することもできます。

sqlj.runtime.ScrollableResultSetIterator srsi;
#sql srsi = { SELECT first_name, employee_id FROM employees };
srsi.afterLast();
while (srsi.previous()) {
   #sql { FETCH CURRENT FROM :srsi INTO :x, :y };
}
srsi.close();

詳細なトランザクション制御

SQLJでは、トランザクションのアクセス・モードと分離レベルを指定できる、SQL SET TRANSACTION文がサポートされています。標準SQLJでは、READ ONLYおよびREAD WRITEアクセス・モード設定がサポートされていますが、Oracle JDBC実装ではREAD ONLYはサポートされていません。ただし、アクセス権を設定して、アクセス・モードと同じ効果を得ることができます。分離レベルとしては、SERIALIZABLEREAD COMMITTEDREAD UNCOMMITTEDおよびREPEATABLE READを設定できます。ただし、Oracle SQL実装では、READ UNCOMMITTEDまたはREPEATABLE READはサポートされていません。

READ WRITEは、標準SQLとOracle SQL実装のデフォルトのアクセス・モードです。READ COMMITTEDはOracle SQL実装でのデフォルトの分離レベルです。SERIALIZABLEは標準SQLでのデフォルトの分離レベルです。

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

SET TRANSACTION構文

SQLJのSET TRANSACTION文の構文は次のようになります。

#sql { SET TRANSACTION <access_mode>, <ISOLATION LEVEL isolation_level> };

この文に接続コンテキスト・インスタンスを指定しなかった場合は、デフォルトの接続が適用されます。SET TRANSACTIONを使用する場合、それがDML文の前に定義され、トランザクション内で最初の文であることが必要です。つまり、データベースへの接続、あるいは最新のCOMMITまたはROLLBACK以降、最初の文であることが必要です。

標準SQLJで設定したアクセス・モードや分離レベルは、以降のトランザクション開始時に明示的にリセットしないかぎり、トランザクション全体にわたって有効のままになっています。標準SQLJ SET TRANSACTION文では、最初に分離レベルを指定することも、アクセス・モードのみ、または分離レベルのみを指定することも可能です。たとえば、次のように指定します。

#sql { SET TRANSACTION READ WRITE };

#sql { SET TRANSACTION ISOLATION LEVEL SERIALIZABLE };

#sql { SET TRANSACTION READ WRITE, ISOLATION LEVEL SERIALIZABLE };

#sql { SET TRANSACTION ISOLATION LEVEL READ COMMITTED, READ WRITE };

SET TRANSACTION文には、デフォルト接続を適用することも、特定の接続コンテキスト・インスタンスも指定できます。

#sql [myCtxt] { SET TRANSACTION ISOLATION LEVEL SERIALIZABLE };

SQLJでは、1つのSET TRANSACTION文で、アクセス・モードと分離レベルの両方を設定できます。これは、サーバー・マネージャやSQL*Plusなどの他のOracle SQLツールには当てはまらず、1つの文にアクセス・モードと分離レベルのいずれか一方しか設定できません。

アクセス・モードの設定

READ WRITEREAD ONLYの各アクセス・モード設定(サポートされている場合)の機能は次のとおりです。

  • READ WRITE (デフォルト): READ WRITEトランザクションでは、ユーザーはデータベースを更新できません。SELECTINSERTUPDATEおよびDELETEのすべてを実行できます。

  • READ ONLY (Oracle JDBC実装でもサポートされています): READ ONLYトランザクションの場合、データベースの更新はユーザーに許可されていません。SELECTは実行できますが、INSERTUPDATEDELETEおよびSELECT FOR UPDATEは実行できません。

分離レベルの設定

READ COMMITTEDSERIALIZABLEREAD UNCOMMITTEDおよびREPEATABLE READの各分離レベルには(サポートされている場合)、次の機能があります。

  • READ UNCOMMITTED: dirty read、non-repeatable read、phantom readのすべてが許可されます。

  • READ COMMITTED(デフォルト): dirty readは許可されませんが、non-repeatable readとphantom readは許可されます。トランザクションに、他のトランザクションで行をロックするDML文が含まれている場合、そのトランザクションで必要な行のロックが他のトランザクションによって解除されるまで、文がブロックされます。

  • REPEATABLE READ: dirty readとnon-repeatable readは許可されませんが、phantom readは許可されます。

  • SERIALIZABLE: dirty read、non-repeatable read、phantom readのすべてが許可されません。トランザクション内のDML文では、トランザクション開始後に変更がコミットされたリソースを更新できません。そのようなDML文は、失敗します。

dirty readは、トランザクションBがトランザクションAによって更新された行にアクセスし、その後でトランザクションAがその更新をロールバックした場合に起こります。その結果、トランザクションBは、実際にはデータベースにコミットされていないデータを扱っていることになります。

non-repeatable readは、トランザクションAが行を取得し、次にトランザクションBがその行を更新し、その後でトランザクションAが同じ行を再度取得した場合に起こります。トランザクションAは同じ行を2回取得しますが、取得した内容が異なります。

phantom readは、トランザクションAが特定の条件を満たす行のセットを取得し、次にトランザクションAの条件を満たす行をトランザクションBが挿入または更新し、その後でトランザクションAが条件に基づいて行を取得した場合に起こります。トランザクションAは追加された行を扱っていることになります。その行をphantomと呼びます。

4つの分離レベルは、次の順で移行します。

SERIALIZABLE > REPEATABLE READ > READ COMMITTED > READ UNCOMMITTED

たとえば、Oracle Database 12c リリース1 (12.1)を使用している場合に、REPEATABLE READREAD UNCOMMITTEDを設定できない場合、上位の(1つ左の)分離レベルを設定して、必要な分離レベルがカバーされるようにします。

JDBC接続クラスのメソッドの使用

必要に応じて、接続コンテキスト・インスタンスの基になるJDBC接続インスタンスのメソッドを使用して、トランザクションのアクセス・モードと分離レベルにアクセスし、それらを設定できます。ただし、これらのJDBCメソッドを使用したSQLJコードは、移植性が高くないので、お薦めしません。

次は、アクセス・モードと分離レベルを設定するConnectionクラスのメソッドです。

  • abstract int getTransactionIsolation(): カレント・トランザクションの分離レベルを示す、次のいずれかの定数の値を戻すメソッド

    TRANSACTION_NONE TRANSACTION_READ_COMMITTED TRANSACTION_SERIALIZABLE TRANSACTION_READ_UNCOMMITTED TRANSACTION_REPEATABLE_READ

  • abstract void setTransactionIsolation(int): 前述のいずれかの定数を入力パラメータとして取り、トランザクションの分離レベルを設定するメソッド

  • abstract boolean isReadOnly(): トランザクションがREAD ONLYの場合はtrueを戻します。トランザクションがREAD WRITEの場合は、falseを戻します。

  • abstract void setReadOnly(boolean): trueが入力された場合は、トランザクションのアクセス・モードをREAD ONLYに設定します。falseが入力された場合は、アクセス・モードをREAD WRITEに設定します。

SQLJとJDBCの連係動作

通常、SQLJ文は静的SQL操作で使用されます。Oracle Database 12c リリース1 (12.1)では動的SQLをサポートする拡張機能もありますが、別の方法として、動的操作のためにJDBCコードをSQLJアプリケーション内で使用することも可能であり、この方が移植性が高くなります。また、JDBCコードをSQLJアプリケーションで使用すると便利になるだけでなく、それを使用する必要がある状況もあります。このような理由から、SQLJでは、SQLJ文とJDBC文を同時に使用して、SQLJコンストラクトとJDBCコンストラクトが連係動作できるようになっています。

SQLJとJDBC間のトランザクションでは、次の2つが特に有用です。

  • SQLJ接続コンテキストとJDBC接続間のトランザクション

  • SQLJイテレータとJDBC結果セット間のトランザクション

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

SQLJ接続コンテキストとJDBC接続の連係動作

SQLJでは、SQLJ接続コンテキスト・インスタンスからJDBC接続インスタンスに変換することも、その逆に変換することも可能です。

注意:

SQLJ接続コンテキストとJDBC接続間で変換する場合、この2つのオブジェクトは基になる同じ物理接続を共有していることに注意してください。

接続コンテキストからJDBC接続への変換

JDBC操作をSQLJで確立したデータベース接続によって実行する場合(たとえば、アプリケーションでJDBC接続オブジェクトを戻すライブラリ・ルーチンをコールする場合)、SQLJ接続コンテキスト・インスタンスをJDBC接続インスタンスに変換する必要があります。

SQLJアプリケーションの接続コンテキスト・インスタンスには、sqlj.runtime.ref.DefaultContextクラスのインスタンスの場合も、宣言済の接続コンテキスト・クラスのインスタンスの場合も、基になるJDBC接続インスタンスとそのJDBC接続インスタンスを戻すgetConnection()メソッドが含まれています。JDBC操作を使用する場合は、JDBC接続インスタンスを使用してJDBC文オブジェクトを作成します。

次は、getConnection()メソッドの使用方法の例です。

import java.sql.*;

...
DefaultContext ctx = new DefaultContext 
      ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", true);
...
(SQLJ operations through SQLJ ctx connection context instance)
...
Connection conn = ctx.getConnection();
...
(JDBC operations through JDBC conn connection instance)
...

接続コンテキスト・インスタンスは、DefaultContextクラスのインスタンスでも、宣言した任意の接続コンテキスト・クラスのインスタンスでもかまいません。

デフォルトのSQLJ接続の基になるJDBC接続を取得する場合は、getConnection()DefaultContext.getDefaultContext()コールから直接使用できます。その場合、getDefaultContext()で事前にデフォルトの接続として初期化したDefaultContextインスタンスを戻し、getConnection()でその基になるJDBC接続インスタンスを戻します。この場合、DefaultContextインスタンスを明示的に使用する必要はないため、Oracle.connect()メソッドを使用することもできます。このメソッドは、暗黙的にインスタンスを生成し、それをデフォルト接続にします。

次に例を示します。

import java.sql.*;

...
Connection conn = Oracle.connect
   ("jdbc:oracle:thin:@localhost:5221/myservice", 
    "HR", "hr").getConnection();
...
(JDBC operations through JDBC conn connection instance)
...

例: 動的SQLに対するJDBCとSQLJ接続の連係動作

次は、デフォルトのSQLJ接続コンテキスト・インスタンスの基になるJDBC接続インスタンスを使用して、JDBC内で動的SQL操作を実行するメソッドの例です。JDBCのjava.sql.Connectionjava.sql.PreparedStatementおよびjava.sql.ResultSetオブジェクトによって、動的操作が実行されます。かわりに、動的SQL操作のOracle SQLJ拡張機能を使用できます。

import java.sql.*;

public static void projectsDue(boolean dueThisMonth) throws SQLException {

   // Get JDBC connection from previously initialized SQLJ DefaultContext.
   Connection conn = DefaultContext.getDefaultContext().getConnection();

   String query = "SELECT name, start_date + duration " +
                  "FROM projects WHERE start_date + duration >= sysdate";
   if (dueThisMonth)
      query += " AND to_char(start_date + duration, 'fmMonth') " +
               " = to_char(sysdate, 'fmMonth') ";

   PreparedStatement pstmt = conn.prepareStatement(query);
   ResultSet rs = pstmt.executeQuery();
   while (rs.next()) {
      System.out.println("Project: " + rs.getString(1) + " Deadline: " +
                         rs.getDate(2));
   }
   rs.close();
   pstmt.close();
}

JDBC接続から接続コンテキストへの変換

JDBC Connectionインスタンスとして接続を開始した後で、それをSQLJ接続コンテキスト・インスタンスとして使用する場合(たとえば、コンテキスト式でこのインスタンスを使用して、SQLJ実行文で使用する接続を指定する場合)は、JDBC接続インスタンスをSQLJ接続コンテキスト・インスタンスに変換します。

DefaultContextクラスとすべての宣言済の接続コンテキスト・クラスに定義されたコンストラクタは、JDBC接続インスタンスを入力パラメータとして取り、SQLJ接続コンテキスト・インスタンスを生成します。

たとえば、JDBC接続インスタンスconnをインスタンス化し、定義してある場合に、宣言済のSQLJ接続コンテキスト・クラスMyContextのインスタンスに対して、同じ接続を使用するとします。その場合は、次のように指定します。

...
#sql context MyContext;
...
MyContext myctx = new MyContext(conn);
...

共有接続について

SQLJ接続コンテキスト・インスタンスとそれに関連付けされたJDBC接続インスタンスは、基になる同じ物理接続を共有します。そのため、次の状況が発生します。

  • 接続コンテキストgetConnection()メソッドを使用して、SQLJ接続コンテキスト・インスタンスからJDBC接続インスタンスを取得すると、この接続コンテキスト・インスタンスの状態がConnectionインスタンスに継承されます。特に、Connectionインスタンスは、接続コンテキスト・インスタンスの自動コミットの設定を保持します。

  • (Connectionインスタンスを入力パラメータとしてとる接続コンテキスト・コンストラクタを使用して) JDBC接続インスタンスからSQLJ接続コンテキスト・インスタンスを作成すると、Connectionインスタンスの状態がこの接続コンテキスト・インスタンスに継承されます。特に、接続コンテキスト・インスタンスは、Connectionインスタンスの自動コミットの設定を継承します。デフォルトでは、JDBC接続インスタンスの自動コミットはtrueに設定されますが、この設定はConnectionインスタンスのsetAutoCommit()メソッドで変更できます。

  • SQLJ接続コンテキスト・インスタンスとそれに関連付けされたJDBC接続インスタンスがある場合、一方のインスタンスのセッション状態を変更するメソッドをコールすると、もう一方のインスタンスにも影響します。実際に変更されるのは、基になる共有セッションのためです。

  • 物理接続は1つのみであるため、その基になるトランザクションのセットも1つのみとなっています。ある接続インスタンスでCOMMITまたはROLLBACK操作を行うと、基になる接続を共有している他の接続インスタンスにも影響します。

注意:

同一のJDBC接続インスタンスから複数のSQLJ接続コンテキスト・インスタンスを作成すると、各インスタンスで基になる物理接続を共有することも可能となります。このようにすると、プログラム・モジュール間で同じ種類のトランザクションを共有する場合などに便利なことがあります。ただし、この場合にも前に述べた注意があてはまるので注意してください。

共有接続の終了

(getConnection()メソッドを使用して) SQLJ接続コンテキスト・インスタンスからJDBC接続インスタンスを取得する場合も、(接続コンテキスト・コンストラクタを使用して) JDBC接続インスタンスからSQLJ接続コンテキスト・インスタンスを作成する場合も、接続コンテキスト・インスタンスのみを終了する必要があります。デフォルトでは、接続コンテキスト・インスタンスのclose()メソッドをコールすると、関連付けられたJDBC接続インスタンスと、基になる物理接続が終了し、その接続に関連付けられたすべてのリソースが解放されます。

関連付けられたJDBC接続インスタンスを終了せずにSQLJ接続コンテキスト・インスタンスを終了する場合(たとえば、Connectionインスタンスが、直接または他の接続コンテキスト・インスタンスによって、別の場所で使用されている場合)、次のようにブール値の定数KEEP_CONNECTIONclose()メソッドに指定できます(ここでは、接続コンテキスト・インスタンスctxを前提としています)。

ctx.close(ConnectionContext.KEEP_CONNECTION);

KEEP_CONNECTIONを指定しないと、デフォルトで、それに関連付けされたJDBC接続インスタンスが終了します。一方、これを明示的に指定するには、次のようにします。

ctx.close(ConnectionContext.CLOSE_CONNECTION);

KEEP_CONNECTIONおよびCLOSE_CONNECTIONは、sqlj.runtime.ConnectionContextインタフェースのstatic定数です。

JDBC接続インスタンスのみを終了しても、関連付けられたSQLJ接続コンテキスト・インスタンスは終了されません。基になる物理接続は終了します。ただし、接続コンテキスト・インスタンスのリソースは、ガベージ・コレクションが行われるまで解放されません。

注意:

  • 基になる1つのJDBC接続が複数の接続コンテキスト・インスタンスで共有される場合、最後のオープン接続コンテキスト・インスタンス以外のすべての接続を終了するには、KEEP_CONNECTIONを使用します。

  • 接続コンテキスト・インスタンスを終了しようとしたときに、その基になるJDBC接続がすでに終了している場合、または基になる接続を終了しようとするときに、その接続がすでに終了している場合は、エラー・メッセージが生成されます。エラー・メッセージが表示された場合は、JDBC接続が単独でJDBCコードによって終了されていないこと、および基になる接続を使用するSQLJ接続コンテキスト・インスタンスでコールされた、先行するすべてのclose()KEEP_CONNECTIONパラメータが使用されていることを確認します。

SQLJイテレータとJDBC結果セットの連係動作

SQLJでは、SQLJイテレータからJDBC結果セットに、またはその逆に変換できます。SQLJ文のデータを選択する場合に、強い型指定のイテレータが特に必要のないときは、SQLJでJDBC結果セットに変換可能な、弱い型指定のイテレータを使用してもかまいません。

結果セットから名前付きイテレータまたは位置イテレータへの変換

JDBC結果セットの操作は、様々な状況で活用できます。たとえば、JDBCに別のパッケージが実装されており、結果セットを介してしかデータにアクセスできない場合、またはResultSetMetaData情報がどの結果セットにも使用できるように記述されたルーチンであるために、その情報が必要な場合などです。これ以外に、SQLJアプリケーションから、JDBC結果セットを戻り値とするストアド・プロシージャを起動する場合も挙げられます。

動的結果セットに構造がある場合は、それをイテレータとして操作して、そのイテレータの提供する強い型指定のパラダイムを使用します。

SQLJでは、既存のJDBC結果セット・オブジェクトを変換することで、名前付きイテレータ・オブジェクトまたは位置イテレータ・オブジェクトに移入できます。これは、結果セットをイテレータにキャストする処理であるといえます。この処理を反映する構文は次のようになります。

#sql iter = { CAST :rs };

これにより、結果セット・オブジェクトrsがSQLJ実行文にバインドされ、結果セットが変換され、イテレータiterに結果セット・データが設定されます。

次に例を示します。myEmpQuery()は、RSClassクラスのstatic Javaファンクションで、JDBC結果セット・オブジェクトを戻り値とする問合せが定義されているとします。

import java.sql.*;
...
#sql public iterator MyIterator (String ename, float sal);
...
ResultSet rs;
MyIterator iter;
...
rs = RSClass.myEmpQuery();
#sql iter = { CAST :rs };
...
(process iterator)
...
iter.close();
...

この例では、名前付きイテレータのかわりに、位置イテレータを使用することもできます。同じ機能が得られます。

次の規則は、JDBC結果セットをSQLJイテレータに変換し、データを処理する場合に適用されます。

  • 位置イテレータを変換する場合、結果セットとイテレータの列数が同じであることが必要です。また、型が正しくマッピングする必要があります。

  • 名前付きイテレータに変換する場合、結果セットに少なくともイテレータと同じ数の列が必要であり、イテレータのすべての列が名前と型で一致する必要があります。結果セットとイテレータの列の数が同じではない場合、SQLJトランスレータによって警告が生成されますが、-warn=nostrictオプションが設定されている場合を除きます。

  • キャストされている結果セットでは、java.sql.ResultSetインタフェースを実装する必要があります。クラスoracle.jdbc.OracleResultSetでは、標準結果セット・クラスと同様に、このインタフェースが実装されています。

  • キャストを受け取るイテレータは、publicと宣言されたイテレータ・クラスのインスタンスであることが必要です。

  • 変換前でも変換後でも、結果セットからデータにアクセスしないでください。データには、イテレータのみからアクセスします。

  • •操作が終了した後、結果セットではなくイテレータを終了します。イテレータを終了すると結果セットも終了しますが、結果セットを終了してもイテレータは終了しません。JDBCと連係している場合は、必ずSQLJエンティティを終了してください。

名前付きイテレータまたは位置イテレータから結果セットへの変換

当初はSQLJで定義した問合せが必要だったときでも、最終的には結果セットの方が必要になる場合があります。

注意:

SQLJでは、わかりやすくて簡単な構文が使用されています。ただし、結果を動的に処理する場合、または結果セットを入力パラメータとして取る既存のJavaメソッドを使用する場合もあります。

イテレータを結果セットに変換するために、名前付きイテレータ・クラスでも位置イテレータ・クラスでも、すべてのSQLJイテレータ・クラスはgetResultSet()メソッドで生成されます。このメソッドを使用すると、イテレータ・オブジェクトの基になるJDBC結果セット・オブジェクトが戻り値になります。

次は、getResultSet()の使用方法の例です。

import java.sql.*;

#sql public iterator MyIterator (String ename, float sal);

...
MyIterator iter;
...
#sql iter = { SELECT * FROM employees };
ResultSet rs = iter.getResultSet();
...
(process result set)
...
iter.close();
...

次の規則は、SQLJイテレータをJDBC結果セットに変換し、データを処理する場合に適用されます。

  • イテレータ・データを結果セットに書き込む場合、結果セットからのみデータにアクセスする必要があります。変換前でも変換後でも、イテレータに直接アクセスしないでください。

  • 操作が終了した後、結果セットではなく元のイテレータを終了します。イテレータを終了すると結果セットも終了しますが、結果セットを終了してもイテレータは終了しません。JDBCと連係している場合は、必ずSQLJエンティティを終了してください。

弱い型指定のイテレータ(ResultSetIterator)の使用と変換

「SQLJイテレータとJDBC結果セットの連係動作」で説明した状況においても、強い型指定のイテレータが必要ない場合があります。このような場合、問合せにSQLJ構文を使用し、結果セットからデータを動的に処理できることが必要です。その場合は、sqlj.runtime.ResultSetIterator型を直接使用して問合せデータを受け取ることができます。

JDBC文と標準の結果セットではなく、SQLJ文とResultSetIteratorを使用すると、SQLJのSELECT構文を簡略化できます。

次は、弱い型指定の結果セット・イテレータの使用方法と変換方法の例です。

import sqlj.runtime.*;
import java.sql.*;

...
ResultSetIterator rsiter;
...
#sql rsiter = { SELECT * FROM table };
ResultSet rs = rsiter.getResultSet();
...
(process result set)
...
rsiter.close();
...

注意:

Oracle SQLJ実装では、next()メソッドとFETCH CURRENT構文を使用して、結果セット・イテレータで移動できます。さらに、スクロール可能な結果セット・イテレータの場合、ナビゲーション・メソッドがサポートされています。

動的SQLのサポート

Oracle SQLJ実装には、動的SQL(事前定義されずリアルタイムに変更できる操作)をサポートする拡張機能があります。SQLJ文に埋め込まれた動的SQL式は、メタ・バインド式と呼ばれます。

注意:

JDBCコードを使用することは、Oracle Database 12c リリース1 (12.1)の動的SQLのためのオプションであり、コードの移植性がかかわる場合はお薦めする方法ですが、動的SQLのためのSQLJサポートでは、SQLJを簡略化された1つのデータ・アクセス用APIとして使用できます。

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

メタ・バインド式

メタ・バインド式は、SQLJ文で動的SQL用に使用されますが、静的SQL句が使用されています。メタ・バインド式には、String型のJava識別子、または実行時に変換される文字列の値のJava式が含まれます。さらに、SQLJではオンラインでセマンティクス・チェックを実行するので、変換時のチェックに使用するために、必要に応じてメタ・バインド式に静的SQL置換コードを指定します。

メタ・バインド式: 使用方法と制限事項の概要

次のいずれかの項目のかわりにメタ・バインド式を使用できます。

  • 表名

  • SELECT文の列名(列の別名が指定されている場合は、別名を除く)

  • WHERE句条件の一部またはすべて

  • データ定義言語(DDL)やDML文のロール名、スキーマ名、カタログ名またはパッケージ名

  • SQLリテラル値またはSQL式

メタ・バインド式では、次の制限事項に注意してください。この制限事項は、SQLJトランスレータでSQL操作の種類を確認して、SQLJ文全体の構文分析を実行するためのものです。

  • メタ・バインド式は、SQLJ文の中で、コメント以外の最初のSQL操作として使用できません。

  • メタ・バインド式には、SQLJ SELECT INTO文のINTOトークンを使用できません。また、メタ・バインド式を拡張して、SELECT INTO式のINTOリストにすることもできません。

  • メタ・バインド式は、次のいずれの種類のSQLまたはSQLJの命令および句では使用できません。使用できない命令および句は、CALLVALUESPSM SETCOMMITROLLBACKFETCH INTOおよびCASTです。

メタ・バインド式: 構文と動作

次にメタ・バインド式の一般的な構文を示します。

:{ Java_bind_expression }

または

:{ Java_bind_expression :: SQL_replacement_code }

空白はオプションであることに注意してください。SQLJ文のSQL命令内には複数のメタ・バインド式を使用できます。

Javaバインド式

Javaバインド式は次のいずれかになります。

  • String型のJava識別子

  • 文字列を評価するJava式

メタ・バインド式内のJavaバインド式には、標準のJava字句解析ルールが適用され、SQLJホスト式と同様の構文が使用されます。ただし、ホスト式と異なり、メタ・バインド式内のJavaバインド式はカッコで囲まれません。これは、SQL置換コードがある場合に、::トークンがJavaバインド式とSQLコードのセパレータとして機能するためです。SQL置換コードがない場合、閉じカッコ(})が終了記号として機能します。いずれの場合も、曖昧さはありません。

注意:

Javaバインド式内またはメタ・バインド式の:{の間には、モード指定子(INOUTまたはINOUT)を指定できません。

SQL置換コード

SQL置換コード句は、0(ゼロ)以上のSQLトークンで構成されています。次の要件と制限事項を参照してください。

  • SQL規則に準拠していることが必要です。

  • 中カッコ({ })を1組で使用する必要があります(SQLのコメント、定数または識別子の一部に使用される場合は例外です)。

  • SQL命令内にSQLJホスト式やネストしたメタ・バインド式を使用できません。

注意:

SQL置換コードを空白にすることは可能です。

変換時の動作

SQL置換コード(空白文字列のみの場合も)がメタ・バインド式にある場合は、変換時にSQLコードによってメタ・バインド式が置換されます。SQL置換コードの目的は、SQLJトランスレータでオンライン・セマンティクス・チェックを実行できるようにすることです。

SQLJ文内のメタ・バインド式にSQL置換コード句がない場合は、SQLJトランスレータでその文に対するオンライン・セマンティクス・チェックを実行できません。その文には構文チェックのみが実行されます。

実行時の動作

実行時に各メタ・バインド式は、Javaバインド式の評価によって置換されます。Javaバインド式の評価がnullになる場合、動的SQL文全体が未定義になります。

SQLJ動的SQLの例

ここでは、SQLJコードでの動的SQLの使用例を示します。

例1

...
int x = 10;
int y = x + 10;
int z = y + 10;
String table = "new_Emp";
#sql { INSERT INTO :{table :: emp} VALUES (:x, :y, :z) };
...

変換時のSQL操作は次のようになります。

INSERT INTO emp VALUES (10, 20, 30);

SQLJによって、emp表があるスキーマに対してオンライン・セマンティクス・チェックが実行されます。ランタイム・スキーマにnew_Empのみが存在し、アプリケーション実行まで作成されません。

実行時のSQL操作は次のようになります。

INSERT INTO new_Emp VALUES (10, 20, 30);

例2

...
String table = "new_Emp";
String query = "ename LIKE 'S%' AND sal>1000";
#sql myIter = { SELECT * FROM :{table :: emp2} 
                         WHERE :{query :: ename='HR'} };
...

変換時のSQL操作は次のようになります。

SELECT * FROM emp2 WHERE ename='HR';

SQLJによって、emp2表があるスキーマに対してオンライン・セマンティクス・チェックが実行されます。

実行時のSQL操作は次のようになります。

SELECT * FROM new_Emp WHERE ename LIKE 'S%' AND sal>1000;

例3

...
double raise = 1.12;
String col = "comm";
String whereQuery = "WHERE "+col+" IS NOT null";
for (int i=0; i<5; i++)
{
   #sql { UPDATE :{"emp"+i :: emp} 
          SET :{col :: sal} = :{col :: sal} * :raise :{whereQuery ::} };
}
...

変換時のSQL操作は次のようになります。

UPDATE emp SET sal = sal * 1.12;

SQLJによって、emp表があるスキーマに対してオンライン・セマンティクス・チェックが実行されます。SQL置換コードは空白なので、変換時にWHERE句はありません。

実行時にSQL操作が次のように5回実行されます。

UPDATE emp0 SET comm = comm * 1.12 WHERE comm IS NOT null;
UPDATE emp1 SET comm = comm * 1.12 WHERE comm IS NOT null;
UPDATE emp2 SET comm = comm * 1.12 WHERE comm IS NOT null;
UPDATE emp3 SET comm = comm * 1.12 WHERE comm IS NOT null;
UPDATE emp4 SET comm = comm * 1.12 WHERE comm IS NOT null;

例4

...
double raise = 1.12;
String col = "comm";
String whereQuery = "WHERE "+col+" IS NOT null";
for (int i=0; i<10; i++)
{
   #sql { UPDATE :{"emp"+i} 
          SET :{col :: sal} = :{col :: sal} * :raise :{whereQuery ::} };
}
...

例3と例4の実行時の動作は同じです。ただし、変換時には違いがあります。例4では、最初のメタ・バインド式のSQL置換コードがないため(:{"emp"+i})、SQLJでオンライン・セマンティクス・チェックを実行できません。

例5: 動的SQLと結果セット・イテレータからのFETCH

この例は、JDBC文のかわりにSQLJ文を使用して、「例: 動的SQLに対するJDBCとSQLJ接続の連係動作」を修正しています。またこの例では、結果セット・イテレータからFETCH CURRENT機能を使用しています。

import java.sql.*;

public static void projectsDue(boolean dueThisMonth) throws SQLException {

   ResultSetIterator rsi;
   String andClause = (dueThisMonth) ? 
                       " AND to_char(start_date + duration, 'fmMonth' ) " 
                       + " = to_char(sysdate, 'fmMonth') " 
                       : "";
   #sql rsi = { SELECT name, start_date + duration FROM projects
                WHERE start_date + duration >= sysdate :{andClause :: } };
   while (rsi.next())
   {
      String name = null;
      java.sql.Date deadline = null;
      #sql { FETCH CURRENT FROM :rsi INTO :name, :deadline };
      System.out.println("Project: " + name + "Deadline: " + deadline);
   } 
   rsi.close();
}

ストアド・アウトラインの使用

注意:

Oracle Database 12c リリース2 (12.2)以降、この機能は非推奨となり、SQL Plan Management (SPM)がこれに置き換わりました。この新機能は、以前より強力でパフォーマンスも向上しているため、こちらを使用することをお薦めします。SPMの詳細は、「計画ベースラインの使用」を参照してください。

環境の変化によってアプリケーションのパフォーマンスも変化するリスクがある場合は、Oracleのアウトライン機能を使用できます。アウトラインは、SQL文と関連付けられた一連のオプティマイザ・ヒントとして実装されます。文に対してアウトラインの使用を有効にすると、格納されたヒントを自動的に考慮して、ヒントに従って実行計画の生成が試みられます。アウトラインを、デフォルトのアウトラインか、クライアント指定のアウトラインかによって、いくつかのカテゴリにグループ化し、使用されるアウトラインのカテゴリを制御すると、アウトラインの管理およびデプロイメントを単純化することができます。USE_STORED_OUTLINESにカテゴリ名またはTRUEを設定すると、アウトラインのヒントが、各文の実行中に使用されます。

関連項目:

アウトラインの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

新しいアウトラインのオプションをtrueまたはカテゴリ名に設定してファイルを変換すると、次の処理が実行されます。

  1. 入力SQLJファイルに存在するすべてのSQL文に対して、CREATE OUTLINE文を含むSQLファイルが作成されます。

  2. SQL文、アウトライン名、アウトラインSQL文、アウトライン・カテゴリおよびステータス情報を含むログ・ファイルが生成されます。

  3. -runoutline optionを指定すると、入力ファイルが正常に変換された後、生成されたSQLファイルが実行されます。

アウトラインを作成するには、次のSQL文を使用できます。

  • SELECT

  • DELETE

  • UPDATE

  • INSERT ... SELECT

  • CREATETABLE...ASSELECT

アウトラインの作成時には、次の制限事項があります。

  • MERGE文にアウトラインは作成できません。

  • 複数表のINSERT文にアウトラインは作成できません。

  • アウトラインのSQL文に、リモート・オブジェクトに対するDML操作を含めることはできません。

アウトライン生成のオプション

注意:

アウトライン・オプションは、オンライン・チェックが終了した場合にのみ有効になります。

SQLJプログラムabc.sqljに次のコード例が含まれることを確認します。

{
#sql iter = {SELECT * FROM employees WHERE employee_id=:var;}
#sql iter1 = {SELECT * FROM departments};
}

次のようにSQLJプログラムをコンパイルします。

%sqlj -url=jdbc:oracle:oci8:@ -user=HR -outline=abccat abc.sqlj
Password: password

前述のSQLJコード例で生成されたSQLファイルabc_sqlj.sqlは、次のようになります。

CREATE OR REPLACE OUTLINE abccat_abc_sqlj_0001 FOR CATEGORY abccat ON SELECT * FROM employees WHERE employee_id=:B1 
/* abccat_abc_sqlj_0001 */;

CREATE OR REPLACE OUTLINE abccat_abc_sqlj_0002 FOR CATEGORY abccat ON SELECT * FROM departments 
/* abccat_abc_sqlj_0002 */;

注意:

ファイル名は、接頭辞が指定されている場合、アウトライン名またはコメントには含まれません。ここでは、接頭辞を使用する例と使用しない例を示します。接頭辞の詳細は、「sqlj.outlineprefix」を参照してください。

正常に変換された後、-outlineオプションによって、2つのファイル(SQLファイルおよびLOGファイル)が生成されます。生成されたSQLファイル名の形式は、次のようになります。

<filename>_<filetype>.sql

たとえば、ファイル名abc.sqljで生成されたSQLファイルの名前はabc_sqlj.sqlです。

アウトライン名およびコメントとして使用される一意の識別子の形式は、次のようになります。

<categoryname >_<filename>_<filetype>_<sequence no.>

順序番号は0001から9999の範囲の4桁の数字です。SQLJプログラムに9999を超えるSQL文が含まれる場合は、「アウトラインの順序番号の最大数を超えています」というエラーが発生します。たとえば、カテゴリ名がabccatの場合、abc.sqljに対して生成される一意識別子の形式はabccat_abc_sqlj_0001です。

注意:

同じコメントが、実行時に使用される生成済のJavaファイルまたはクラス・ファイルのSQLに追加されます。

アウトラインをtrueに設定すると、デフォルトのカテゴリを使用してアウトラインが格納されます。

%sqlj -user=HR -url=jdbc:oracle:oci8:@ -outline=true abc.sqlj
Password: password

この場合、生成されたSQLファイルabc_sqlj.sqlは、次のようになります。

CREATE OR REPLACE OUTLINE default_abc_sqlj_0001 ON SELECT * FROM employees WHERE employee_id=:B1 /* default_abc_sqlj_0001 */;

CREATE OR REPLACE OUTLINE default_abc_sqlj_0002 ON SELECT * FROM departments /* default_abc_sqlj_0002 */;

次のコマンドを使用して、アウトライン名を特定の接頭辞に設定できます。

%sqlj -user=HR -url=jdbc:oracle:oci8:@ -outline=abccat -outlineprefix=pref1 abc.sqlj
Password: password

この場合、生成されたSQLファイルabc_sqlj.sqlは、次のようになります。

CREATE OR REPLACE OUTLINE pref1_0001 FOR CATEGORY abccat ON SELECT * FROM employees WHERE employee_id=:B1 /* pref1_0001 */';

CREATE OR REPLACE OUTLINE pref1_0002 FOR CATEGORY abccat ON SELECT * FROM departments /* pref1_0002 */';

注意:

-outlineprefixオプションを設定した場合、トランスレータにはSQLJファイルを1つしか渡せません。

注意:

次を実行すると、複数のファイルをoutlineprefixオプションで変換できます。

%sqlj -outline=abccat -outlineprefix=pref1,pref2,pref3 abc.sqlj def.sqlj fgh.sqlj

現在、アウトライン名の長さの上限は30バイトです。このため、生成されたアウトライン名が30バイトを超える場合、「アウトライン名が上限を超えています。-outlineprefixオプションを使用してください」というSQLJエラーがスローされます。この場合、-outlineオプションを使用するには、前の例に示したように-outlineprefixオプションをコールする必要があります。SQLJで生成したアウトライン名を使用せずに、データベース・サーバーでアウトライン名を生成するには、-outlineprefixオプションにnoneを設定します。次に例を示します。

%sqlj -user=HR -url=jdbc:oracle:oci8:@ -outline=abccat -outlineprefix=none abc.sqlj
Password: password

この場合、生成されたSQLファイルabc_sqlj.sqlは、次のようになります。

CREATE OR REPLACE OUTLINE FOR CATEGORY abccat ON SELECT * FROM employees WHERE employee_id=:B1 /* abccat_abc_sqlj_0001 */';

CREATE OR REPLACE OUTLINE FOR CATEGORY abccat ON SELECT * FROM departments /* abccat_abc_sqlj_0002 */';

複数のファイルを-outlineprefixオプションを指定して変換するには、次のコマンドを使用します。

%sqlj -user=HR -url=jdbc:oracle:oci8:@ -outline=abccat -outlineprefix=pref1,pref2 abc.sqlj def.sqlj
Password: password

SQLJファイルがパッケージの一部で、-outlineprefixオプションを指定していない場合は、パッケージ名がアウトライン名およびコメントに追加されます。たとえば、abc.sqljxyz.def.fghパッケージの一部である場合、コマンド%sqlj -url=jdbc:oracle:oci8:@ -user=HR/password -outline=abccat abc.sqljを実行して生成されたSQLファイルabc_sqlj.sqlは、次のようになります。

CREATE OR REPLACE OUTLINE abccat_xyz$def$fgh$abc_sqlj_0001 FOR CATEGORY abccat ON SELECT * FROM employees WHERE employee_id=:B1 
/* abccat_xyz$def$fgh$abc_sqlj_0001 */;

CREATE OR REPLACE OUTLINE abccat_xyz$def$fgh$abc_sqlj_0002 FOR CATEGORY abccat ON SELECT * FROM departments 
/* abccat_xyz$def$fgh$abc_sqlj_0002 */;

正常に変換が終了した後、生成されたSQLファイルをトランスレータによって実行するには、runoutlineオプションにtrueを設定します。デフォルトはfalseです。次に例を示します。

%sqlj -user=HR -url=jdbc:oracle:oci8:@ -outline=default -runoutline=true abc.sqlj
Password: password

ここで、SQLコード計画をエクスポートまたは修正するためにアウトライン名を取得する場合は、手動でまたはツールを使用してOL$表から取得できます。コメントはSQL文を一意に識別するため、SQL問合せのコメントを使用して適切なSQL文を検索し、アウトライン名を識別できます。

表8-1に、アウトラインを生成するためにトランスレータに渡すすべてのオプションおよび値を示します。

表8-1 アウトラインを生成するオプションおよび値

オプション名 オプション値

-outline

true<category name>

-outlineprefix

none<prefix name>|none<prefix name 1, prefix name 2,…>

-runoutline

true|false

関連項目:

アウトラインの詳細は、『Oracle Databaseリファレンス』を参照してください。

生成されるLOGファイルの名前

生成されるファイルの名前の形式は、次のようになります。

<filename>_<filetype>.log

たとえば、abc.sqljを変換する場合、生成されるログ・ファイルはabc_sqlj.log.になります。

生成されるLOGファイルの形式

次のコード例があるとします。

#sql iter = {SELECT * FROM employees WHERE employee_id=:var };
#sql iter1 = {SELECT * FROM departments };

前述のコードに対して生成されるログ・ファイルは、次のようになります。

CATERGORY abccat
Source SQL_1
SELECT * FROM employees WHERE employee_id=:B1
OUTLINE NAME
abccat_abc_sqlj_0001
OUTLINE SQL_1
CREATE OR REPLACE OUTLINE abccat_abc_sqlj_0001 FOR CATEGORY abccat ON SELECT * FROM employees WHERE employee_id = :B1 
/* abccat_abc_sqlj_0001 */
STATUS success
Source SQL_2
SELECT * FROM departments
OUTLINE NAME
abccat_abc_sqlj_0002
OUTLINE SQL_2
CREATE OR REPLACE OUTLINE abccat_abc_sqlj_2 FOR abccat ON SELECT * FROM departments
/* abccat_abc_sqlj_2 */
STATUS fail

前述の例では、生成されるログ・ファイルの形式について、次のことを示しています。

  • カテゴリは、生成されるアウトラインのカテゴリを意味します。

  • ソースは、アウトラインが生成されるSQL文を意味します。

  • アウトライン名は、生成されるアウトラインの名前です。

  • ステータスは、ソースとして使用されるSQL文の実行ステータスです。正常に実行されると、ステータスはsuccessになります。それ以外は、failになります。

構成ファイル

構成ファイルには、次のように様々なコマンドライン・オプションを設定できます。

sqlj.outline

  • パラメータ名: outline

  • パラメータ・タイプ: String

  • 許容値: {true|category_name}

  • デフォルト値: true

  • 詳細: SQL文に対するアウトラインSQLファイルを生成して、次の場所に格納する必要があります。

    • 値がデフォルトのtrueの場合はDEFAULTカテゴリ

    • category_nameが示されている場合は該当するカテゴリ

    このオプションを使用しないと、アウトラインSQLファイルは生成されません。

  • その他のパタメータへの依存性: このオプションが設定されているときは、オンライン・チェックをフルにする必要があります。

sqlj.runoutline

  • パラメータ名: runoutline

  • パラメータ・タイプ: boolean

  • 許容値: {true|false}

  • デフォルト値: false

  • 詳細: runoutline=trueに設定されている場合、生成されるSQLファイルは、正常に変換が終了した後でトランスレータによって実行される必要があります。

  • その他のパタメータへの依存性: このオプションが設定されているときは、オンライン・チェックをフルにして、アウトライン・オプションを設定する必要があります。

sqlj.outlineprefix

  • パラメータ名: outlineprefix

  • パラメータ・タイプ: String

  • 許容値: {prefix name}, none

  • 詳細: このオプションを設定すると、生成されるSQLのアウトライン名は、<prefix>_<seqno>に設定されます。このオプションをプロパティ・ファイルのnone以外の値に設定すると、1つのSQLJファイルのみがトランスレータに渡されます。このオプションをnoneに設定すると、create outline文をサーバーで実行する際にシステムによってアウトライン名が生成されます。また、–outlineprefixをnoneに設定すると、複数のファイルをトランスレータに渡すことができます。

  • その他のパタメータへの依存性: このオプションが設定されているときは、オンライン・チェックをフルにして、アウトライン・オプションを設定する必要があります。

計画ベースラインの使用

Oracle Database 12c リリース2 (12.2)以降、SQLJではOracle Database SQL Plan Management (SPM)を使用した計画ベースラインの作成がサポートされています。SQLJファイルの変換時に計画ベースラインを生成できます。計画ベースラインの作成に必要なSQL文は.sqlファイルに生成されます。計画ベースラインは、SQLJアプリケーションのデプロイ前に確認、調整および修正できます。

関連項目:

計画ベースラインの詳細は、『Oracle Database SQLチューニング・ガイド』を参照してください

この項では、次の項目について説明します。

計画ベースラインの生成

SPMでサポートされるSQL文すべてに対して計画ベースラインを生成できます。生成されるログ・ファイルには、サポートされていない文があれば、その文がレポートされます。

パラメータ

計画ベースラインのオプションを指定すると、SQLJにより、dbms_spm_internal.create_sql_plan_baselineプロシージャがコールされてSQLファイルが生成されます。このプロシージャのパラメータは、次のとおりです。

パラメータ 説明

SQL_TEXT

計画ベースラインを作成する必要があるSQLテキストを指定します。

PARSING_SCHEMA

渡されるSQLテキストのセマンティク・チェックに使用されるスキーマを指定します。

PLAN_NAME

計画ベースラインの名前を指定します。このパラメータは省略可能です。このパラメータが指定されていない場合、デフォルトのプラン名はdefaultです。

ENABLED

プランを有効にするかどうかを指定します。デフォルト値はyesです。

FIXED

プランが固定プランかどうかを指定します。デフォルト値はnoです。

注意:

  • 計画ベースラインのSQL文を生成して実行するには、DBMS_SPM_INTERNALパッケージに対するExecute権限と、Administer SQL Management Object権限が必要です。

  • 計画ベースラインのオプションは、オンライン・セマンティック・チェックが実行される場合のみ有効です。これらのオプションをオフライン・セマンティック・チェックとともに指定すると、オプションは無視され、オプションはオンライン・セマンティック・チェックのみとともに使用する必要があることを通知する警告がスローされます。

コマンドラインおよびプロパティ・ファイルのオプション

計画ベースラインのSQL文の生成には、次のコマンドライン・オプションを使用します。

注意:

生成されたファイルでは、ファイルを実行するのに適切なユーザーを指定しています。たとえば、生成されたファイルの次の文では、HRがファイルを実行できることを指定しています。

var ORA_SPM_PARSE_SCHEMA varchar2(30);
exec :ORA_SPM_PARSE_SCHEMA:='HR';
plan_baseline

plan_baselineオプションは、SQLファイル内のSQL文全部に対して計画ベースラインを生成するかどうかを指定する場合に使用します。このオプションをtrueに設定すると、defaultがベースライン名として使用されます。ベースライン名は、アウトラインの使用時のカテゴリ名に相当します。このオプションに指定した値は、SQLファイルの実行時にモジュール名として使用されます。

SQLファイルの名前は順序番号を付けて変換され、その組合せがSQLファイルの各SQL文を一意に識別するために使用されます。この組合せは、計画名としても使用されます。順序番号は、0から9999までの範囲です。計画名の形式は、次のとおりです。

<filename>_<filetype>_<sequence_no>

構文

コマンドラインでは、plan_baselineオプションを次のように指定します。

-plan_baseline= <true/false/module_name>

プロパティ・ファイルでは、plan_baselineオプションを次のように指定します。

sqlj.plan_baseline=<true/false/module_name>

デフォルト値

plan_baselineオプションのデフォルト値はfalseで、その場合、計画ベースラインは生成されません。このオプションにtrueを指定した場合、モジュール名はdefaultです。

sqlj test.sqlj –plan_baseline=true -user=HR/hr

test.sqljmypackageというパッケージの一部であり、次の2つのSQL文のみが含まれているとします。

Select first_name from employees;
Select employee_id from employees;

この場合、生成されるSQLファイルの内容は次のとおりです。

var ORA_SPM_PARSE_SCHEMA varchar2(30);
exec :ORA_SPM_PARSE_SCHEMA:='HR';
begin
    dbms_application_info.set_module(‘default','');
end;
 
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'mypackage_test_sqlj_0000') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select first_name from employees /*mypackage_test_sqlj_0000*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'mypackage_test_sqlj_0000', 
   'no', 
   'no'); 
END ; 
/
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'mypackage_test_sqlj_0001') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select employee_id from employees /*mypackage_test_sqlj_0001*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'mypackage_test_sqlj_0001', 
   'no', 
   'no'); 
END ; 
/

test.sqljに、次の2つのSQL文のみが含まれているとします。

Select first_name from employees;
Select employee_id from employees;

また、SPM計画名を次のように指定するとします。

sqlj –plan_name=mybaseline -user=HR/hr test.sqlj

この場合、生成されるSQLファイルの内容は次のとおりです。

var ORA_SPM_PARSE_SCHEMA varchar2(30) ; 
exec :ORA_SPM_PARSE_SCHEMA:='HR'; 
begin
    dbms_application_info.set_module(‘mybaseline,'');
end;
 
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'mypackage_test_sqlj_0000') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select first_name from employees /*mypackage_test_sqlj_0000*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'mypackage_test_sqlj_0000', 
   'no', 
   'no'); 
END ; 
/
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'mypackage_test_sqlj_0001') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select employee_id from employees /*mypackage_test_sqlj_0001*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'mypackage_test_sqlj_0001', 
   'no', 
   'no'); 
END ; 
/
plan_prefix

plan_prefixオプションは、計画の名前を指定する場合に使用します。これは、create_sql_plan_baselineプロシージャのPLAN_NAME引数に対応します。このオプションを使用しない場合、計画名は自動的に生成されます。

構文

コマンドラインでは、plan_prefixオプションを次のように指定します。

-plan_prefix=<name>

プロパティ・ファイルでは、plan_prefixオプションを次のように指定します。

sqlj.plan_prefix=<name>

デフォルト値

plan_prefixオプションの値は、noneです。このオプションに他の値を指定すると、計画名の形式は次のようになります。

<name>_<sequence_no>

sqlj test.sqlj –plan_baseline=mybaseline true -user=HR/hr –plan_prefix=myprefix

test.sqljに、次の2つのSQL文のみが含まれているとします。

Select first_name from employees;
Select employee_id from employees;

この場合、生成されるSQLファイルの内容は次のとおりです。

var ORA_SPM_PARSE_SCHEMA varchar2(30);
exec :ORA_SPM_PARSE_SCHEMA:='HR';
begin
    dbms_application_info.set_module(‘default','');
end;
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'myprefix_0000') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select first_name from employees /*myprefix_0000*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'myprefix_0000', 
   'no', 
   'no'); 
END ; 
/
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'myprefix_0001') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select employee_id from employees /*myprefix_0001*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'myprefix_0001', 
   'no', 
   'no'); 
END ; 
plan_run

plan_runオプションは、生成されたSQLファイルを変換の最後にSQLJで実行するかどうかを指定する場合に使用します。

注意:

生成されたSQLファイルを実行するには、次の権限が必要です

  • DBMS_SPM_INTERNALパッケージに対するExecute権限

  • Administer SQL Management Object権限

構文

コマンドラインでは、plan_runオプションを次のように指定します。

-plan_run=<yes|no>

プロパティ・ファイルでは、plan_runオプションを次のように指定します。

sqlj.plan_run=<yes|no>

デフォルト値

このplan_runオプションのデフォルト値はyesです。

plan_fixed

plan_fixedオプションは、生成されたベースラインを修正するかどうかを指定する場合に使用します。

構文

コマンドラインでは、plan_fixedオプションを次のように指定します。

-plan_fixed = <yes|no>

プロパティ・ファイルでは、plan_fixedオプションを次のように指定します。

sqlj.plan_fixed=<yes|no>

デフォルト値

このオプションのデフォルト値は、yesです。

plan_enabled

plan_enabledオプションは、生成されたベースラインを有効にするかどうかを指定する場合に使用します。

構文

コマンドラインでは、plan_enabledオプションを次のように指定します。

-plan_enabled = <yes|no>

プロパティ・ファイルでは、plan_enabledオプションを次のように指定します。

sqlj.plan_enabled=<yes|no>

デフォルト値

このオプションのデフォルト値は、yesです。

生成されるSQLファイル

「コマンドラインおよびプロパティ・ファイルのオプション」で説明されているオプションを指定したSQLJファイルのプリコンパイルの最後に、SQLファイルが生成されます。このSQLファイルには、SQLJファイルの各SQL文に対してSPM計画を作成するためのSQL文が入っています。

この項では、次の項目について説明します。

生成されるSQLファイルの名前

生成されるSQLファイルの名前は次の形式になります。

<filename>_<filetype>_bln.sql

SQLJの場合、ファイル・タイプは常に.sqljです。したがって、SQLファイルの名前は必ず次の形式になります。

<filename>_sqlj_bln.sql
生成されるSQLファイルの形式

test.sqljファイルがmypackageというパッケージの一部であり、次のSQL文が含まれているとします。

#sql  {select * from employees };
#sql  {select manager_id from employees };

次のコマンドを使用してファイルをプリコンパイルするとします。

sqlj test.sqlj –plan_baseline=mybaseline –plan_prefix=myprefix -userid=HR/hr

この場合、生成されるSQLファイルmypackage_test_sqlj_bln.sqlの内容は次のとおりです。

var ORA_SPM_PARSE_SCHEMA varchar2(30) ;
exec :ORA_SPM_PARSE_SCHEMA:='HR';
begin
    dbms_application_info.set_module(‘default','');
end;
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'myprefix_0000') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select first_name from employees /*myprefix_0000*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'myprefix_0000', 
   'no', 
   'no'); 
END ; 
/
BEGIN 
  BEGIN 
    d := SYS.DBMS_SPM.DROP_SQL_PLAN_BASELINE( PLAN_NAME => 'myprefix_0001') ; 
    EXCEPTION 
    WHEN OTHERS THEN NULL; 
  END; 
  c:=SYS.DBMS_SPM_INTERNAL.CREATE_SQL_PLAN_BASELINE( 
   'Select employee_id from employees /*myprefix_0001*/', 
   :ORA_SPM_PARSE_SCHEMA, 
   'myprefix_0001', 
   'no', 
   'no'); 
END ;

注意:

  • 順序番号0000および0001が各SQL文の計画名を一意に識別するために使用されます。

  • plan_prefixオプションでnone以外の値を指定した場合、–plan_baselineオプションで指定した値ではなく、接頭辞値が使用されます。

生成されるログ・ファイル

プリコンパイルの最後にSQLファイルが生成されます。ここでは、ログ・ファイルの次の詳細について説明します。

生成されるログ・ファイルの名前

生成されるログ・ファイルの名前は次の形式になります。

<filename>_<filetype>_bln.log

SQLJの場合、ファイル・タイプは常に.sqljです。したがって、SQLファイルの名前は必ず次の形式になります。

<filename>_sqlj_bln.log
生成されるログ・ファイルの形式

test.sqljファイルがmypackageというパッケージの一部であり、次のSQL文が含まれているとします。

#sql  {select * from employees };
#sql  {select manager_id from employees };

次のコマンドを使用してファイルをプリコンパイルするとします。

sqlj test.sqlj –plan_baseline=true userid=HR/hr

この場合、生成されるログ・ファイルの内容は次のとおりです。

MODULE default 
    SOURCE SQL_0
       select * from employees;
    PLAN NAME 
       mypackage_test_sqlj_0000 
    STATUS Success 
 
/******************************************/ 
/******************************************/ 
MODULE default 
    SOURCE SQL_1
       select manager_id from employees 
    PLAN NAME 
       mypackage_test_sqlj_0001 
    STATUS Success 
 
/******************************************/

次のコマンドを使用してファイルをプリコンパイルするとします。

sqlj test.sqlj –plan_baseline=true  userid=HR/hr –plan_prefix=myprefix

この場合、生成されるログ・ファイルの内容は次のとおりです。

MODULE default 
    SOURCE SQL_0
       select * from employees;
    PLAN NAME 
       myprefix_0000 
    STATUS Success 
 
/******************************************/ 
/******************************************/ 
MODULE default 
    SOURCE SQL_1
       select manager_id from employees 
    PLAN NAME 
       myprefix_0001 
    STATUS Success 
 
/******************************************/

生成されるJavaファイル

プリコンパイルの最後にJavaファイルが生成されます。

test.sqljファイルがmypackageというパッケージの一部であり、次のSQL文が含まれているとします。

#sql {select * from employees };

次のコマンドを使用してファイルをプリコンパイルするとします。

sqlj test.sqlj –plan_baseline=mybaseline –plan_prefix=myprefix -userid=HR/hr

この場合、生成されるJavaファイルには、次のようにSQL文の最後に付加される識別子が入っています。

try {
   String theSqlTS = "select first_name from employees  /*mybaseline_test_sqlj_0001*/";
   __sJT_st = __sJT_ec.prepareOracleStatement(__sJT_cc,"0Select",theSqlTS);
   // execute query
   iter = new Iter(new sqlj.runtime.ref.OraRTResultSet(__sJT_ec.oracleExecuteQuery(),__sJT_st,"0Select",null));
  } finally { __sJT_ec.oracleCloseQuery(); }