4 プログラミング上の主な考慮事項

この章では、SQLJアプリケーションの開発および実行前に検討する必要がある重要な問題について説明し、要約とサンプル・アプリケーションも示します。次の内容について説明します。

4.1 JDBCドライバの選択

Java Database Connectivity(JDBC)ドライバの選択にあたっては、変換時と実行時にそれぞれ別のドライバを使用するかどうかを検討する必要があります。変換用と実行用の各ドライバ・クラスを選択または登録し、そのドライバを接続URLで指定してください。

注意:

Oracle固有コード生成を使用する場合、またはISO SQLJ標準コード生成とOracleカスタマイザを使用する場合は、Oracle JDBCドライバが必要です。Oracle固有の機能を実際には使用しない場合にも必要です。

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

4.1.1 Oracle JDBCドライバの概要

Oracle JDBCドライバを次に示します。

  • Oracle Call Interface(OCI)ドライバ: Oracleクライアント環境とともにクライアント側で使用します。

  • Thinドライバ: Pure Javaで記述されたドライバで、クライアント側で特にアプレットから使用します。Oracleクライアント環境は不要です。

  • サーバー側Thinドライバ: クライアント側Thinドライバと同様の機能を備えていますが、Oracle Databaseインスタンス内で実行するコード用のドライバでありリモート・サーバーにアクセスするためのものです。

  • サーバー側内部ドライバ: ターゲット・サーバー内(つまり、アクセスするOracle Databaseインスタンス内)で実行するコード用のドライバ。

Oracle Database 12c リリース1 (12.1)ではJDK 6およびJDK 7と互換性があるクライアント側ドライバが用意されています。

注意:

変換時と実行時とで、別々のドライバを選ぶ場合もあります。具体的には、変換時のセマンティクス・チェックにはOracle JDBC OCIドライバを使用し、実行時にはOracle JDBC Thinドライバを使用します。

JDBCの中核となる機能

Oracle JDBCドライバは、いずれも同様の機能性を備えています。各ドライバでサポートされている機能セット、構文、プログラミング・インタフェースおよびOracle拡張型は、すべて同じです。

Oracle JDBCドライバはすべて、oracle.jdbc.OracleDriverクラスでサポートされています。

JDBC OCIドライバ

Oracle JDBC OCIドライバでは、OCIを直接Javaからコールしてデータベースにアクセスできるため、Oracle Databaseの様々なバージョンとの高度な互換性が確保されています。これらのドライバでは、インストール済Oracle Netアダプタ(プロセス間通信(IPC)、Named Pipes、TCP/IP、IPX/SPXなど)がサポートされています。

ネイティブ・メソッドを使用してCエントリ・ポイントをコールする際、このOCIドライバはOracleプラットフォームに依存するため、Oracle NetをはじめとするOracleクライアントのインストールが必要になります。このため、これはアプレットには適していません。

OCIドライバ用の接続文字列は次の形式になります(tnsは、オプションでTNSの別名またはTNS完全指定です)。

jdbc:oracle:oci:@<tns>

注意:

下位互換性のため、ociのかわりにoci8も使用できます。

JDBC Thinドライバ

Oracle JDBC Thinドライバは、プラットフォームに依存しないPure Javaの実装であり、Javaソケットを使用してOracleまたはOracle以外のクライアントから直接Oracle Databaseに接続できます。アプレットの実行と同時にこのドライバをブラウザにダウンロードすることも可能です。

このJDBC Thinドライバでは、TCP/IPプロトコルしかサポートされていないため、データベース・サーバーからのTCP/IPソケットをリスニングするTNSリスナーが必要です。このJDBC Thinドライバとアプレットとを併用する場合、Javaソケットをサポートしているクライアント・ブラウザを使用してください。

JDBC Thinドライバ用の接続文字列は、通常次の形式です。

jdbc:oracle:thin:@host:port/servicename

関連項目:

データベース・サービス名の詳細は『Oracle Database JDBC開発者ガイド』を参照してください

Oracle Database 12c リリース2 (12.2)では、SIDを使用した接続文字列は非推奨ですが、下位互換性の目的で引き続きサポートされています。

jdbc:oracle:thin:@host:port:sid

JDBCサーバー側Thinドライバ

Oracle JDBCサーバー側のThinドライバは、クライアント側のJDBC Thinドライバと同様の機能性を備えていますが、さらにデータベース内で動作し、リモート・サーバーにアクセスできます。このドライバは、あるOracle Databaseインスタンス内部(Javaストアド・プロシージャなど)から別のOracle Databaseインスタンスへのアクセスに利用できます。

サーバー側Thinドライバ用の接続文字列は、クライアント側Thinドライバ用と同じです。

注意:

サーバー側Thinドライバの使用時に、元のデータベースをそのままにしておくためには、ユーザー・アカウントにSocketPermissionを割り当てる必要があります。詳細は、『Oracle Database JDBC開発者ガイド』を参照してください。また、SocketPermissionおよび他のアクセス権の概要は、『Oracle Database Java開発者ガイド』を参照してください。

サーバー側JDBC内部ドライバ

Oracle JDBCサーバー側内部ドライバは、SQL操作を行うターゲットのOracle Databaseインスタンスで実行される、あらゆるJavaコードをサポートしています。サーバー側内部ドライバを使用すると、Oracle Java Virtual Machine(JVM)から直接SQLエンジンに通信できるようになります。このドライバは、Oracle Database 12c リリース2 (12.2)でストアド・プロシージャ、ストアド・ファンクションまたはトリガーとしてSQLJコードを実行するためのデフォルトのJDBCドライバです。

サーバー側内部ドライバ用の接続文字列は、次の形式です。

jdbc:oracle:kprb:

SQLJコードでデフォルトの接続コンテキストを使用する場合、SQLJでは、Oracle JVMで実行するコード用に自動的にこのドライバが使用されます。

4.1.2 変換用ドライバの選択

ドライバ・マネージャ・クラスを選択し、変換用のドライバを指定するには、コマンドラインまたはプロパティ・ファイルでSQLJオプションを設定します。

OracleDriver(デフォルト)以外のドライバ・マネージャ・クラスを選択するには、SQLJの-driverオプションを使用します。

SQLJの-urlオプションで接続URLを指定するときは、Oracle Database用のJDBC ThinドライバやJDBC OCIドライバなど、選択した特定のJDBCドライバも一緒に指定します。

通常は、ソース・コードでランタイム接続用に指定したドライバを使用します。

注意:

前述の-driverオプションは、特定のドライバの選択には使用できません。このオプションを使用すると、ドライバ・マネージャにあるドライバ・クラスを登録します。そのドライバ・クラスは、複数のドライバ・プロトコル(すべてのOracle JDBCプロトコルで使用されるOracleDriverなど)に使用できます。

4.1.3 実行時に使用するドライバの選択および登録

実行時にデータベースに接続するには、接続インスタンス(sqlj.runtime.ref.DefaultContextクラスまたは宣言済の接続コンテキスト・クラスのインスタンス)のいずれかに対して指定したURLを認識するドライバを1つ以上登録する必要があります。

Oracle JDBCドライバを使用している場合に、Oracle.connect()メソッドでデフォルトの接続を作成すると、SQLJはこれを自動的に処理します。Oracle.connect()メソッドによって、oracle.jdbc.OracleDriverクラスが登録されます。

Oracle JDBCドライバを使用し、Oracle.connect()を使用しない場合は、次のようにOracleDriverクラスを手動で登録する必要があります。

DriverManager.registerDriver(new oracle.jdbc.OracleDriver());

Oracle JDBCドライバを使用しない場合は、次のように適切なドライバ・クラスを登録する必要があります。

DriverManager.registerDriver(new mydriver.jdbc.driver.MyDriver());

どの場合でも、接続URL、ユーザー名およびパスワードの設定が必要です。

注意:

JDBC接続を確立する際にJDBCドライバ・マネージャのかわりに、データ・ソースを使用することができます。「WITH句宣言」で説明するように、with句にデータ・ソースを指定できます。データ・ソースの概要は、『Oracle Database JDBC開発者ガイド』を参照してください。

4.2 接続の際の考慮事項

SQLJアプリケーションで使用するデータベース接続の選択にあたっては、次の点を考慮してください。

  • 必要なデータベース接続が単一であるか複数であるか。

  • 複数の接続(また、必要に応じて複数のスキーマ)を使用する場合、各接続で同じ名前のSQLエンティティ、つまり、表の名前、列の名前とデータ型、ストアド・プロシージャの名前とシグネチャなどは、各接続のたびに同じ名前のものを使用するか。

  • 変換時と実行時にそれぞれ別の接続を使用するか、同じ接続を使用するか。

データベース接続用の接続コンテキストのインスタンス(DefaultContextまたは宣言した接続コンテキスト・クラスのインスタンス)は、SQLJ実行文で指定します。接続コンテキスト仕様を指定せずに、デフォルトの接続(デフォルトとしてあらかじめ設定したDefaultContextのインスタンス)を使用することも可能です。

注意:

操作の際に数種類のSQLエンティティを使用する場合は、通常、新たに接続コンテキスト・クラスを宣言し、使用します。

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

4.2.1 DefaultContextを使用した単一接続または複数接続

ここでは、DefaultContextクラスの接続インスタンスのみを使用する場合について説明します。

単一接続や、名前とデータ型が同じSQLエンティティを使用する複数接続では、代表的な方法です。

単一接続

単一接続の場合は、DefaultContextクラスのインスタンスを1つ使用します。DefaultContextオブジェクトの作成時に、データベースURL、ユーザー名およびパスワードを指定します。

この作業は、oracle.sqlj.runtime.Oracleクラスのconnect()メソッドで行えます。このメソッドをコールすると、デフォルトの接続コンテキスト・インスタンスが自動的に初期化されます。このメソッドには複数のシグネチャがあり、たとえば、ユーザー名、パスワードおよびURLを直接指定できるものや、プロパティ・ファイルに指定するものがあります。次に、プロパティ・ファイルconnect.propertiesを使用した例を示します。

Oracle.connect(MyClass.class, "connect.properties");

注意:

connect.propertiesファイルは、指定したクラスに基づいて検索されます。たとえばMyClassmy-packageにある場合、connect.propertiesは同じパッケージの場所であるmy-packageで検索されます。

connect.propertiesを使用する場合は、必要な編集とアプリケーションでのパッケージ化を行ってください。この例では、oracle.sqlj.runtime.Oracleクラスのインポートも必要です。

次のようにすると、ユーザー名、パスワードおよびURLを直接指定できます。

Oracle.connect("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr");

この例では、JDBC Thinドライバを使用してHRユーザー(パスワードhr)をコンピュータlocalhost上のデータベースに、ポート5221経由で接続します。myserviceは、接続用のデータベース・サービスの名前です。

どちらの場合も、DefaultContextクラスの特別な静的インスタンスが生成され、デフォルトの接続としてインストールされます。DefaultContextのインスタンスを直接操作する必要はありません。

ここまでの手順を終えた後、アプリケーションのSQLJ実行文に対していっさい接続を指定しないことも可能です。ただし、その場合は常にデフォルトの接続が使用されます。

