| 目次 | 前の項目 | 次の項目 | JDBCTM ガイド: 使用の開始 |
Statementオブジェクトは、SQL 文をデータベースへ送る場合に使用します。実際には Statement、PreparedStatement、および CallableStatement の 3 種類の Statement オブジェクト (Statement、 PreparedStatement、 CallableStatement) があり、このすべてのオブジェクトが指定の接続で SQL 文の実行のためのコンテナの役目をします。PreparedStatement は Statement から継承し、CallableStatement は
PreparedStatement から継承しています。これらは次のような特定の型の SQL 文の送付専用に使われます。Statement オブジェクトは、パラメータを持たない単純な SQL 文の実行に使用されます。PreparedStatement オブジェクトは、IN パラメータの有無に関係なくプリコンパイルされた SQL 文の実行に使用されます。また、CallableStatement オブジェクトは、データベースに格納されたプロシージャの呼び出しの実行に使用されます。
Statement インタフェースは、文の実行および結果の取り出しのための基本メソッドを提供します。PreparedStatement インタフェースは、IN パラメータの処理のためのメソッドを追加します。CallableStatement は、 OUT パラメータの処理のためのメソッドを追加します。
Statement オブジェクトは、次のコード断片にあるような Connection
メソッド createStatement を用いて作成されます。
Connection con = DriverManager.getConnection(url, "sunny", "");
Statement stmt = con.createStatement();
データベースに送付される SQL 文は、Statement オブジェクトを実行するためのメソッドの 1 つに引数として提供されます。
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");
Statement インタフェースは、SQL 文の実行に executeQuery、executeUpdate、および execute の 3 つの異なるメソッドを提供します。使用するメソッドは、SQL 文が何を作成するかによって決まります。
メソッド executeQuery は、SELECT 文などの単一の結果のセットを作成する文のために設計されています。
メソッド executeUpdate は、INSERT、UPDATE、または DELETE の各文、および CREATE、TABLE、DROP TABLE. のような SQL DDL (データ定義言語) 文を実行するために使用します。INSERT、UPDATE、または
DELETE 文の結果は、テーブルの中の 0 行以上の行中の 1 列以上の列の修正です。 executeUpdate の戻り値は、影響を受ける行の数を示す整数値 (更新カウント) となります。CREATE TABLE または DROP TABLE のような文では、列に対して動作しないため、 executeUpdate の戻り値は常に 0 になります。
メソッド execute は、複数の結果セット、複数の更新カウント、あるいはその 2 つの組み合わせを返す文を実行するのに使用します。このメソッドは高度な機能であり、通常の場合、プログラマが必要とすることはまず考えられないので、この概要ではあとの項で説明します。
文を実行するすべてのメソッドは、開いている呼び出し Statement オブジェクトの現在の結果のセットが開いていればこれを閉じます。これは、Statement オブジェクトを再実行する前に、現在の ResultSet オブジェクトの処理をすべて完了する必要があることを意味します。
Statement インタフェースの中のすべてのメソッドを継承している PreparedStatement インタフェースは、メソッド executeQuery、executeUpdate、および execute の独自のバージョンを持っていることに注意してください。 Statement オブジェクト自体は SQL 文を含んでいません。したがって、SQL 文を引数として Statement.execute メソッドに指定する必要があります。上記のメソッドは、PreparedStatement オブジェクトは、これらのメソッドにはすでにプリコンパイルされた SQL 文を含んでいるため、SQL 文を引数として与えません。CallableStatement は、これらのメソッドの PreparedStatement 形式を継承しています。PreparedStatement または CallableStatement バージョンのこれらのメソッドに対してパラメータとしてクエリーを与えると、これらのメソッドには、SQLException 例外がスローされます。
executeQuery メソッドに対しては 1 つの結果セットを返すので、文は、ResultSet オブジェクトのすべての列の取り出しが終了したときに完了します。executeUpdate メソッドに対しては、文は、その実行が終了したときに完了します。ただし、execute メソッドが呼ばれるようなまれな場合には、それが生成したすべての結果セットまたは更新カウントの取り出しが終了するまでは、文は完了しません。
DBMS の中には、ストアドプロシージャ中の各文を別個の文として取り扱うものもあります。また、全体のプロシージャを 1 つの複合文として取り扱うものもあります。この相違は、commit メソッドが呼ばれるタイミングに影響するため、自動コミットが使用可能になっている場合に重要になります。前者の場合には、各文は、個別にコミットされます。後者の場合には、すべてが一緒にコミットされます。
Statement オブジェクトは、Java ガベージコレクタによって自動的に閉じられます。ただし、Statement オブジェクトが必要なくなった場合には、明示的に閉じるようにプログラミングすることをお勧めします。Statement オブジェクトを閉じるとただちに、DBMS のリソースが解放され、メモリでの問題が起こるのを防止できます。
Statement オブジェクトは SQL エスケープ構文を使用する SQL 文を含むことができます。エスケープ構文は、この中のコードを異なる方法で処理する信号をドライバに送ります。ドライバはエスケープ構文がないかスキャンし、それを特定のデータベースが理解するコードに変換します。このため、エスケープ構文は DBMS から独立しており、プログラマは他では使用できないような機能を使用できるようになります。
{keyword . . . parameters . . . }
キーワードは、下記のように、エスケープ句の種類を示します。
LIKE エスケープ文字のための escape
「%」と「_」の文字は SQL
LIKE句ではワイルドカードのように機能する (「%」は 0 個以上の文字に一致し、「_」は 1 文字だけに一致する)。これらを文字どおりに解釈するには、文字列の特殊なエスケープ文字であるバックスラッシュ (「¥」) が直前になければならない。照会の終りに次の構文を組み込むと、どの文字をエスケープ文字として使用すべきかを指定することができる
{escape 'escape-character'}
たとえば、バックスラッシュ文字をエスケープ文字として使用している次の照会では、アンダーバーで始まる識別名を検索する
stmt.executeQuery("SELECT name FROM Identifiers
WHERE Id LIKE `¥_%' {escape `¥'};
fn
ほとんどすべての DBMS は、スカラー値に数字、文字列、時刻、日付、システム、および変換の関数を持っている。希望の関数の名前およびその引数のあとに続くキーワード
fnを用いて、これらの関数の 1 つをエスケープ構文の中に入れると、その関数を使用することができる。たとえば、次のコードは、連結する 2 つの引数を持つ関数concatを呼び出す
{fn concat("Hot", "Java")};
次の構文で現在のデータベースのユーザ名を取得することができる
{fn user()};
スカラー関数は、多少構文を変えることで異なるデータベースによってもサポートできるが、すべてのドライバがサポートすることはできない。さまざまのDatabaseMetaDataメソッドで、サポートされている関数のリストを取得できる。たとえば、メソッドgetNumericFunctionsは数字関数の名前のカンマで区切ったリストを返し、メソッドgetStringFunctionsは文字列関数を返すドライバは、エスケープされた関数呼び出しを適切な構文にマッピングするか、あるいはその関数をそれ自体に実装する
d、t、および ts
DBMS では、日付、時刻、およびタイムスタンプのリテラルに使用する構文に違いがある。JDBC は、ドライバが DBMS 表現に変換しなければならないエスケープ句を使用して、これらのリテラルの構文の ISO 標準フォーマットをサポートしている
たとえば、日付は次の構文を用いた JDBC SQL 文で指定される
{d 'yyyy-mm-dd'}
この構文では、yyyyは年、mmは月、そしてddは日である。ドライバはエスケープ句を等価の DBMS 固有の表現と置換する。たとえば、ドライバは{d '1999-02-28'}の基礎データベースの適切なフォーマットが'28-FEB-99'であればこれに置換する
TIMEおよびTIMESTAMPには、次のような類似のエスケープ句がある
{t 'hh:mm:ss'}
{ts 'yyyy-mm-dd hh:mm:ss.f . . .'}
TIMESTAMPの小数秒 (.f . . .) 部分は省略することもできる
call または ? = call
データベースがストアドプロシージャをサポートする場合、次のような構文でこれらを JDBC から呼び出すことができる
{call procedure_name[(?, ?, . . .)]}
また、プロシージャが結果のパラメータを返す場合は次のような構文になる
{? = call procedure_name[(?, ?, . . .)]}
角括弧は、これらに囲まれたものがオプションであることを指し示す。これらは構文の一部ではない入力引数はリテラルまたはパラメータのどちらかになる。詳細についてはこの JDBC ガイドの 7、「CallableStatement」の項を参照
メソッド
DatabaseMetaData.supportsStoredProceduresを呼び出すことにより、データベースが格納された プロシージャをサポートするかどうかを調べることができる
oj
外部ジョインの構文は次のとおりである
{oj outer-join}
この場合outer-joinは次の形式である
table LEFT OUTER JOIN {table | outer-join} ON search-condition
外部ジョインは高度機能であり、そして外部ジョインを指定するために記述する SQL 文法を検査することができる。JDBC は、ドライバがサポートする外部ジョインの種類の決定のために、次の 3 つのDatabaseMetaDataメソッド、すなわちsupportsOuterJoins、supportsFullOuterJoins、およびsupportsLimitedOuterJoinsを提供している
メソッドStatement.setEscapeProcessingは、エスケープ処理をオンまたはオフに切り替える。省略時設定ではオンである。パフォーマンスを最高にし、処理時間を短縮するには、プログラマがこれをオフにする場合もあるが、エスケープ処理は、通常オンにされる。setEscapeProcessingはPreparedStatementオブジェクトに対しては稼動しないことに注意すること。この文を呼び出す前にこれがすでにデータベースへ送付されていることがあるためである。プリコンパイルについては、PreparedStatementを参照
execute メソッドは、文が複数の ResultSet オブジェクト、複数の更新カウント、または ResultSet オブジェクトと更新カウントの組み合せを返す可能性がある場合だけに使用してください。この結果が複数になる可能性が、まれにですが、一部のストアドプロシージャを実行するとき、または(コンパイル時にアプリケーションプログラマに知られていない)
未知の SQL 文字列を動的に実行するときに起きることがあります。たとえば、ユーザがストアドプロシージャを実行することがあり (CallableStatement オブジェクトを使用する。この JDBC ガイドの「CallableStatement」を参照) 、ストアドプロシージャが更新、続いて選択、さらに更新、続いて選択と次々に実行することがある場合があります。ストアドプロシージャを使用する場合には、それが何を返すかわからないのが普通です。
メソッド execute は、通常でないケースを処理するので、その結果の取り出しに特殊な処理を必要とする場合があるのが普通です。たとえば、プロシージャが 2 つの結果のセットを返すことがわかっていると仮定してください。メソッド execute を使用してそのプロシージャを実行したあと、メソッド getResultSet を呼び出して最初の結果のセットを取得し、さらに適切な getXXX メソッドでそのセットから値を取り出す必要があります。2 番目の結果のセットを取得するには、getMoreResults の呼び出しに続いて getResultSet を再度呼び出す必要があります。プロシージャが 2 つの更新カウントを返すことがわかっている場合には、メソッド getUpdateCount を最初に呼び出し、そのあとに
getMoreResults を呼び出し、さらに再度 getUpdateCount を呼び出します。
何が返されるかわからないケースはもっと複雑になります。メソッド execute は、結果が ResultSet オブジェクトである場合には true を返し、結果が Java int である場合には false を返します。メソッドが int を返す場合は、結果が更新カウントであるかまたは実行された文が DDL コマンドであったかのどちらかであることを意味します。メソッド execute を呼び出したあと最初にすべきことは、getResultSet
または getUpdateCount のどちらかを呼び出すことです。メソッド getResultSet は、2 個以上の ResultSet オブジェクトの最初のものを取得する場合に呼び出します。メソッド getUpdateCount は、2 個以上の更新カウントの最初の可能性があるものを取得する場合に呼び出します。
SQL 文の結果が結果セットでないときは、メソッド getResultSet は null を返します。これは、結果が更新カウントであるか、または結果がもうないことを意味することになります。この場合に null が実際何を意味するかを調べる唯一の方法は、メソッド getUpdateCount を呼び出すことです。これは整数を返します。この整数は、呼び出し文の影響を受ける行の数か、または結果が結果セットであるか、または結果がないかのどちらであるかを示す
-1 になります。メソッドgetResultSet が、ResultSet オブジェクトが結果でないことを意味する null をすでに返している場合は、-1 の戻り値はもう結果がないことを意味するだけになります。すなわち、以下が真であるときは結果がない (またはもう結果がそれ以上はない) ことを意味します。
((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))メソッド
getResultSet を呼び出し、それが返した ResultSet オブジェクトも処理した場合は、メソッド getMoreResults を呼び出して別の結果セットまたは更新カウントがないか調べるを必要があります。getMoreResults が true を返した場合は、再び getResultSet を呼び出して、実際に次の結果セットを取り出す必要があります。前述したように、getResultSet が null を返した場合は、getUpdateCount を呼び出して、null が、結果が更新カウントであるか、またはもう結果がないことのどちらを意味するかを調べなければなりません。
getMoreResults が false を返した場合には、SQL 文が更新カウントを返したか、またはもう結果がないことを意味します。したがって、メソッド getUpdateCount を呼び出して、どちらの場合であるか調べる必要があります。この状況で、以下が true であるときはもう結果がありません。
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))以下のコードは、
execute メソッドの呼び出しが生成する結果セットと更新結果のすべてにアクセスしたことを確実に知るための方法を示します。
stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // this is an update count
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
continue;
}
if (rowCount == 0) { // DDL command or 0 updates
System.out.println(" No rows changed or statement was DDL
command");
stmt.getMoreResults();
continue;
}
// if we have gotten this far, we have either a result set
// or no more results
ResultSet rs = stmt.getResultSet;
if (rs != null) {
. . . // use metadata to get info about result set columns
while (rs.next()) {
. . . // process results
stmt.getMoreResults();
continue;
}
break; // there are no more results