JDBC データベースへのアクセス
この章では、Caché JDBC ドライバを使用してデータベースを検索し、その結果を処理する Java コードの例を説明します。
-
"簡単な JDBC アプリケーション" — JDBC の基本機能を示す完全だが非常に簡単なアプリケーションです。
-
"ステートメントの使用方法" — JDBC SQL クエリ・クラスの概要です。
-
"データの挿入および更新" — JDBC 結果セットを使用して、Caché データベースにデータを挿入して更新する方法です。
-
"JDBC アプリケーションのログ" — ログを有効にして、JDBC アプリケーションのテストおよびトラブルシューティングを行う方法です。
この章で提供される例では、いくつかのメソッドは、SQLException 型の例外を返します。わかりやすくするために、必要な try ブロックおよび catch ブロックは省略します。
簡単な JDBC アプリケーション
このセクションでは、最も一般的な JDBC クラスの一部の使用法を例示する非常に簡単な JDBC アプリケーションについて説明します。
-
CacheDataSource オブジェクトは、JDBC アプリケーションを Caché データベースにリンクする Connection オブジェクトの作成に使用されます。
-
Connection オブジェクトは、ダイナミック SQL クエリを実行可能な PreparedStatement オブジェクトの作成に使用されます。
-
PreparedStatement クエリは、要求された行を含む ResultSet オブジェクトを返します。
-
ResultSet オブジェクトには、特定の行に移動し、その行の特定の列の読み取りまたは更新を行うために使用することができるメソッドがあります。
これらのクラスについては、すべてこの章の後で詳しく説明します。
最初に、JDBC パッケージをインポートし、try ブロックを開きます。
import java.sql.*;
import javax.sql.*;
import com.intersys.jdbc.*;
public class TinyJDBC{
public static void main() {
try {
接続を開くには、次のように CacheDataSource を使用します (詳細は、"接続のための CacheDataSource の使用法" を参照してください)。
Class.forName ("com.intersys.jdbc.CacheDriver").newInstance();
CacheDataSource ds = new CacheDataSource();
ds.setURL("jdbc:Cache://127.0.0.1:1972/SAMPLES");
Connection dbconn = ds.getConnection("_SYSTEM","SYS");
クエリを実行し、スクロールおよび更新可能な結果セットを取得します。
String sql="Select Name from Sample.Person Order By Name";
int scroll=ResultSet.TYPE_SCROLL_SENSITIVE;
int update=ResultSet.CONCUR_UPDATABLE;
PreparedStatement pstmt = dbconn.prepareStatement(sql,scroll,update);
java.sql.ResultSet rs = pstmt.executeQuery();
結果セットの最初の行に移動し、名前を変更します。
rs.first();
System.out.println("\n Old name = " + rs.getString("Name"));
rs.updateString("Name", "Bill. Buffalo");
rs.updateRow();
System.out.println("\n New name = " + rs.getString("Name") + "\n");
オブジェクトを閉じ、例外を検出します。
pstmt.close();
rs.close();
dbconn.close();
} catch (Exception ex) {
System.out.println("TinyJDBC caught exception: "
+ ex.getClass().getName() + ": " + ex.getMessage());
}
} // end main()
} // end class TinyJDBC
この章の残りの部分では、アプリケーション全体ではなくコード断片として例を表します。これらの例は、基本機能をできる限り簡単かつ明確に例示することを意図しています。接続が既に開かれていて、コード断片すべてが適切な try/catch 文の内側にあることを前提とします。また、ユーザが標準的なコーディング作業に精通していることを前提としています。標準的なコーディング作業については、ここでは説明しません。
ステートメントの使用方法
sql.java パッケージは、データベースのクエリに使用する 3 つのクラスを提供し、ResultSet:Statement、PreparedStatement、および CallableStatement を返します。3 つのクラスは、すべて Connection メソッドの呼び出しによってインスタンス化されます。以下のセクションでは、これら 3 つのクラスの使用方法を説明します。
-
"SELECT を実行する文の使用方法" — 非常に簡単な文呼び出しです。
-
"作成済み文の実行" — より詳細な例です。
-
"CallableStatement を使用したストアド・プロシージャの実行" — Sample.PersonOpens in a new tab から ByName ストアド・プロシージャを実行する例です。
-
"複数の結果セットを返す" — Caché ストアド・プロシージャによって返される複数の結果セットへのアクセス方法です。
-
"文のプーリング" — Caché ドライバが最適化された文を格納し、使用する方法です。
SELECT を実行する文の使用方法
以下のコードは、Statement クラスを使用して Caché で SQL SELECT を実行します。
-
クエリ文字列を作成し、これを次のように java.sql.Statement execute() メソッドを使用して実行します。
String stQuery="SELECT ID, Name from Sample.Person"; java.sql.ResultSet rs = stmt.executeQuery(stQuery);
常に完全修飾名 java.sql.ResultSet を使用して、com.intersys.classes.ResultSet とのクラッシュを回避する必要があります。
-
次のように、クエリの結果を処理して表示します。
ResultSetMetaData rsmd = rs.getMetaData(); int colnum = rsmd.getColumnCount(); while (rs.next()) { for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " "); }
作成済み文の実行
以下のクエリは、作成済み文を使用して、M ~ Z で始まる名前の会社を担当する “A” ~ E で始まる名前の従業員すべてのリストを返します。
Select ID, Name, Company->Name from Sample.Employee
Where Name < ? and Company->Name > ?
Order By Company->Name
この文は、暗黙結合構文 (–> 演算子) を使用しています。この構文は、Sample.EmployeeOpens in a new tab が参照する Company クラスにアクセスする簡単な方法を提供します。
作成済み文は、通常の文と同様に実装されます。
-
クエリを含む文字列を作成し、これを使用して PreparedStatement オブジェクトを初期化した後、クエリ・パラメータ値を設定し、クエリを実行します。
String sql= "Select ID, Name, Company->Name from Sample.Employee " + "Where Name < ? and Company->Name > ? " + "Order By Company->Name"; PreparedStatement pstmt = dbconnection.prepareStatement(sql); pstmt.setString(1,"F"); pstmt.setString(2,"L"); java.sql.ResultSet rs = pstmt.executeQuery();
-
結果セットを取得して表示します。
java.sql.ResultSet rs = pstmt.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int colnum = rsmd.getColumnCount(); while (rs.next()) { for (int i=1; i<=colnum; i++) { System.out.print(rs.getString(i) + " "); } System.out.println(); }
ストアド・プロシージャを実行する呼び出し可能な文の使用方法
以下のコードは、ByName (Sample.PersonOpens in a new tab に格納されている Caché ストアド・プロシージャ) を実行します。
-
java.sql.CallableStatement オブジェクトを作成し、これをストアド・プロシージャの名前で初期化します。プロシージャの SqlName は、SP_Sample_By_Name です。これは、Java クライアント・コード内でこの名前がどのように参照されるかを示します。
String sql="call Sample.SP_Sample_By_Name(?)" CallableStatement cs = dbconnection.prepareCall(sql);
-
クエリ・パラメータ値を設定し、このクエリを実行した後、結果セットを繰り返し処理してデータを表示します。
cs.setString(1,"A"); java.sql.ResultSet rs = cs.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int colnum = rsmd.getColumnCount(); while (rs.next()) { for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " "); } System.out.println();
複数の結果セットを返す
Caché では、複数の結果セットを返すストアド・プロシージャを定義できます。Caché JDBC ドライバは、このようなストアド・プロシージャの実行をサポートしています。次に、2 つの結果セットを返す Caché ストアド・プロシージャの例を示します (2 つのクエリ結果の列構造は異なっています)。
/// This class method produces two result sets.
ClassMethod DRS(st) [ ReturnResultsets, SqlProc ]
{
$$$ResultSet("select Name from Sample.Person where Name %STARTSWITH :st")
$$$ResultSet("select Name, DOB from Sample.Person where Name %STARTSWITH :st")
Quit
}
このストアド・プロシージャは、Sample.PersonOpens in a new tab では定義されていません。この例を試行するために、最初に Atelier で Sample.PersonOpens in a new tab を開いて、上記のクラス・メソッドを追加する必要があります。$$$ResultSet ルーチンは、SQL 文 (コンパイル時に利用可能な文字列リテラルである必要がある) を作成して実行し、結果セットを返します。これを使用するには、Sample.PersonOpens in a new tab ファイルの先頭 (クラス定義の前、下記を参照) に宣言 include %occResultSet を追加する必要があります。
include %occResultSet
Class Sample.Person Extends (%Persistent, %Populate, %XML.Adaptor)
{ ...
これらの変更を行った後、Sample.PersonOpens in a new tab をリコンパイルしてください。
以下のコードは、このストアド・プロシージャを実行し、返された結果セット両方の繰り返し処理を行います。
-
java.sql.CallableStatement オブジェクトを作成し、これをストアド・プロシージャの名前を使用して初期化します。クエリ・パラメータを設定し、execute を使用して、クエリを実行します。
CallableStatement cs = dbconnection.prepareCall("call Sample.Person_DRS(?)"); cs.setString(1,"A"); boolean success=cs.execute();
-
データを表示する結果セットのペアに繰り返し処理を行います。getMoreResults は Statement オブジェクトの次の結果セットに移動し、getResultSet は現在の結果セットを取得することに注意してください。
if(success) do{ java.sql.ResultSet rs = cs.getResultSet(); ResultSetMetaData rsmd = rs.getMetaData(); for (int j=1; j<rsmd.getColumnCount() + 1; j++) System.out.print(rsmd.getColumnName(j)+ "\t\t"); System.out.println(); int colnum = rsmd.getColumnCount(); while (rs.next()) { for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " \t "); System.out.println(); } System.out.println(); } while (cs.getMoreResults());
既定では、getMoreResults は、次の結果セットに移動する前に現在の結果セットを閉じます。Caché JDBC ドライバは、次の結果セットに移動した後に現在の結果セットを開いたままにしておくことをサポートしていません。
文のプーリング
JDBC 4.0 は、追加のインフラストラクチャである「文のプーリング」を追加しています。この機能により、最適化された文は、最初に使用されたときにキャッシュに格納されます。文のプールは、接続プールによって維持され、プールされた文を接続間で共有できます。実装の詳細は、ユーザに対して完全に透過的です。必要な機能を提供するかどうかはドライバで決まります。
Caché JDBC は、この概念が JDBC 仕様の一部になるかなり前から文のプーリングを実装していました。一方、Caché ドライバは、この仕様で推奨されているテクニックに類似したテクニックを使用しており、実際にプーリングを実装すると高度に最適化されます。ほとんどの実装と異なり、Caché JDBC は、3 つの別々の文プーリング・キャッシュを備えています。1 つは JDBC 仕様で定義されている文プーリングにほぼ相当しますが、その他の 2 つは Caché 特有の最適化です。Caché 文のキャッシングの説明は、"Caché SQL 最適化ガイド" の "クエリ・キャッシュ" を参照してください。必要に応じて、Caché JDBC 文プーリングは、ユーザに対して完全に透過的になります。
Caché JDBC の実装は、Statement メソッドである setPoolable() および isPoolable() を、当該の文をプールする必要があるかどうかのヒントとしてサポートします。Caché は、独自のヒューリスティックを使用して、3 つの文プールすべての適切なサイズを決定します。したがって、ConnectionPoolDataSource の maxStatements プロパティを設定することによる文プール・サイズの制約をサポートしません。オプションの javax.sql.StatementEventListener インタフェースは同じ理由でサポートされません (また、重要ではありません)。
データの挿入および更新
JDBC を使用して Caché データを挿入および更新するには、以下のような方法があります。
-
"データの挿入および生成されるキーの取得" — PreparedStatement および SQL INSERT コマンドを使用します。
-
"結果セットのスクロール" — 結果セットの任意の行にランダムにアクセスします。
-
"スクロール可能な結果セットの更新" — 結果セットの行のデータにアクセスし、データを変更します。
-
"トランザクションの使用法" — JDBC トランザクション API を使用して変更をコミットまたはロールバックします。
このセッションでは、いくつかの例で新しい行を Sample.PersonOpens in a new tab に挿入します。そのためには、一意のキーとして SSN (社会保障番号) が必要です。これらの例で以下のメソッドが使用され、nnn-nn-nnnn の形式のランダムの SSN が生成されます。
public static String makeTestSSN() {
java.util.Random random = new java.util.Random();
StringBuffer sb = new StringBuffer();
for (int i=1; i<=9; i++) sb.append(random.nextInt(10));
sb.insert(5,'-');
sb.insert(3,'-');
return sb.toString();
}
データの挿入および生成されるキーの取得
以下のコードは、新しい行を Sample.PersonOpens in a new tab に挿入し、生成される ID キーを取得します。
-
PreparedStatement オブジェクトを作成し、これを SQL 文字列を使用して初期化し、生成されるキーが返されるように指定します。
String sqlIn="INSERT INTO Sample.Person (Name,SSN,DOB) " + "VALUES(?,?,?)"; int keys=Statement.RETURN_GENERATED_KEYS; PreparedStatement pstmt = dbconnection.prepareStatement(sqlIn, keys);
-
クエリ・パラメータ値を設定し、更新を実行します (makeTestSSN() 呼び出しの説明は、"makeTestSSN() メソッド" を参照)。
String SSN = makeTestSSN(); // generate a random SSN java.sql.Date DOB = java.sql.Date.valueOf("1973-02-01"); pstmt.setString(1,"Smith,John"); // Name pstmt.setString(2,SSN); // Social Security Number pstmt.setDate(3,DOB); // Date of Birth pstmt.executeUpdate();
-
新しい行を挿入するたびに、システムは、その行のオブジェクト ID を自動的に生成します。生成される ID キーは、結果セットに取得され、SSN と共に表示されます。
java.sql.ResultSet rsKeys = pstmt.getGeneratedKeys(); rsKeys.next(); String newID=rsKeys.getString(1); System.out.println("new ID for SSN " + SSN + " is " + newID);
このコードは ID が rsKeys で生成される最初で唯一のキーであることを想定していますが、実際にはこの想定が常に安全であるとは限りません。
-
ID で新しい行を取得し、これを表示します (Age は DOB に基づいて計算される値です)。
String sqlOut="SELECT ID,Name,Age,SSN FROM Sample.Person WHERE ID="+newID; pstmt = dbconnection.prepareStatement(sqlOut); java.sql.ResultSet rsPerson = pstmt.executeQuery(); int colnum = rsPerson.getMetaData().getColumnCount(); rsPerson.next(); for (int i=1; i<=colnum; i++) System.out.print(rsPerson.getString(i) + " "); System.out.println();
結果セットのスクロール
Caché JDBC ドライバは、スクロール可能な結果セットをサポートします。これにより、Java アプリケーションでこの結果セット・データを順方向および逆方向に移動できます。prepareStatement() メソッドは、以下のパラメータを使用して、結果セットがどのように機能するかを決定します。
-
resultSetType パラメータは、変更の表示方法を決定します。
-
ResultSet.TYPE_SCROLL_SENSITIVE は、別のプロセスで基本データに対して行われた変更を表示するスクロール可能な結果セットを作成します。
-
ResultSet.TYPE_SCROLL_INSENSITIVE は、現在のプロセスで行われた変更のみを表示するスクロール可能な結果セットを作成します。
-
-
resultSetConcurrency パラメータは、結果セットを更新する場合、ResultSet.CONCUR_UPDATABLE に設定する必要があります。
以下のコードは、スクロール可能な結果セットを作成して使用します。
-
PreparedStatement オブジェクトを作成し、クエリ・パラメータを設定して、このクエリを実行します。
String sql="Select ID, Name, SSN from Sample.Person "+ " Where Name > ? Order By Name"; int scroll=ResultSet.TYPE_SCROLL_INSENSITIVE; int update=ResultSet.CONCUR_UPDATABLE; PreparedStatement pstmt = dbconnection.prepareStatement(sql,scroll,update); pstmt.setString(1,"S"); java.sql.ResultSet rs = pstmt.executeQuery();
-
このアプリケーションでは、この結果セットを順方向および逆方向にスクロールできます。結果セットのカーソルを最後の行の後ろに移動するには、afterLast を使用します。逆方向にスクロールするには、previous を使用します。
rs.afterLast(); int colnum = rs.getMetaData().getColumnCount(); while (rs.previous()) { for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " "); System.out.println(); }
-
absolute を使用して、特定の行に移動します。このコードでは 3 番目の行の内容が表示されます。
rs.absolute(3); for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " "); System.out.println();
-
relative を使用して、現在の行を基準にした特定の行に移動します。以下のコードでは、最初の行に移動して 2 行下方にスクロールした後、3 行目を再度表示します。
rs.first(); rs.relative(2); for (int i=1; i<=colnum; i++) System.out.print(rs.getString(i) + " "); System.out.println();
スクロール可能な結果セットの更新
以下のコードでは、開いている結果セットを更新し、データベースに対する変更を保存します。
-
PreparedStatement オブジェクトを作成し、クエリ・パラメータを設定して、このクエリを実行します。
String sql="Select Name, SSN from Sample.Person "+ " Where Name > ? Order By Name"; int scroll=ResultSet.TYPE_SCROLL_SENSITIVE; int update=ResultSet.CONCUR_UPDATABLE; PreparedStatement pstmt = dbconnection.prepareStatement(sql,scroll,update); pstmt.setString(1,"S"); java.sql.ResultSet rs = pstmt.executeQuery();
新しい行が挿入される結果セットには、Caché ID 列を含めないでください。ID 値は、Caché によって自動的に定義されます。
-
行を更新するには、カーソルを更新する行に移動し、目的の列を更新してから updateRow を呼び出します。
rs.last(); rs.updateString("Name", "Avery. Tara R"); rs.updateRow();
-
行を挿入するには、カーソルを “挿入行” に移動してからその行の列を更新します。NULL 値が許容されない列がすべて更新されていることを確認します (makeTestSSN() 呼び出しの説明は、"makeTestSSN() メソッド" を参照)。最後に、insertRow を呼び出します。
rs.moveToInsertRow(); rs.updateString(1, "Abelson,Alan"); rs.updateString(2, makeTestSSN()); rs.insertRow();
トランザクションの使用法
Caché JDBC ドライバは、JDBC トランザクション API をサポートしています。
-
SQL 文をトランザクションにグループ化するには、最初に次のように setAutoCommit() を使用して、自動コミット・モードを無効化する必要があります。
dbconnection.setAutoCommit(false);
-
commit() の最後の実行またはロールバック以降に実行されたすべての SQL 文をデータベースに対してコミットするには、commit() を使用します。
pstmt1.execute(); pstmt2.execute(); pstmt3.execute(); dbconnection.commit();
-
トランザクション内のすべてのトランザクションをロールバックするには、rollback() を使用します。ここで、SQLException がトランザクションで任意の SQL 文によってスローされる場合、rollback() が呼び出されます。
catch(SQLException ex) { if (dbconnection != null) { try { dbconnection.rollback(); } catch (SQLException excep){ // (handle exception) } } }
トランザクションの処理メソッド
次に、トランザクション処理に使用する java.sql.Connection メソッドの概要を説明します。
既定では、Connection オブジェクトは自動コミット・モードになっています。このモードでは、SQL 文は実行されるとすぐにコミットされます。複数の SQL 文を 1 つのトランザクションにグループ化するには、最初に setAutoCommit(false) を使用して Connection オブジェクトの自動コミット・モードを終了します。setAutoCommit(true) を使用して、Connection オブジェクトを自動コミット・モードに再設定します。
commit() を実行すると、commit() または rollback() の最後の実行以降に実行されたすべての SQL 文がコミットされます。最初に自動コミットを false に設定しないで commit() を呼び出すと、例外が返されないことに注意してください。
rollback を実行すると、トランザクションが中止され、トランザクションによって変更されたすべての値が元の状態にリストアされます。
トランザクションの分離レベルを設定します。Caché は、以下の JDBC トランザクション分離レベルをサポートしています。
-
Connection.TRANSACTION_READ_UNCOMMITTED — レベル 1。ダーティ・リード、非再現リード、およびファントム・リードを許可します。
-
Connection.TRANSACTION_READ_COMMITTED — レベル 2。ダーティ・リードを防止しますが、非再現リード、およびファントム・リードを許可します。
Connection オブジェクトの現在のトランザクション分離レベルを返します。
JDBC アプリケーションのログ
アプリケーションで問題が発生した場合、適切なログを有効にしてアプリケーションを監視できます。
アプリケーションを実行し、エラー条件をトリガしたことを確認した後、エラー・メッセージや、異常な活動のすべてのログ記録をチェックします。エラーの原因は通常、メッセージ内に表れています。
JDBC SQL ゲートウェイを使用している場合、接続中のリモート・データベースのドキュメントを参照することで、ログの詳細を確認できます。
ログは、トラブルシューティングを実行する必要がある場合のみ有効にします。ログを有効にするとパフォーマンスが大幅に低下するため、通常の操作時は有効にしないでください。
JDBC のログの有効化
Caché に接続するときに JDBC のログを有効にするには、JDBC 接続文字列の末尾にログ・ファイル名を追加します。接続時に、ドライバによってアプリケーションの作業ディレクトリに保存されるログ・ファイルが保存されます。
例えば、元の接続文字列が以下であるとします。
jdbc:Cache://127.0.0.1:1972/USER
ログを有効にするには、この文字列を以下のように変更して再接続します。
jdbc:Cache://127.0.0.1:1972/USER/myjdbc.log
このログには、Caché データベースから見た対話処理が記録されます。
指定されたログ・ファイルが存在する場合、既定では新しいログ・エントリがそのファイルに追加されます。既存のファイルを削除して、新しいファイルを作成するには、接頭語としてログ・ファイル名の前にプラス (+) 文字を付けます。例えば、次の文字列では、myjdbc.log を削除し (既存の場合)、同じ名前で新しいログ・ファイルを作成します。
jdbc:Cache://127.0.0.1:1972/USER/+myjdbc.log
接続パラメータの完全なリストについては、"JDBC 接続 URL の定義" を参照してください。
JDBC SQL ゲートウェイのログの有効化
JDBC と共に使用すれば、Caché SQL ゲートウェイでもログを生成できます。このログを有効にするには、以下の操作を実行します。
-
管理ポータルで、システム管理, 構成, 接続性, JDBCゲートウェイ設定 に進みます。
-
[JDBC ゲートウェイログ] に値を指定します。これは、ログ・ファイルの名前 (jdbcSqlGateway.log など) にする必要があります。このログ・ファイルには、ゲートウェイとデータベースの間の対話処理が記録されます。