JDBC Thinドライバを使用する場合は、前の例で示したように、ホスト名、ポート番号およびサービス名(またはSID、ただしOracle Database 12c リリース2 (12.2)では非推奨)をURLに含める必要があります。また、データベースには、指定したポートで実行されるリスナーが必要です。JDBC OCIドライバを使用する場合、クライアントのデフォルト・アカウントを使用するときはサービス名(またはSID)は不要です。このドキュメントの例はこれに該当します。また、名前-値ペアを使用することもできます。

関連項目:

詳細は、『Oracle Database JDBC開発者ガイド』を参照してください

次のURLでは、クライアントのデフォルト・アカウントに接続できます。

jdbc:oracle:oci:@

注意:

  • デフォルトの接続がすでに設定されていれば、Oracle.connect()を指定した場合でもデフォルトの接続が再設定されることはありません。この場合は、NULLが戻されます。この機能により、クライアントとサーバーで同じコードを使用できます。デフォルトの接続を指定変更するには、DefaultContextのstatic setDefaultContext()メソッドを使用します。

  • Oracle.connect()メソッドの自動コミット・フラグのデフォルト値はfalseです。ただし、このメソッドのシグネチャは明示的に設定できます。Oracle JDBC実装の自動コミット・フラグのデフォルト値はtrueです。

  • Oracle.connect()をコールする際に、必要に応じて、MyClass.classのかわりにgetClass()を指定できますが、この指定はgetClass()をstaticメソッドからコールしない場合にのみ有効です。getClass()メソッドは、一部のSQLJデモ・アプリケーションで使用されています。

  • 次のように、static DefaultContextインスタンスに接続できます。これは、デフォルトの接続に対応しています。

    DefaultContext.getDefaultContext();
    

複数接続

複数接続の場合は、DefaultContextクラスのインスタンスをさらに作成して使用します。デフォルト接続もそのまま使用できます。

DefaultContextのインスタンス化するには、次の例のようにOracle.getConnection()メソッドを使用します。

最初に、デフォルト接続を大半の文で使用し、残りの文では別の接続を使用するとします。DefaultContextのインスタンスを新たに1つ作成する必要があります。

DefaultContext ctx = Oracle.getConnection (
   "jdbc:oracle:thin:@localhost2:5221/myservice2", "bill", "lion");

注意:

同一スキーマに対して複数の操作を行う場合は、ctxHR/hrスキーマを使用することも可能です。

デフォルトの接続を使用する場合は、接続コンテキストを指定する必要はありません。

#sql { SQL operation };

これは実際には、次の形式を省略しています。

#sql [DefaultContext.getDefaultContext()] { SQL operation };

追加した接続を使用する場合は、接続としてctxを指定します。

#sql [ctx] { SQL operation };

次に、複数の接続を使用する状況を想定し、各接続を名前付きDefaultContextインスタンスにします。これによって、接続を交互に切り替えることができます。

次の文は、同一スキーマに対して複数の接続を確立します(複数のOracle Databaseセッションやトランザクションを使用する場合など)。接続ごとにDefaultContextクラスをインスタンス化します。

DefaultContext ctx1 = Oracle.getConnection
   ("jdbc:oracle:thin:@localhost1:5221/myservice1", "HR", "hr");
DefaultContext ctx2 = Oracle.getConnection
   ("jdbc:oracle:thin:@localhost1:5221/myservice1", "HR", "hr");

接続コンテキストのインスタンスが2つ作成されます。どちらも、Oracle JDBC Thinドライバを介して、コンピュータlocalhost1上でサービスmyservice1を使用してHR/hrに接続します。

次に、スキーマごとにそれぞれ別の接続を使用する場合を想定します。この場合も、接続ごとにDefaultContextクラスをインスタンス化します。

DefaultContext ctx1 = Oracle.getConnection
   ("jdbc:oracle:thin:@localhost1:5221/myservice1", "HR", "hr");
DefaultContext ctx2 = Oracle.getConnection
   ("jdbc:oracle:thin:@localhost2:5221/myservice2", "bill", "lion");

接続コンテキストのインスタンスが2つ作成されます。この2つのインスタンスは、Oracle JDBC Thinドライバを使用し、それぞれ別のスキーマを使用します。ctx1オブジェクトは、コンピュータlocalhost1上のサービスmyservice1を使用してHR/hrに接続し、ctx2オブジェクトは、コンピュータlocalhost2上のサービスmyservice2を使用してbill/lionに接続します。

アプリケーションのSQLJ実行文でこれらの接続を交互に切り替える方法には、次の2通りがあります。

  • 接続の切替え頻度が高い場合に、アプリケーションの各文に対して、次のように接続を指定する方法。

    #sql [ctx1] { SQL operation };
    ...
    #sql [ctx2] { SQL operation };
    

    注意:

    接続コンテキストのインスタンスの名前は、必ず大カッコで囲みます。大カッコも構文の構成要素です。

  • コード・フロー内の行で片方の接続を複数回連続して使用する場合、デフォルト接続をリセットするために、DefaultContextクラスのstatic setDefaultContext()メソッドを定期的に使用する方法。このメソッドによって、デフォルトの接続コンテキスト・インスタンスが初期化されます。この方法では、SQLJ文で接続を指定する必要がありません。

    DefaultContext.setDefaultContext(ctx1);
    #sql { SQL operation };   // These three statements all use ctx1
    #sql { SQL operation };
    #sql { SQL operation };
    ...
    DefaultContext.setDefaultContext(ctx2);
    #sql { SQL operation };   // These three statements all use ctx2
    #sql { SQL operation };
    #sql { SQL operation };
    

    注意:

    前述の文では接続コンテキストを指定していないため、変換時にデフォルトの接続コンテキストかどうかが文ごとにチェックされます。

4.2.2 接続の終了

接続が完了した後、接続コンテキスト・インスタンスをクローズしてください。tryブロックのfinally句(アプリケーションが例外時に終了した場合)でクローズを指定することをお薦めします。

DefaultContextクラス、および宣言したその他のあらゆる接続コンテキスト・クラスには、close()メソッドが用意されています。このclose()メソッドをコールすると、SQLJ接続コンテキスト・インスタンスが終了し、デフォルトでは、基になるJDBC接続インスタンスおよび物理的な接続も終了します。

また、oracle.sqlj.runtime.Oracleクラスに用意されているstatic close()メソッドを使用すると、デフォルトの接続のみが終了します。次の例では、あらゆる接続コンテキスト・クラスのインスタンスをctxとして示した場合を想定しています。

...
finally
{
   ctx.close();
}
...

SQL例外が発生して、finally句がtryブロック内にない場合は、次のようになります。

...
finally
{
   try { ctx.close(); } catch(SQLException ex) {...}
}
...

デフォルトの接続を終了する方法としては、Oracleクラスに用意されているclose()メソッドを使用する方法もあります。

...
finally
{
   Oracle.close();
}
...

接続を終了する前に、必ず保留中の変更をコミットまたはロールバックしてください。接続を閉じる際に暗黙的なCOMMIT操作が行われるかどうかは、JDBC規格では指定されておらず、またベンダーによっても異なります。Oracleの場合、接続を終了する際に暗黙的なCOMMITが行われるようになっており、接続が終了されずにガベージ・コレクションが行われた場合は暗黙的なROLLBACKが行われるようになっていますが、これらのメカニズムに頼るのはお薦めできません。

注意:

基になる接続(共有接続の場合)を終了せずに、接続コンテキスト・インスタンスを終了することも可能です。

4.2.3 宣言済の接続コンテキスト・クラスを使用した複数接続

数種類のSQLエンティティを使用した接続の場合は、接続コンテキストの宣言によって、接続コンテキスト・クラスをさらに定義すると利便性が高まります。使用するSQLエンティティごとに個別の接続コンテキスト・クラスを作成すると、SQLJではコードのセマンティクス・チェックがより厳密に行われます。

4.2.4 Oracleクラスについて

Oracle SQLJ実装のoracle.sqlj.runtime.Oracleクラスでは、DefaultContextクラスのインスタンスを簡単に作成して使用できます。

static connect()メソッドは、デフォルトの接続コンテキスト・インスタンスを初期化します(DefaultContextオブジェクトのインスタンスを作成し、デフォルトの接続としてインストールします)。connect()から戻されたDefaultContextのインスタンスの代入や使用は、任意に行ってください。デフォルトの接続がすでに確立されていると、connect()からNULLが戻されます。

static getConnection()メソッドでは、単にDefaultContextオブジェクトのインスタンスが生成され、そのインスタンスが戻されます。必要に応じて、戻されたインスタンスを使用できます。

oracle.jdbc.OracleDriverクラスをCLASSPATHに指定すると、どちらのメソッドを使用してもOracle JDBCドライバ・マネージャが自動的に登録されるようになります。static close()メソッドは、デフォルトの接続を終了するメソッドです。

Oracle.connect()メソッドおよびOracle.getConnection()メソッドのシグネチャ

いずれのメソッドにも、入力として次のパラメータをとるシグネチャがあります。

  • URL (String)、ユーザー名(String)、パスワード(String)

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

  • URL(String)、java.util.Propertiesオブジェクト(接続のプロパティが格納されているオブジェクト)

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

  • ユーザー名やパスワードなど、接続を詳細に指定するURL(String)

    次に示すURL文字列の形式の例では、ユーザー名(HR)およびパスワード(hr)が指定されており、Oracle JDBCドライバとしてはJDBC Thinドライバが使用されています。

    "jdbc:oracle:thin:HR/hr@localhost:5221/myservice"
    
  • URL(String)、自動コミット・フラグ(boolean)

  • クラスのjava.lang.Classオブジェクト(そのクラスに基づいてプロパティ・ファイルがロードされる)、プロパティ・ファイル名(String)

  • java.lang.Classオブジェクト、プロパティ・ファイル名(String)、自動コミット・フラグ(boolean)

  • java.lang.Classオブジェクト、プロパティ・ファイル名(String)、ユーザー名(String)、パスワード(String)

  • java.lang.Classオブジェクト、プロパティ・ファイル名(String)、ユーザー名(String)、パスワード(String)、自動コミット・フラグ(boolean)

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

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

最後の2つのシグネチャは既存のデータベース接続を継承します。接続を継承すると、その接続に対する自動コミットの設定も継承されます。

自動コミット・フラグは、SQL操作を自動的にコミットするかしないかを指定するフラグです。Oracle.connect()メソッドおよびOracle.getConnection()メソッドの場合にのみ、デフォルトはfalseになっています。デフォルト値を使用すると、入力として自動コミットをとらないシグネチャを使用できます。ただし、コンストラクタを使用して、DefaultContextなどの接続コンテキスト・クラスのインスタンスを作成する場合は、自動コミットの設定を指定する必要があります。Oracle JDBC実装の自動コミット・フラグは、デフォルトとしてtrueをとります。

Oracle.close()メソッドのオプションのパラメータ

Oracle.close()を使用してデフォルト接続を閉じる際に、基になる物理的なデータベース接続を終了するかどうかを指定できます。デフォルトでは接続の終了が指定されます。この指定は、物理的な接続を複数の接続オブジェクト(SQLJ接続コンテキスト・インスタンスまたはJDBC接続インスタンス)で共有している場合に関係します。

基になる物理的な接続を、オープンした状態に保つには、次のようにします。

Oracle.close(ConnectionContext.KEEP_CONNECTION);

基になる物理的な接続を終了するには、次のようにします(デフォルトの動作)。

Oracle.close(ConnectionContext.CLOSE_CONNECTION);

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

4.2.5 DefaultContextクラスについて

sqlj.runtime.ref.DefaultContextクラスは、接続コンテキスト・クラスの完全なデフォルト実装を提供します。接続コンテキストを宣言して作成したクラスと同じように、DefaultContextクラスもsqlj.runtime.ConnectionContextインタフェースを実装します。DefaultContextクラスのクラス定義は、次のように宣言したときにSQLJトランスレータで生成される定義と同じです。

#sql public context DefaultContext;

DefaultContextメソッド

DefaultContextクラスの主なメソッドを次に示します。

  • getConnection()

    基になるJDBC接続オブジェクトを取得します。これは、動的SQL操作を使用する1つの方法として、アプリケーションにJDBCコードを使用する場合に役立ちます。基になるJDBC接続オブジェクトのsetAutoCommit()メソッドを使用すると、接続の自動コミット・フラグを設定できます。

  • setDefaultContext()

    アプリケーションで使用するデフォルトの接続を設定します。これは、入力としてDefaultContextインスタンスをとるstaticメソッドです。SQLJ実行文で接続コンテキストのインスタンスを指定しないと、このメソッドまたはOracle.connect()メソッドを使用して定義したデフォルトの接続が使用されます。

  • getDefaultContext()

    アプリケーションのデフォルト接続として現在定義されているDefaultContextインスタンスを戻します。これは、staticメソッドです。

  • close()

    接続コンテキスト・インスタンスをクローズします。

getConnection()メソッドおよびclose()メソッドは、sqlj.runtime.ConnectionContextインタフェースで指定します。

注意:

クライアント側では、setDefaultContext()をあらかじめコールしないと、getDefaultContext()からの戻り値がNULLになります。ただし、データ・ソース・オブジェクトがJNDIのjdbc/defaultDataSource下にバインドされている場合は、クライアントでデフォルトの接続としてこのデータ・ソース・オブジェクトが使用されます。

サーバーでは、getDefaultContext()によってデフォルトの接続(サーバー自体への接続)が戻されます。

DefaultContextコンストラクタ

通常は、Oracle.connect()またはOracle.getConnection()メソッドを使用してDefaultContextをインスタンス化します。ただし、DefaultContextの場合、5つのコンストラクタのうちのいずれかを使用すると、インスタンスを直接作成できます。これらのコンストラクタには、次の異なる入力パラメータ・セットがあります。

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

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

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

    次に示すURLの形式の例では、ユーザー名およびパスワードが指定されており、Oracle JDBCドライバとしてはJDBC Thinドライバが使用されています。

    "jdbc:oracle:thin:HR/hr@localhost:5221/myservice"
    
  • JDBC接続オブジェクト(Connection)

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

最後の2つのシグネチャは既存のデータベース接続を継承します。接続を継承すると、その接続に対する自動コミットの設定も継承されます。

DefaultContextインスタンスを作成する例を、次に示します。

DefaultContext defctx = new DefaultContext
   ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", false);

接続コンテキスト・コンストラクタに関する注意:

注意:

接続コンテキスト・コンストラクタを使用する際は、次の点に注意する必要があります。

  • 接続コンテキスト・クラスは、Oracle.connect()メソッドとは異なり、自動コミットの設定が必要です。

  • 前述のコンストラクタのうち、最初の3つを使用するには、あらかじめJDBCドライバを登録する必要があります。ドライバを自動的に登録するには、Oracle JDBCドライバを使用し、Oracle.connect()をコールします。「実行時に使用するドライバの選択および登録」を参照してください。

  • 通常宣言する接続コンテキスト・クラスのコンストラクタ・シグネチャは、DefaultContextクラスと同じです。ただし、接続コンテキスト・クラスをデータ・ソースに対応付けるように宣言する場合、別のコンストラクタが用意されています。詳細は、「標準データ・ソースのサポート」を参照してください。

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

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

DefaultContext close()メソッドのオプションのパラメータ

接続コンテキスト・インスタンスを終了するとき、基になる物理的な接続を終了するかどうかを指定できます。デフォルトでは接続の終了が指定されます。この指定は、複数の接続オブジェクト(SQLJ接続コンテキスト・インスタンスまたはJDBC接続インスタンス)で物理的な接続を共有している場合に有効です。DefaultContextインスタンスのdefctxを前提とした例を、いくつか次に示します。

基になる物理的な接続をオープンした状態に保つには、次の構文を使用します。

defctx.close(ConnectionContext.KEEP_CONNECTION);

基になる物理的な接続を終了する(デフォルトの動作)には、次の構文を使用します。

defctx.close(ConnectionContext.CLOSE_CONNECTION);

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

関連項目:

これらのパラメータの使用方法および共有接続の詳細は、「共有接続の終了」を参照してください

4.2.6 変換時の接続

変換時にオンラインのセマンティクス・チェックを使用する場合、SQLJで使用するデータベース接続を指定する必要があります。これらは、基本スキーマといいます。

変換時と実行時で別々の接続を使用できます。実際、別々の接続を使用する必要がある場合、またそれが推奨される場合が多くあります。現在開発を行っている環境と将来アプリケーションを実行する環境とが異なる場合は、別々の接続を使用する必要が生じる場合があります。変換時にランタイム接続を使用できる場合でも、オンライン・チェックの精度を高めるためにはリソースを絞り込んでアカウントを作成する方が望ましいといえます。実際にこの精度を高めるには、ランタイム接続時に使用可能なSQLエンティティうち、アプリケーションで使用するSQLエンティティのサブセットを少量にします。アプリケーションで実際に使用するSQLエンティティのみに絞り込んだ基本スキーマを作成すると、オンライン・チェックの精度と信頼度が高まります。

変換時の接続を指定するには、コマンドラインまたはプロパティ・ファイルでSQLJトランスレータの接続オプションを使用します。

4.2.7 カスタマイズ時の接続

一般に、Oracleのカスタマイズではデータベース接続は不要です。ただし、Oracle SQLJ実装ではカスタマイザ接続がサポートされています。これは、2つの環境で利用できます。

  • 使用するOracleカスタマイザのoptcolsオプションを使用可能にすると、接続が必須になります。このオプションを使用すると、イテレータ列型およびサイズは、パフォーマンスが最適化されるように定義されます。

  • SQLCheckerCustomizerと呼ばれる、プロファイルに対してセマンティクス・チェックを実行する特別なカスタマイザを使用した場合、オンライン・チェッカ(デフォルトでは使用)を使用するためには接続が必要になります。

Oracle固有コード生成の場合は、SQLJトランスレータに、同じ機能を持つ-optcolsオプションがあります。SQLCheckerCustomizerは、Oracleカスタマイザ・ハーネスのverifyオプションを指定すると起動されるようになります。どのカスタマイザを使用する場合にも、必要に応じて、カスタマイザ・ハーネスのuserpasswordurlおよびdriverオプションを使用して、接続パラメータを指定します。

4.3 NULLの処理

Javaの基本型(intdoublefloatなど)は、null値をとりません。結果式およびホスト式の型を選択するときは、このことに注意してください。

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

4.3.1 NULL処理用のラッパー・クラス

JDBCではNULL0または特定のデータ型に対してはfalseとして取り出されますが、SQLJではSQLのNULLは、常にJavaのnullとして取り出されます。そのため、SQLでNULLを受け取る可能性がある場合に、SQLJで出力変数にJavaの基本型を使用しないでください(Javaの基本型は、null値をとらないためです)。

このことは、結果式、出力ホスト式、入出力ホスト式およびイテレータ列の型について当てはまるので、注意が必要です。Javaの基本型を受取り用にしてSQLのNULLを取り出そうとすると、sqlj.runtime.SQLNullExceptionが発生し、代入が行われなくなります。

Javaの基本型にNULL値が代入されるのを避けるには、基本型のかわりに次のラッパー・クラスを使用します。

  • java.lang.Boolean

  • java.lang.Byte

  • java.lang.Short

  • java.lang.Integer

  • java.lang.Long

  • java.lang.Double

  • java.lang.Float

基本型の値に再変換する必要が生じた場合は、各ラッパー・クラスにxxxValue()メソッドがあります。たとえば、intValue()を使用すると、int値がIntegerオブジェクトから戻され、floatValue()を使用すると、float値がFloatオブジェクトから戻されます。この例では、intobjIntegerオブジェクトとします。

int j = intobj.intValue();

注意:

  • SQLNullExceptionは、標準java.sql.SQLExceptionクラスのサブクラスです。

  • JavaオブジェクトはNULL値を持つことができます。SQLJでは、他のホスト言語(C、C++、COBOLなど)と異なり、インジケータ変数は不要です。

4.3.2 NULL処理の例

次に、java.langラッパー・クラスを使用してNULLを処理する例を示します。

NULL入力ホスト変数の例

次の例では、Floatオブジェクトを使用して、NULL値をデータベースに渡します。

int empno = 7499;
Float commission = null;

#sql { UPDATE employees SET commission_pct = :commission WHERE employee_id = :empno };

この場合は、Javaの基本型floatを使用できません。

NULLイテレータ行の例

次の例では、NULLデータに対応できるように、イテレータにDouble列型を使用します。

employee表の従業員のうち、給与が$50,000以上の従業員の名前(FIRST_NAME)と歩合(COMMISSION_PCT)をイテレータに取り出します。次に各行をテストして、COMMISSION_PCTフィールドが実際にNULLになっているかどうかを調べます。NULLの場合は、NULLの処理が行われます。

#sql iterator EmployeeIter (String first_name, Double commission);

EmployeeIter ei;
#sql ei = { SELECT first_name, commission_pct FROM employees WHERE salary >= 50000 };

while (ei.next())
{
   if (ei.commission_pct() == null) 
      System.out.println(ei.first_name() + " is not on commission.");
}
ei.close();
...

注意:

WHERE句のNULL値との比較を実行するには、次のSQL構文を使用します。

...WHERE :x IS NULL

4.4 例外処理の基本

ここでは、SQLJアプリケーションにおける例外処理の基本として、エラー・チェック要件などについて説明します。この項の内容は次のとおりです。

4.4.1 SQLJおよびJDBCの例外処理要件

SQLJ実行文は、sqlj.runtimeからのJDBCコールになり、JDBCではSQL例外が捕捉またはスローされる必要があるため、SQLJでも、SQLJ実行文を含むブロックで、SQL例外が捕捉またはスローされる必要があります。適切な例外処理が含まれていないと、ソース・コードのコンパイル中にエラーが生成されます。

SQL例外の処理には、SQLExceptionクラスが必要です。これは標準JDBCのjava.sql.*パッケージに含まれています。

例外処理の例

SQLJアプリケーションに必要な基本的な例外処理の例です。このコードでは、try/catchブロックを含むmainメソッド、および例外発生時にSQLExceptionをスローする別のメソッドを宣言します。このコードを次に示します。

    /* Import SQLExceptions class.  The SQLException comes from
       JDBC. Executable #sql clauses result in calls to JDBC, so methods
       containing executable #sql clauses must either catch or throw
       SQLException.
    */
import java.sql.* ;
import oracle.sqlj.runtime.Oracle;

    // iterator for the select

#sql iterator MyIter (String ITEM_NAME);

public class TestInstallSQLJ 
{
    //Main method
  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(TestInstallSQLJ.class, "connect.properties");

      TestInstallSQLJ ti = new TestInstallSQLJ();

    // This method throws SQLException. Therefore, it ic called within a try block
      ti.runExample();

    } catch (SQLException e) { 
      System.err.println("Error running the example: " + e);
    }

  } //End of method main

  //Method that runs the example
  void runExample() throws SQLException
  {
      //Issue SQL command to clear the SALES table
    #sql { DELETE FROM SALES };
    #sql { INSERT INTO SALES(ITEM_NAME) VALUES ('Hello, SQLJ!')};

    MyIter iter;
    #sql iter = { SELECT ITEM_NAME FROM SALES };

    while (iter.next()) {
      System.out.println(iter.ITEM_NAME());
    }
  }
}

4.4.2 例外の処理

ここでは、SQLJアプリケーションにおける例外の処理および解析方法について説明します。実行時の例外の発生元は次のいずれかです。

  • SQLJランタイム

  • JDBCドライバ

  • RDBMS

エラー・テキストの出力

前項の例では、SQL例外の捕捉方法およびエラー・メッセージの出力方法を示しました。このコードの一部を次に示します。

...
try {
...
} catch (SQLException e) { 
      System.err.println("Error running the example: " + e); 
}
...

この結果、SQLExceptionオブジェクトからのエラー・テキストが出力されます。

エラー情報を取り出すには、SQLExceptionクラスのgetMessage()getErrorCode()およびgetSQLState()メソッドを使用します。

この例に示したようにエラー・テキストを出力すると、エラー・メッセージの他にSQLExceptionなどのテキストも出力されます。

SQLの状態およびエラー・コードの取出し

java.sql.SQLExceptionクラスとサブクラスには、メソッドgetMessage()getErrorCode()およびgetSQLState()があります。次のメソッドを使用すると、例外またはエラーの発生元およびこれらの実装方法に応じて、次のような補足情報が得られます。

  • String getMessage()

    SQLJランタイムまたはJDBCドライバで発生したエラーに対してこのメソッドを使用すると、接頭辞なしのエラー・メッセージが戻されます。RDBMSで発生したエラーに対してこのメソッドを使用すると、接頭辞としてORA番号の付いたエラー・メッセージが戻されます。

  • int getErrorCode()

    SQLJランタイムで発生したエラーに対しては、このメソッドを使用しても役立つ情報は戻されません。JDBCドライバまたはRDBMSで発生したエラーに対してこのメソッドを使用すると、ORA番号が5桁の整数として戻されます。

  • String getSQLState()

    SQLJランタイムで発生したエラーに対してこのメソッドを使用すると、SQLの状態を示す5桁のコードと文字列が戻されます。JDBCドライバで発生したエラーに対しては、このメソッドを使用しても役立つ情報は戻されません。RDBMSで発生したエラーに対してこのメソッドを使用すると、SQLの状態を示す5桁のコードが戻されます。アプリケーションには、NULLの戻り値を扱えるように適切なコードを含める必要があります。

次に、エラー・メッセージを出力し、さらにSQLの状態をチェックする例を示します。

...
try {
...
} catch (SQLException e) { 
      System.err.println("Error running the example: " + e); 
      String sqlState = e.getSQLState();
      System.err.println("SQL state = " + sqlState); 
}
...

4.4.3 SQLExceptionサブクラスの使用方法

さらに細かいエラー・チェックを実行するには、java.sql.SQLExceptionクラスのサブクラスを使用します。

SQLJでは、java.sql.SQLExceptionのサブクラスであるsqlj.runtime.NullExceptionクラスが提供されます。この例外は、Javaの基本型変数にNULLが戻される場合に使用できます。

バッチ処理が有効な環境では、標準java.sql.BatchUpdateExceptionサブクラスを使用できます。詳細は、「バッチ実行中のエラー状態」を参照してください。

SQLExceptionサブクラスを使用する際は、サブクラスの例外を捕捉してから、SQLExceptionを捕捉してください。次に例を示します。

...
try {
...
} catch (SQLNullException ne) {
     System.err.println("Null value encountered: " + ne); }
  catch (SQLException e) { 
     System.err.println("Error running the example: " + e); }
...

このようにするのは、サブクラスの例外もSQLExceptionとして捕捉できるからです。SQLExceptionを先に捕捉すると、サブクラスの例外に対して特別な処理が行われるコード部分まで進みません。

4.5 基本的なトランザクション制御

ここでは、データ更新を管理する方法について説明します。内容は次のとおりです。

4.5.1 トランザクションの概要

トランザクションは、Oracleで1つの単位として順次処理されるSQL操作です。次の処理を行った後のSQL実行文でトランザクションが開始されます。

  • データベースへの接続

  • COMMIT(データの更新を自動的にまたは手動でデータベースにコミットします)

  • ROLLBACK(変更内容を取り消します)

トランザクションは、COMMIT操作またはROLLBACK操作で終了します。

注意:

Oracle Database 12c リリース2 (12.2)では、CREATEALTERなどのすべてのデータ定義言語(DDL)文に暗黙的なCOMMITが含まれます。これによって、DDL文のみでなく、コミットまたはロールバックされていない、実行済のすべてのデータ操作言語(DML)文(INSERTDELETEUPDATEなど)もコミットされます。

4.5.2 自動コミットと手動コミットとの違い

SQLJまたはJDBCを使用すると、自動でも手動でも、データの更新をコミットできます。どちらの場合も、新規のトランザクションはCOMMIT操作で開始されます。変更内容の自動コミットを指定するには、自動コミット・フラグを使用可能にします。これを行うには、SQLJ接続を定義するときに自動コミット・フラグを使用可能にするか、既存の接続の基になるJDBC接続オブジェクトのsetAutoCommit()メソッドを使用します。手動で制御するには、自動コミット・フラグを使用禁止にし、SQLJのCOMMIT文およびROLLBACK文を使用します。

自動コミットを使用可能にすると、手間が省けますが、細かい制御ができません。たとえば、変更内容のロールバックができません。また、自動コミット・モードでは、SQLJやJDBCの一部の機能を使用できません。そのため、バッチ更新やSELECT FOR UPDATE構文を使用する場合などは、自動コミット・フラグを使用禁止にする必要があります。

4.5.3 接続を定義する際の自動コミットの指定

Oracle.connect()またはOracle.getConnection()メソッドでDefaultContextのインスタンスを作成し、接続を定義すると、自動コミット・フラグがデフォルトでfalseに設定されます。ただし、これらのメソッドのシグネチャでは、このフラグを明示的に設定できます。自動コミット・フラグは、必ず最後のパラメータで指定します。

次に、DefaultContextをインスタンス化し、自動コミット・モードのデフォルト値falseを使用する例を示します。

Oracle.getConnection
   ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr");

trueに設定するには、次のように指定します。

Oracle.getConnection
   ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", true);

コンストラクタで接続コンテキストのインスタンスを作成する場合は、DefaultContextクラスの場合でも、宣言済の接続コンテキスト・クラスの場合でも、自動コミットの設定を指定する必要があります。この設定のフラグも、次のように最後のパラメータで指定します。

DefaultContext ctx = new DefaultContext
   ("jdbc:oracle:thin:@localhost:5221/myservice", "HR", "hr", false);

JDBCのConnectionインスタンスを直接作成する理由がある場合、自動コミット・フラグの設定は、プログラムがクライアント側で実行されるときはデフォルトでtrueになり、サーバー側で実行されるときはデフォルトでfalseになります。JDBCのConnectionインスタンスを直接作成するときに自動コミットの設定値は指定できませんが、その設定をsetAutoCommit()メソッドで変更することはできます。

注意:

自動コミット機能は、サーバー側のJDBC内部ドライバではサポートされていません

4.5.4 既存の接続の自動コミットに対する変更

既存の接続の場合は、自動コミット・フラグの設定変更は通常不要ですが、必要な場合は変更できます。変更するには、基になるJDBC接続オブジェクトのsetAutoCommit()メソッドを使用します。

基になるJDBC接続オブジェクトを取得するには、SQLJの接続コンテキスト・インスタンスのgetConnection()メソッドを使用します。つまり、DefaultContextクラスのインスタンスのメソッドを使用するか、または宣言済の接続コンテキスト・クラスのインスタンスのメソッドを使用します。

この2つの操作を一度に行うには、次のように指定します。

ctx.getConnection().setAutoCommit(false);

または

ctx.getConnection().setAutoCommit(true);

この例では、ctxをSQLJの接続コンテキストのインスタンスとしています。

注意:

トランザクションの処理中は、自動コミットの設定を変更しないでください。

4.5.5 手動COMMITまたはROLLBACKの使用方法

自動コミット・フラグを使用禁止にした場合は、データの更新を手動でコミットする必要があります。最終のCOMMIT操作以降に加えられた変更内容をコミットするには、次のようにSQLJのCOMMIT文を使用します。

#sql { COMMIT };

最終のCOMMIT操作以降に加えられた変更内容のロールバックを行うには、次のようにSQLJのROLLBACK文を使用します。

#sql { ROLLBACK };

注意:

  • 自動コミットを使用可能にしたときは、COMMITコマンドやROLLBACKコマンドを使用しないでください。使用した場合は、意図しない動作またはSQL例外が発生します。

  • 指定したセーブポイントをロールバックすることもできます。「セーブポイントの使用」を参照してください。

  • Oracle SQL構文のすべてのDDL文には、暗黙的なCOMMIT操作が組み込まれています。SQLJには、この機能はありません。そのような文は標準のOracle SQL規則に従います。

  • 自動コミット・モードがオフの場合に、クライアント・アプリケーションから接続コンテキスト・インスタンスをクローズすると、最後のCOMMIT操作後に行った変更がコミットされます(ただし、接続コンテキスト・インスタンスをKEEP_CONNECTIONでクローズした場合を除きます)。詳細は、「共有接続の終了」を参照してください。

4.5.6 イテレータおよび結果セットに対するコミットおよびロールバックの影響

COMMITおよびROLLBACK操作は、オープンしている結果セットとイテレータには反映されません。結果セットおよびイテレータはオープンしたままになります。通常それぞれの内容はSELECT文を実行したときのデータベースの状態が引き続き反映されています。

注意:

イテレータ・クラスをsensitivity=SENSITIVEと宣言している場合は例外です。この場合、イテレータをウィンドウ・サイズの外部でスクロールすると、基になる結果セットへの変更が表示されます。スクロール可能なイテレータの詳細は、「スクロール可能なイテレータ」を参照してください。基になるスクロール可能な結果セットの詳細は、『Oracle Database JDBC開発者ガイド』を参照してください。

UPDATEINSERTおよびDELETE文をSELECT文の後に実行した場合も同じです。これらの文を実行しても、オープンしている結果セットとイテレータの内容は変わりません。

SELECTUPDATEおよびCOMMITの各操作をこの順に実行した場合を考えてください。UPDATEおよびCOMMITを実行しても、sensitiveではない結果セットやイテレータの内容は、SELECT文で設定されたときの状態のまま変更されません。

さらに、UPDATESELECTおよびROLLBACKの操作をこの順に実行したとします。SELECTで移入されたsensitiveでない結果セットやイテレータには、依然として更新データが格納されており、その後に実行されたROLLBACKの影響は受けません。

4.5.7 セーブポイントの使用

JDBC 3.0の仕様では、セーブポイントのサポートが追加されました。セーブポイントとは、トランザクション全体をロールバックするのではなく、必要に応じてトランザクション内でロールバックできるように定義された位置です。セーブポイントとは、SAVEPOINT文が出現するトランザクション内の位置です。

Oracle9i データベース リリース2(9.2)のSQLJで、セーブポイントをサポートするためのOracle固有の構文が導入されました。Oracle Database 12c リリース2 (12.2)のSQLJでは、ISO SQLJ規格のセーブポイント構文のサポートが追加されています。

ISO SQLJ規格のセーブポイント構文のサポート

ISO SQLJ規格の構文では、SAVEPOINT文で文字列リテラルを使用して、セーブポイントの名前を指定します。これを行うには、次の手順を実行します。

#sql { SAVEPOINT savepoint1 };

そのセーブポイントまで変更をロールバックする場合、次のように、後でROLLBACK TO文にその指定した名前を指定できます。

#sql { ROLLBACK TO savepoint1 };

セーブポイントが不要になった場合、RELEASE SAVEPOINT文を使用します。

#sql { RELEASE SAVEPOINT savepoint1 };

セーブポイントは、SQLJ実行コンテキストに保存されます。SQLJ実行コンテキストには、これらの3つの文の機能に相当するメソッドがあります。

COMMIT操作によってトランザクションが終了するため、トランザクション内のセーブポイントもすべて解放されます。

Oracle SQLJのセーブポイント構文

ISO SQLJ規格の構文に加えて、次に示すOracle固有のセーブポイントの構文がサポートされています。Oracle構文では、文字列リテラルではなく、文字列のホスト式が使用されていることに注意してください。

セーブポイントを設定するには、次の構文を使用します。

#sql { SET SAVEPOINT :savepoint };

この例のホスト式、savepointは、セーブポイントの名前をJavaのStringで指定する変数です。

セーブポイントまでロールバックするには、次の構文を使用します。

#sql { ROLLBACK TO :savepoint };

セーブポイントを解放するには、次のSQLJ文を使用します。

#sql { RELEASE :savepoint };

注意:

Oracle固有の構文は、下位互換性のために引き続きサポートされています。Oracle構文とISO SQLJ規格の構文の次の相違点に注意してください。

  • Oracle構文は、文字列リテラルではなく、文字列変数をとります。

  • Oracle構文では、SAVEPOINTのかわりにSET SAVEPOINTを使用します。

  • Oracle構文では、RELEASE SAVEPOINTのかわりにRELEASEを使用します。

4.6 概要: 簡単なSQLJコード

これまでに説明したSQLJ実行文の特長と機能について要点を把握するには、簡潔で包括的なプログラムを試してみるのが一番効果的な方法です。ここでは、2つの例を示します。

最初の例では、操作を1つずつ示してから、全体を示します。この例では、SELECT INTO文を使用して従業員表の2つの列に対して単一行の問合せを実行します。この例を実行する場合は、connect.propertiesファイルのパラメータを、適切なデータベースに接続できる設定に変更する必要があります。

2番目の例は多少複雑です。SQLJのイテレータを使用して複数行問合せを実行します。

必要なクラスのインポート

必要なJDBCまたはSQLJパッケージをすべてインポートします。少なくとも、java.sqlパッケージ内のクラスがいくつか必要です。

import java.sql.*;

java.sqlパッケージ内のクラスを、必ずしもすべて使用するわけではありません。主に使用するクラスとしては、java.sql.SQLExceptionと、明示的に参照するクラスがあります。たとえば、java.sql.Datejava.sql.ResultSetです。

Oracleクラスには、次のパッケージが必要です。通常、DefaultContextオブジェクトをインスタンス化し、デフォルト接続を確立するときに、このクラスを使用します。

import oracle.sqlj.runtime.*;

SQLJのランタイム・クラスをコード中に直接使用する場合は、次のパッケージをインポートします。

import sqlj.runtime.*;
import sqlj.runtime.ref.*;

ただし、コード中にSQLJのランタイム・クラスを直接使用しない場合でも、ランタイム・クラスをCLASSPATHに指定するのみで十分です。

重要なランタイム・クラスとしては、sqlj.runtimeパッケージのResultSetIteratorおよびExecutionContextと、sqlj.runtime.refパッケージのDefaultContextなどがあります。

JDBCドライバの登録およびデフォルト接続の設定

デフォルトの接続を設定するには、static Oracle.connect()メソッドを使用するコンストラクタで、SimpleExampleクラスを宣言します。この結果、Oracle JDBCドライバの登録も行われます。

ここでは、URL、ユーザー名およびパスワードをconnect.propertiesファイルから取得するconnect()のシグネチャを使用します。このファイルの例は、ORACLE_HOME/sqlj/demoディレクトリおよび「ランタイム接続の設定」にあります。

public class SimpleExample {

  public SimpleExample() throws SQLException {
    // Set default connection (as defined in connect.properties).
    Oracle.connect(getClass(), "connect.properties");
  }

例外処理の設定

main()を作成します。このメソッドからSimpleExampleコンストラクタをコールし、try/catchブロックをセットアップします。このブロックでは、runExample()メソッドにより発生したSQL例外を処理します。このアプリケーションの実際の処理は、このメソッドが行います。

...
public static void main (String [] args) {
    
   try {
      SimpleExample o1 = new SimpleExample();
      o1.runExample();
   }
   catch (SQLException ex) {
      System.err.println("Error running the example: " + ex);
   }
}
...

接続を終了するには、try/catchブロックをfinally句の内部に使用します。SQL例外の場合にfinally句がtry/catchブロック内部に使用されていないことが前提です。

finally
{
   try { Oracle.close(); } catch(SQLException ex) {...}
}

ホスト変数のセットアップ、SQLJ句の実行、結果の処理

次の処理を行うrunExample()メソッドを作成します。

  1. main()メソッドに対してSQL例外をスローし、処理させます。

  2. Javaのホスト変数を宣言します。

  3. SQLJ句を実行します。Javaのホスト変数が埋込みSELECT文にバインドされ、データがホスト変数に取り込まれます。

  4. 結果を出力します。

このメソッドのコードは次のようになります。

void runExample() throws SQLException {
     
     System.out.println( "Running the example--" );
     
     // Declare two Java host variables--
     Float salary;
     String empname;

     // Use SELECT INTO statement to execute query and retrieve values.
      #sql { SELECT first_name, salary INTO :empname, :salary FROM employees
             WHERE employee_id = 7499 };
     
     // Print the results--
     System.out.println("Name is " + empname + ", and Salary is " + salary);
  }
}    // Closing brace of SimpleExample class

前述のコード例では、Javaのホスト変数としてsalaryempnameが宣言されています。その次のSQLJ句の処理では、employees表のfirst_name列とsalary列から選択されたデータが、ホスト変数に格納されます。最後に、salaryempnameの値が出力されます。

このSELECT文ではemployees表から選択できる行は1行のみで、WHERE句のemployee_id列が表の主キーになっているためです。

SELECT INTOを使用した単一行問合せの例

ここでは、前に1つずつ順を追って説明した内容をSimpleExampleクラスを使用して総復習します。次の例は単一行問合せの例であるため、イテレータは使用しません。

// Import SQLJ classes:
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
import oracle.sqlj.runtime.*;

// Import standard java.sql package:
import java.sql.*;

public class SimpleExample {

  public SimpleExample() throws SQLException {
    // Set default connection (as defined in connect.properties).
    Oracle.connect(getClass(), "connect.properties");
  }

  public static void main (String [] args) throws SQLException {
    
    try {
      SimpleExample o1 = new SimpleExample();
      o1.runExample();
    }
    catch (SQLException ex) {
      System.err.println("Error running the example: " + ex);
    }
  }

  finally
  {
     try { Oracle.close(); } catch(SQLException ex) {...}
  }

  void runExample() throws SQLException {
     
     System.out.println( "Running the example--" );
     
     // Declare two Java host variables--
     Float salary;
     String empname;

     // Use SELECT INTO statement to execute query and retrieve values.
        #sql { SELECT first_name, salary INTO :empname, :salary FROM employees
              WHERE employee_id = 7499 };
     
     // Print the results--
     System.out.println("Name is " + empname + ", and Salary is " + salary);
  }
}

名前付きイテレータの設定

次の例は、前の例がベースになっていますが、それに加えて名前付きイテレータを使用し、複数行問合せを実行します。

最初に、イテレータ・クラスを宣言します。NULL値が戻される可能性がある場合は、基本型intfloatではなくて、オブジェクト型IntegerFloatを使用してください。

#sql iterator EmpRecs(
      int empno,       // This column cannot be null, so int is OK.
                       // (If null is possible, use Integer.)
      String ename,
      String job,
      Integer mgr,
      Date hiredate,
      Float sal,
      Float comm,
      int deptno);

次にEmpRecsクラスをインスタンス化し、問合せ結果を移入します。

EmpRecs employees;

#sql employees = { SELECT employee_id, first_name, job_id, manager_id, hire_date,
                   salary, commission_pct, department_tno FROM employees };

次に、イテレータのnext()メソッドで結果を出力します。

    while (employees.next())  {
      System.out.println( "Name:       " + employees.first_name() );
      System.out.println( "EMPNO:      " + employees.employee_id() );
      System.out.println( "Job:        " + employees.job_id() );
      System.out.println( "Manager:    " + employees.manager_id) );
      System.out.println( "Date hired: " + employees.hire_date() );
      System.out.println( "Salary:     " + employees.salary() );
      System.out.println( "Commission: " + employees.commission_pct() );
      System.out.println( "Department: " + employees.department_no() );
      System.out.println();
    }

最後に、イテレータを終了します。

employees.close();

名前付きイテレータを使用した複数行問合せの例

この例では、名前付きイテレータを使用して複数行問合せを行い、従業員表から複数のデータ列を選択します。

この例は、名前付きイテレータを使用していることを除けば、前に示した単一行問合せの例と概念はほとんど同じです。

// Import SQLJ classes:
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
import oracle.sqlj.runtime.*;

// Import standard java.sql package:
import java.sql.*;

// Declare a SQLJ iterator.
// Use object types (Integer, Float) for mgr, sal, And comm rather
// than primitive types to allow for possible null selection.

#sql iterator EmpRecs(
      int empno,       // This column cannot be null, so int is OK.
                       // (If null is possible, Integer is required.)
      String ename,
      String job,
      Integer mgr,
      Date hiredate,
      Float sal,
      Float comm,
      int deptno);

// This is the application class.  
public class EmpDemo1App {

   public EmpDemo1App() throws SQLException {
      // Set default connection (as defined in connect.properties).
      Oracle.connect(getClass(), "connect.properties");
   }

  public static void main(String[] args) {

    try {
      EmpDemo1App app = new EmpDemo1App();
      app.runExample();
    }
    catch( SQLException exception ) {
      System.err.println( "Error running the example: " + exception );
    }
  }

  finally
  {
     try { Oracle.close(); } catch(SQLException ex) {...}
  }

  void runExample() throws SQLException  {
    System.out.println("\nRunning the example.\n" );

    // The query creates a new instance of the iterator and stores it in
    // the variable 'employees' of type 'EmpRecs'.  SQLJ translator has
    // automatically declared the iterator so that it has methods for
    // accessing the rows and columns of the result set.

    EmpRecs employees;

    #sql employees = { SELECT employee_id, first_name, job_id, manager_id, hire_date,
                       salary, commission_pct, department_no FROM employees };

    // Print the result using the iterator.

    // Note how the next row is accessed using method 'next()', and how
    // the columns can be accessed with methods that are named after the
    // actual database column names.

    while (employees.next())  {
      System.out.println( "Name:       " + employees.first_name() );
      System.out.println( "EMPNO:      " + employees.employee_id() );
      System.out.println( "Job:        " + employees.job_id() );
      System.out.println( "Manager:    " + employees.manager_id() );
      System.out.println( "Date hired: " + employees.hire_date() );
      System.out.println( "Salary:     " + employees.salary() );
      System.out.println( "Commission: " + employees.commission_pct() );
      System.out.println( "Department: " + employees.department_no() );
      System.out.println();
    }

    // You must close the iterator when it's no longer needed.
    employees.close() ;
  }
}

4.7 Oracle固有コード生成(プロファイルなし)

このマニュアルでは、全体にわたって一般的かつ標準的なSQLJランタイム・レイヤーおよびSQLJプロファイルについて説明しています。ただし、Oracle SQLJ実装では、SQLJランタイムをコールするISO SQLJ規格コードではなく、Oracle JDBCドライバを直接コールするOracle固有コードがデフォルトで生成されます。Oracle固有コード生成の場合、プロファイル・ファイルはなく、SQLJランタイム・レイヤーの役割はプログラム実行時に大幅に縮小されます。Oracle固有コードでは、すべてのOracle固有の拡張機能がサポートされています。

コード生成は、SQLJトランスレータの-codegenオプションによって決まります。Oracle固有コード生成のデフォルト設定は、-codegen=oracleです。また、ISO SQLJ規格に従ったコード生成を行うために、-codegen=isoを設定することもできます。

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

4.7.1 Oracle固有コード生成用環境の要件

Oracle固有コード生成を使用する場合、次の環境の要件に留意してください。

  • Oracle固有コード生成にはJDBC文のキャッシング機能が必要であるため、Oracle11g以降のバージョンのJDBCドライバを使用する必要があります。

  • 汎用SQLJランタイム・ライブラリruntimeは、Oracle固有コード生成ではサポートされていません。次のいずれかのOracle SQLJランタイム・ライブラリをCLASSPATHに指定する必要があります。

    • runtime12.jar

    • runtime12ee.jar

4.7.2 Oracle固有コード生成でのコード上の考慮事項と制限事項

Oracle固有コード生成が使用されるSQLJアプリケーションのコーディング時には、次のプログラミング上の考慮事項および制限に注意してください。

  • デフォルト以外の文キャッシュ・サイズを使用する場合は、Oracleカスタマイザのstmtcacheオプションが使用できないため、コードに適切なメソッド・コールを記述する必要があります。

  • 同じアプリケーションに、Oracle固有生成コードとISO SQLJ規格生成コードを混在しないでください。ただし、Oracle固有コードおよびISO SQLJ規格コードが同じ接続を共有する必要がある場合は、次のいずれかを実行します。

    • Oracle固有コードおよびISO標準コードが、必ず異なるSQLJ実行コンテキスト・インスタンスを使用するようにします。SQLJ実行コンテキストの詳細は、「実行コンテキスト」を参照してください。

    • トランザクション境界(手動のCOMMIT文またはROLLBACK文)を2種類のコードの間に配置します。

    コードの混在に関するこの制限事項は、あるセッションで実行中のすべてのJavaコードが同じJDBC接続とSQLJ接続コンテキストを使用するので、サーバー側のコードで特に重要になります。

  • データベースから値が戻されるとき、パラメータ表現での副作用に頼らないでください。Oracle固有コード生成では、OUTパラメータ、IN OUTパラメータ、SELECT INTO変数、またはSQL文の戻り引数を評価するための一時変数が作成されません。

    たとえば、次のような文は使用しないでください。

    #sql { SELECT * FROM EMPLOYEES INTO :(x[i++]), :(f_with_sideffect()[i++]),
                                  :(a.b[i]) };
    

    または

    #sql x[i++] = { VALUES f(:INOUT (x[i++]), :OUT (f_with_sideffect())) };
    

    引数の評価は、生成コードの適切な場所で実行されます。これによって、ISO SQLJ規格に従って評価が実施される場合と、動作が異なることになります。

  • Oracleオブジェクト機能の型マップは、対応するJavaクラスでjava.sql.SQLDataインタフェースが実装されることを前提としています。Oracleオブジェクト機能の型マップを使用する場合、イテレータ宣言と接続コンテキスト宣言で同じ型マップを指定する必要があります。これは、with句で指定します。

    たとえば、接続コンテキスト・クラスを宣言する場合、次のようにします。

    #sql context TypeMapContext with (typeMap="MyTypeMap");
    

    また、この接続コンテキスト・クラスのインスタンスを使用するSQLJ文からイテレータ・インスタンスを移入するには、次のようにします。

    TypeMapContext tmc = new TypeMapContext(...);
    ...
    MyIterator it;
    #sql [tmc] it = ( SELECT pers, addr FROM tab WHERE ...);
    

    イテレータ宣言では同じ型マップを指定する必要があります。次のようにします。

    #sql iterator MyIterator with (typeMap="MyTypeMap") 
                  (Person pers, Address addr);
    

    注意:

    この制限事項の理由は、Oracle固有コード生成の場合、イテレータを取得するすべてのメソッドが変換時に完全にOracle JDBCコールとして生成されているためです。適切なコールを生成するには、イテレータがある特定の型マップで使用されるかどうかをSQLJトランスレータで把握する必要があります。

4.7.3 Oracle固有コード生成時のSQLJ使用方法の変更

以前にOracleカスタマイザのオプションとしてのみ使用可能なオプションで、Oracle固有コード生成でも便利なオプションがあります。プロファイルのカスタマイズはOracle固有コード生成には適用できないため、これらのオプションは他の方法で使用できるようになりました。

文キャッシュのサイズを変更したり、Oracle固有コードの生成時に文のキャッシングを無効化するには、カスタマイザのstmtcacheオプションではなく、メソッド・コールをコードに使用します。sqlj.runtime.ref.DefaultContextクラスと同様に、宣言するすべての接続コンテキスト・クラスには、現在次のstaticメソッドがあります。

  • setDefaultStmtCacheSize(int)

  • int getDefaultStmtCacheSize()

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

  • setStmtCacheSize(int)

  • int getStmtCacheSize()

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

さらに、次のオプションは、Oracleカスタマイザのオプションと同様に、フロントエンドのOracle SQLJトランスレータとして使用できます。

  • -optcols: パフォーマンスが最適化されるようにイテレータ列型およびサイズ定義を有効化するためのオプション。

  • -optparams: JDBCリソース割当てが最適化されるようにパラメータ・サイズ定義を有効化するためのオプション。このオプションは、optparamdefaultsと併用されます。

  • -optparamdefaults: 特定のデータ型に対してパラメータ・サイズのデフォルトを設定するためのオプション。このオプションは、optparamsと併用されます。

  • -fixedchar: WHERE句に関して、空白埋めを考慮したCHAR比較を有効にするためのオプション。

以下の点に注意します。

  • オンラインのセマンティクス・チェックを使用している場合のみ(SQLJトランスレータの-user-passwordおよび-urlオプションを使用して、変換時にデータベース接続を要求している場合)、-optcolsオプションを使用します。

  • -optcols-optparamsおよび-optparamdefaultsオプションの機能とデフォルト値は、カスタマイザのオプションと同じです。

4.7.4 Oracle固有コード生成のメリットとデメリット

Oracle固有コード生成を使用すると、ISO標準コード生成と比較して次のメリットがあります。

  • アプリケーションの実行がより効率的になります。コードではJDBC Application Programming Interface (API)を直接コールするため、ランタイムのパフォーマンスがただちにJDBCレベルになります。中間的なSQLJランタイム・レイヤーの役割は、プログラム実行時に大幅に縮小されます。

  • アプリケーションのサイズが小さくなります。

  • プロファイル・ファイル(.ser)は生成されません。変換されたアプリケーションをデータベースにロードするか、別のシステムに移植すると、コンポーネントが少ないため、これは特に便利です。

  • プロファイルのカスタマイズ処理がないので、変換が高速になります。

  • 実行時に、Oracle SQLJランタイムとOracle JDBCドライバは同じ文キャッシュ・リソースを使用するため、Oracle SQLJランタイムとOracle JDBCドライバ間のリソースのパーティション化が不要です。

  • プロファイル・ファイルではなくJavaクラス・ファイルにSQL固有情報があると、潜在的なセキュリティの問題を回避できます。

  • 静的SQLコードの実行のために考慮されている拡張機能など、将来サポートされるOracle JDBCのパフォーマンス強化のメリットを活用するために、コードを再度記述する必要はありません。Oracle SQLJトランスレータの将来のリリースではこれが自動的に処理できるようになります。

  • ブラウザ環境に完全な移植性が備わるので、Javaリフレクションが実行時に使用されることが少なくなります。

ただし、いくつかのデメリットがあります。

4.8 ISO標準コード生成

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

4.8.1 ISO標準コード生成用環境の要件

Oracle SQLJ実装では、デフォルトで、SQLJランタイムをコールするISO SQLJ規格コードを生成するのではなく、Oracle JDBCドライバを直接コールするOracle固有コードを生成します。ISO標準コード生成の一般的な環境設定を次に示します。

  • SQLJコード生成: -codegen=iso

  • SQLJ変換ライブラリ: translator.jar

  • SQLJランタイム・ライブラリ: JDK 6またはJDK 7と、Oracle Database 12c リリース2 (12.2)を含むruntime12.jar

  • JDBCドライバ: Oracle Database 12c リリース2 (12.2)のojdbc6.jarまたはojdbc7.jar

  • JDKバージョン: JDK 6またはJDK 7

4.8.2 SQLJトランスレータとSQLJランタイム

ここでは、ISO標準コード生成の場合に見られるOracle SQLJ実装の特徴について説明します。

  • SQLJトランスレータ: .javaファイルに加え、トランスレータによってISO標準コード生成用に1つ以上のSQLJプロファイルも生成されます。これらのプロファイルには、埋込みSQL操作についての情報が含まれます。SQLJでは、その後Javaコンパイラが自動的に起動され、.javaファイルから.classファイルが生成されます。

  • SQLJランタイム: ISO標準コード生成の場合、SQLJランタイムによって、JDBCドライバを介してデータベースにアクセスすることで、SQL操作の必要な処理が実装されます。汎用のISO SQLJ標準では、SQLJランタイムはJDBCドライバを使用してデータベースにアクセスする必要はありません。

トランスレータとランタイムに加え、カスタマイザと呼ばれるコンポーネントも役割を担います。カスタマイザを使用すると、特定のデータベースの実装、ベンダー固有の機能およびデータ型に合わせてSQLJプロファイルが調整されます。デフォルトでは、ISO標準コードに対して、SQLJフロントエンドがOracleカスタマイザを起動し、Oracle DatabaseインスタンスとOracle固有の機能およびデータ型に合わせてプロファイルを調整します。

変換時にOracleカスタマイザを使用すると、アプリケーションの実行時にSQLJランタイムおよびOracle JDBCドライバが必要になります。

注意:

Oracle Database 10g リリース1以降、Oracle JDBCドライバのみがSQLJでサポートされています。

4.8.3 SQLJプロファイル

ISO標準コード生成の場合、SQLJプロファイルとは、SQLJトランスレータによって生成される、シリアライズされたJavaリソースまたはクラスのことで、埋込みSQL文に関する詳細情報が記述されています。トランスレータはこれらのプロファイルを作成します。次に、トランスレータのオプション設定に従って、プロファイルをシリアライズしてバイナリ・リソース・ファイルに出力するか、.classファイルに出力します。

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

4.8.3.1 プロファイルの概要

ISO標準コードでは、SQLJ実行文に埋込みSQL操作を実装する場合にSQLJプロファイルを使用します。プロファイルの内容は、SQL操作およびアクセスするデータの型とモードに関する情報です。プロファイルはエントリの集合であり、各エントリが1つのSQL操作に対応します。各エントリでは、対応するSQL操作が十分に指定され、この指示の処理時に使用される各パラメータが記述されています。

SQLJでは、アプリケーション内の接続コンテキスト・クラスごとにプロファイルが生成され、各接続コンテキスト・クラスはデータベース操作で使用する特定のSQLエンティティのセットに対応しています。デフォルトの接続コンテキスト・クラスが1つあり、さらにクラスを宣言できます。ISO SQLJ規格では、プロファイルを標準の形式および内容にする必要があります。そのため、アプリケーションでベンダー固有の拡張機能を使用する場合は、プロファイルのカスタマイズが必要になります。デフォルトの設定では、これが自動的に行われ、Oracle固有の拡張機能を使用するようにプロファイルがカスタマイズされます。

プロファイルをカスタマイズすると、次の利点があります。

  • ベンダー固有のデータ型とSQL構文を使用できます。たとえば、Oracleカスタマイザを使用して、変換済SQLJコードのJDBC標準PreparedStatementメソッド・コールをOraclePreparedStatementメソッド・コールにマッピングすることにより、Oracle拡張型がサポートされます。

  • ベンダー固有の最適化によってパフォーマンスを向上できます。

注意:

  • デフォルトの設定では、SQLJプロファイルの拡張子が.serになりますが、すべての.serファイルがプロファイルであるとは限りません。その他のシリアライズされたオブジェクトにもこの拡張子を使用できるため、SQLJプログラム・ユニットでプロファイル以外のシリアライズされたオブジェクトを使用できます。必要に応じて、プロファイルは.serファイルではなく.classファイルにも変換できます。

  • ソース・コード中にSQLJ実行文を記述しないかぎり、SQLJプロファイルは生成されません。

4.8.3.2 バイナリ移植性

SQLJで生成したプロファイル・ファイルは、バイナリ移植性をサポートしています。つまり、プロファイル・ファイルをそのまま異種データベースまたは他の環境に移植して使用できます(ただし、ベンダー固有のデータ型または機能を使用していない場合に限ります)。生成した.classファイルについても同様です。

4.8.4 SQLJ変換処理

ISO標準コード生成(-codegen=iso)の場合は、トランスレータがSQLJソース・コードを処理し、SQL操作をSQLJランタイム・コールに変換し、Java出力コードと1つ以上のSQLJプロファイルを生成します。プロファイルは、ソース・コード中の接続コンテキスト・クラスごとに別個に生成されますが、これは操作で使用される相互に関係するSQLエンティティのセットごとに、通常は異なる接続コンテキスト・クラスが使用されるためです。

生成されたJavaコードは.java出力ファイルに保存されます。次の内容がこのファイルに出力されます。

  • .sqljソース・ファイル内のクラス定義とJavaコード。

  • SQLJイテレータおよび接続コンテキストの宣言に基づいて生成されたクラス定義。

  • 専用クラス(プロファイルキー・クラス)のクラス定義。SQLJでは、このクラスは、プロファイルとともに生成され、使用されます(ISO標準SQLJコード生成のみ)。

  • SQLJランタイムへのコール。埋込みSQL操作のアクションを実装します。

生成されたプロファイルには、実行するアクション、操作するデータ型、アクセスする表など、SQLJソース・コード中のすべての埋込みSQL文に関する情報が格納されます。アプリケーションを実行すると、SQLJランタイムはプロファイルにアクセスしてSQL操作を取り出し、JDBCドライバに渡します。

プロファイルは、デフォルトでシリアライズ・リソース・ファイル(.ser)に出力されますが、オプションで、変換時に.serファイルを.classファイルに変換できます。

生成されたJavaソース・ファイルがコンパイルされ、Javaの.classファイルが生成されます。これには、定義される各クラス、各SQLJ宣言、プロファイルキー・クラスに対する.classファイルが含まれます。次に、JVMでOracleカスタマイザまたは指定したその他のカスタマイザを起動して、生成したプロファイルをカスタマイズします。

SQLJの一般的な注意

ISO固有のコード生成でSQLJアプリケーションを変換および実行する場合は、次の点を考慮してください。

  • Oracle固有コード生成では、コマンドラインで既存の.javaファイルをコンパイルして、型変換可能にすることに加え、次を実行する必要があります。

    • 既存のプロファイルのカスタマイズ

    • プロファイルを含むJava Archive(JAR)ファイルのカスタマイズ

  • ソース・コードにSQLJ実行可能文が含まれる場合のみ、SQLJによってプロファイルおよびプロファイルキー・クラスが生成されます。

  • 変換時にOracleカスタマイザを使用する場合は、アプリケーションの実行時にOracle SQLJランタイムおよびOracle JDBCドライバが必要になります(コードでOracle固有の機能を使用しない場合でも必要です)。変換時に-profile=falseを指定して、Oracle固有のカスタマイズを省略すると、これを回避できます。

4.8.5 トランスレータ入出力の概要

Oracle固有のコード生成の場合に、SQLJトランスレータが入力としてとるもの、出力として生成するもの、およびその出力先について説明しました。ここでは、同じ項目について、ISO標準のコード生成の観点で説明します。

4.8.5.1 トランスレータへの入力

Oracle固有のコード生成の場合と同様に、SQLJトランスレータは、入力として1つ以上の.sqljソース・ファイルをとります。このソース・ファイルはコマンドラインで指定できます。メインの.sqljファイルの名前は、そこで定義されるPublicクラス(ある場合)または定義された最初のクラスに基づきます。

4.8.5.2 トランスレータの出力

変換処理では、アプリケーション内の.sqljファイルごとにJavaソース・ファイルが生成されます。また、ISO標準コード生成の場合、アプリケーションのプロファイルは、1つ以上生成されます(ソース・コードにSQLJ実行文があるとき)。

SQLJでは、次のJavaソース・ファイルとアプリケーション・プロファイルが生成されます。

  • Oracle固有のコード生成の場合と同様に、Javaソース・ファイルは.sqljファイルと同じベース名の.javaファイルです。

  • アプリケーションのプロファイル・ファイル(ある場合)には、SQLJアプリケーションのSQL操作に関する情報が含まれます。プロファイルは、アプリケーションで使用する接続クラスごとに1つ生成されます。プロファイルの名前は、メインの.sqljファイルと同じベース名になり、次の拡張子が付きます。

    _SJProfile0.ser
    _SJProfile1.ser
    _SJProfile2.ser
    ...
    

    たとえば、MyClass.sqljの場合は、トランスレータによって次のプロファイル・ファイルが生成されます。

    MyClass_SJProfile0.ser
    

    .serファイル拡張子は、プロファイルがシリアライズされていることを示します。.serファイルはバイナリ・ファイルです。

    注意:

    トランスレータ・オプション-ser2classを指定すると、トランスレータによって生成されるプロファイル・ファイルが.serファイルではなく.classファイルになります。ファイル名の拡張子以外の部分は、同じになります。

Oracle固有のコード生成のコンパイル手順と同様に、Javaソース・ファイルを複数のクラス・ファイルにコンパイルすると、.sqljソース・ファイルに定義されたクラスごとに、1つの.classファイルが生成されます。ただし、ISOコード生成の場合は、トランスレータがSQL操作を実装するためにプロファイルとともに生成して使用するプロファイルキー・クラスと呼ばれるクラスに対しても、1つの.classファイルが生成されます。SQLJのイテレータまたは接続コンテキストを宣言した場合は、さらに.classファイルが生成されます。また、Oracle固有コード生成と同様に、コード中に内部クラスまたは無名クラスが宣言されている場合は、それらのクラスごとに.classファイルが個別に生成されます。

これらの.classファイルには、次の名前が付けられます。

  • Oracle固有のコード生成と同様に、定義された各クラスのクラス・ファイル名は、クラス名および.class拡張子で構成されます。

  • トランスレータで生成されたプロファイルキー・クラスは、メインの.sqljファイルのベース名と次の文字列から構成される名前が付けられます。

    _SJProfileKeys
    

    つまり、クラス・ファイルの拡張子は、次のような名前になります。

    _SJProfileKeys.class
    

    たとえば、MyClass.sqljの場合は、トランスレータとコンパイラによって次のクラス・ファイルが生成されます。

    MyClass_SJProfileKeys.class
    
  • Oracle固有のコード生成と同様に、トランスレータを実行すると、イテレータ・クラスと接続コンテキスト・クラスに、宣言方法に従って名前が付けられます。

カスタマイズ処理によってプロファイルが変更されますが、追加出力はありません。

注意:

必ずしもSQLJプロファイルまたはプロファイルキー・クラスを直接参照する必要はありません。これらは、すべて自動的に処理されます。

4.8.5.3 ファイルの出力先

Oracle固有のコード生成とISO標準のコード生成のファイルの出力先は同じです。

4.8.6 SQLJランタイム処理

ここでは、プログラム実行時のISO標準コードに対するランタイム処理について説明します。

ISO標準SQLJアプリケーションの場合は、SQLJランタイムがプロファイルを読み取って接続プロファイルを作成し、接続プロファイルにデータベース接続情報が組み込まれます。アプリケーションでデータベースへのアクセスが必要になると、そのつど次に示す処理が発生します。

  1. SQLJで生成したアプリケーション・コードは、SQLJで生成したプロファイルキー・クラスのメソッドを使用して接続プロファイルにアクセスし、関係するSQL操作を読み取ります。アプリケーション内のSQLJ実行文とプロファイル内のSQL操作はマッピングされています。

  2. SQLJで生成したアプリケーション・コードからSQLJランタイムをコールします。SQL操作がプロファイルから読み込まれます。

  3. SQLJランタイムがJDBCドライバをコールして、SQL操作をドライバに渡します。

  4. SQLJランタイムが入力パラメータをJDBCドライバに渡します。

  5. JDBCドライバがSQL操作を実行します。

  6. 戻り値がある場合は、データベースがデータをJDBCドライバに送ります。このデータはドライバからSQLJランタイムに送られ、アプリケーションで使用できるようになります。

注意:

入力パラメータを渡すことは、入力パラメータをバインドするまたはホスト式をバインドするとも言います。ホスト変数、ホスト式、バインド変数およびバインド式という用語はすべて、SQL操作の入力または出力として使用されるJava変数または式を説明するために使用されます。

4.8.7 デプロイメント例

次の方法でOracle固有のSQLJコードを実行する方法について説明しました。

  • アプレットから

  • サーバー内で(SQLJトランスレータもサーバーで実行できます)

アプレットからISO標準コードを実行する際に、いくつかの考慮事項があります。

  • 次のSQLJランタイム・パッケージはすべて、アプレットとともにパッケージ化する必要があります。次のパッケージを含めます。

    sqlj.runtime
    sqlj.runtime.ref
    sqlj.runtime.profile
    sqlj.runtime.profile.ref
    sqlj.runtime.error
    

    Oracle独自のカスタマイズを行った場合は、次のパッケージも含める必要があります。

    oracle.sqlj.runtime
    oracle.sqlj.runtime.error
    

    これらのパッケージは、Oracleインストールによって、ORACLE_HOME/libディレクトリのランタイム・ライブラリの中の1つに組み込まれています。

  • Netscape Navigator 4.xなどの一部のブラウザでは、プロファイル用のSQLJシリアル化オブジェクト・ファイルの拡張子である、.ser拡張子の付いたリソース・ファイルをサポートしていません。ただし、Sun MicrosystemsのJavaプラグインでは.serファイルをサポートしています。

    Oracle SQLJ実装では、このプラグインを使用する以外の方法として、.serファイルを.classファイルに変換するための-ser2classオプションを使用する方法があります。

    注意:

    この考慮事項は、プロファイルが生成されないデフォルトのOracle固有コード生成には適用されません

  • Oracle固有の機能を使用するアプレットでは、Oracle SQLJランタイムを起動する必要があります。Oracle SQLJランタイムは、oracle.sqlj.*下のSQLJランタイム・ライブラリ・ファイル内のクラスで構成されます。Oracle SQLJ runtime.jarライブラリでは、Java Reflection API(java.lang.reflect.*)が必要です。大半のブラウザでは、Reflection APIをサポートしていなかったりセキュリティ上の制約があるのに対し、Sun社のJavaプラグインの場合はReflection APIに対するサポートが提供されています。

    ISO標準のコード生成の場合、次のSQLJ言語の機能では、使用しているSQLJランタイムのバージョンに関係なく、常にJava Reflection APIが必要となります。

    • CAST

    • SQLJイテレータのインスタンスとしてのデータベースから取得されるREF CURSORパラメータまたはREF CURSOR

    • java.sql.RefStructArrayBlobまたはClobオブジェクトの取得

    • oracle.sql.ORADataまたはjava.sql.SQLDataインタフェースを実装するJavaクラスのインスタンスとしてのSQLオブジェクトの取得

      注意:

      • ただし、ISOと完全に互換性のあるモードでSQLJを使用する場合は例外です。つまり、SQLJをJ2EEに準拠した環境で使用する場合、SQLJ runtime12ee.jarライブラリでプログラムを変換および実行する場合、およびISOで指定したように接続コンテキストの型マップを採用する場合です。この場合、java.sql.RefStructArrayBlobClobおよびSQLDataのインスタンスは、リフレクションを使用せずに取得されます。

      • Oracle固有コード生成を使用すると、前述のすべてのインスタンスでのリフレクションの使用を削減します。

4.9 Oracle固有コード生成とISO標準コード生成との違い

Oracle SQLJの実装により、Oracle固有コード生成が可能になり、Oracle JDBCコールがコード内に直接生成されます。これはデフォルトの動作です。Oracle固有コード生成の場合、次の点に注意する必要があります。

  • プロファイル・ファイルがないため、変換時のカスタマイズ処理もありません。

  • 変換されたコード内に、SQLJランタイム・コールではなくJDBCコールが直接生成されるため、実行時にSQL操作はSQLJランタイム・レイヤーを経由する必要がありません。

4.10 ネーミングの要件および制限事項

ネーミング要件、ネーミング制限および予約語については、次の4点を考慮する必要があります。

  • Javaのネームスペース。ローカル変数とクラスの名前については、SQLJ固有の制限もあります。

  • SQLJのネームスペース

  • SQLのネームスペース

  • ソース・ファイル名

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

4.10.1 Javaのネームスペース: ローカル変数およびクラスのネーミングに関する制限事項

Javaのネームスペースは、Javaのクラスやローカル変数のネーミングなど、標準Javaのすべての文および宣言に適用されます。標準Javaのネーミングに関するすべての制限が適用されるため、Javaの予約語は使用しないでください。

また、ローカル変数とクラスのネーミングには、SQLJ固有の制限も若干伴います。

注意:

ホスト変数名固有の制約については、「ホスト式の制限事項」を参照してください。

ローカル変数のネーミングに関する制限

ローカル変数のネーミングの際は、SQLJトランスレータの一部の機能に起因する制限が若干伴います。SQLJトランスレータは各SQLJ実行文を文ブロックに置き換えますが、このブロックでは、次のように標準構文に基づくSQLJ実行文が使用されます。

#sql { SQL operation };  

SQLJでは、生成された文ブロック内に一時変数を宣言できます。この一時変数の名前には、次の接頭辞が付きます。

 __sJT_

注意:

アンダースコアが先頭に2つ、末尾に1つ付きます。

SQLJで生成した文ブロックでは、次のような宣言が使用されます。

int __sJT_index;
Object __sJT_key;
java.sql.PreparedStatement __sJT_stmt;

文字列__sJT_は、SQLJで生成される変数名の接頭辞として予約されています。SQLJのプログラミングの際は、この文字列を次の名前の接頭辞としては使用しないでください。

  • SQL実行文を含むブロックで宣言した変数名

  • SQL実行文を含むメソッドのパラメータ名

  • SQL実行文を含むクラスのフィールド名、あるいはサブクラスまたは被包含クラスにSQL実行文があるクラスのフィールド名

クラスのネーミングに関する制限

SQLJアプリケーションのクラスのネーミングには、次のような制限が伴います。

  • SQLJの内部クラスと競合する可能性のあるクラス名は宣言しないでください。具体的には、SQLJアプリケーション内の既存クラスの名前がaの場合、最上位クラスに次の形式の名前を付けることはできません。

    a_SJb
    

    abは正当なJava識別子です。

    たとえば、ファイルFoo.sqlj内のアプリケーション・クラスがFooである場合、SQLJはFoo_SJProfileKeysというプロファイルキー・クラスを生成します。この名前と競合するクラス名は宣言しないでください。

  • SQLJ実行文を含むクラスの名前が、アプリケーションで使用されるJava型を格納するどのパッケージの名前の先頭部分とも同じにならないようにしてください。たとえば、javasqljおよびoracle(大/小文字の区別あり)は、クラス名としては使用しないでください。また、SQLJ文に使用するホスト変数の型がabc.def.MyClassである場合、このホスト変数を使用するクラスの名前にはabcを使用できません。

    こうした制限を回避するには、Javaのネーミング規則に従い、パッケージ名の先頭を小文字にし、クラス名の先頭を大文字にすることをお薦めします。

4.10.2 SQLJのネームスペース

SQLJのネームスペースは、#sqlクラス宣言と#sql実行文の中カッコの外側の部分です。

注意:

イテレータ列のネーミングに伴う特別な制限については、「名前付きイテレータの使用」を参照してください。

SQLJの次の予約語は、宣言する接続コンテキスト・クラスまたはイテレータ・クラスのクラス名として、with句またはimplements句で使用できません。イテレータ列の型宣言リストでも使用できません。

  • iterator

  • context

  • with

たとえば、イテレータ・クラスまたはインスタンスの名前をiteratorにしたり、接続コンテキスト・クラスまたはインスタンスの名前をcontextにしたりしないでください。

ただし、ストアド・ファンクションからの戻り値を格納する変数の名前には、これらのどのワードでも使用できます。

4.10.3 SQLのネームスペース

SQLのネームスペースは、SQLJ実行文の中カッコ内の部分です。このネームスペースには、標準SQLのネーミングに関する制限事項が適用されます。

ただし、ホスト式は、SQLのネームスペースの規則ではなく、Javaのネームスペースの規則に従います。これは、ホスト変数の名前にも、ホスト式の外部のカッコで囲まれたすべての部分にも適用されます。

4.10.4 ファイル名の要件と制限

SQLJソース・ファイルのファイル名拡張子は.sqljです。ソース・ファイルでPublicクラスを宣言した場合(最大1つ)、ソース・ファイルのベース名がこのクラス名と一致する必要があります(大/小文字の区別あり)。ソース・ファイルでPublicクラスが宣言されていない場合、ファイル名はJava識別子として有効である必要があるため、ファイル名を最初に定義されたクラスの名前と同じにすることをお薦めします。

たとえば、ソース・ファイルにPublicクラスMySourceが定義されている場合は、次に示すファイル名を付けてください。

MySource.sqlj

注意:

これらのファイル・ネーミング要件は、Java言語仕様(JLS)準拠であり、SQLJ固有のものではありません。これらの要件は、Oracle Database 12c リリース2 (12.2)には直接適用されませんが、それらに従うことをお薦めします。

4.11 中間層のSQLJに関する考慮事項

Oracle9i Application Server Containers for J2EE (OC4J)環境など、中間層でSQLJを実行する場合は特別に考慮する事項があります。

Oracle JDBCドライバでは、oracle.jdbcパッケージにOracle固有のインタフェースが提供されます。Oracle SQLJライブラリ(runtime12.jarおよびruntime12ee.jar)は、これらのインタフェースを最大限に活用しますが、これらのライブラリには、Oracle9i Application Serverより前のOracle JDBC実装との互換性がありません。

Oracle9i Application Serverの場合、接続はデータ・ソースを介して確立され、通常は旧式のoracle.jdbc.driver.OracleConnectionクラスではなく、oracle.jdbc.OracleConnectionインタフェースのインスタンスを戻します。これは分散トランザクション(XA)などの特定の接続機能に必要です。このような機能をサポートするには、接続オブジェクトが新規インタフェースを実装する必要があります。

この結果、Oracle9i Application Server中間層環境またはデータ・ソースが使用される他の環境について、次のような処理が必要となります。

  • コードの移植性と柔軟性を最大限にするには、oracle.jdbc.driver.OracleXXX型ではなくoracle.jdbc.OracleXXX型を使用します。

  • カスタムJava型(通常はSQLオブジェクトとコレクション)については、oracle.sql.ORADataを実装します。

  • SQLJ runtimeライブラリを使用しないでください。かわりに(環境に応じて)runtime12またはruntime12eeを使用してください。ランタイム・ライブラリは、Oracle8i Databaseリリース8.1.7などの古いJDBCドライバとの下位互換性があるため、oracle.jdbc.OracleXXX型ではなく、oracle.jdbc.driver.OracleXXX型がサポートされています。

    しかし、なんらかの理由でruntimeライブラリを使用する必要がある場合は、変換時にオプション-profile=falseを設定します。その場合、プログラムではOracle固有のカスタマイズが使用されず、そのため、oracle.jdbc.driver.OracleConnectionインスタンスのかわりにoracle.jdbc.OracleConnectionインスタンスを渡しても処理は失敗しません。この状況では、Oracle固有の機能はサポートされません

データ・ソースを介して取得される接続の管理を容易にしたり、JavaBean (SQLJ JavaServer Pages用)を接続するために、Oracle SQLJ実装では、runtime12eeライブラリに多くのAPIが用意されています。

データ・ソースおよび接続JavaBeansへのSQLJサポートの概要は、次の項を参照してください。