この連載では、Javaのデータベース・アクセスAPIである「JDBC」の機能を、サンプルコードを交えて解説していきます。また、J2EEにおけるJDBCの位置付けや、JDBCを利用するさまざまなテクノロジについても解説していく予定です。前提知識としては、Javaとリレーショナル・データベースに関するベーシックな知識があれば十分です。
本記事は2001年に執筆されたものです。Java高速化・チューニング全般の最新情報は@IT キーワードINDEXの「Javaパフォーマンス管理」をご参照ください。
第2回「JDBCによるDBへの接続と検索の実行」では、select文の実行を、そして第3回「JDBCによる更新処理の実行」では、DDL文やDML文の実行を取り上げました。第4回の今回は、SQL文をプリコンパイルするプリペアド・ステートメントの利用方法、そして、ストアド・プロシージャの実行方法を紹介しましょう。
プリペアド・ステートメントを利用する
プリペアド・ステートメントとは
ここでは、SQL文をプリコンパイルするために使用するプリペアド・ステートメントについて見ていきましょう。
サンプル・コードは、where句の中のパラメータだけが異なるselect文を、forループで1万回実行し、その実行時間を出力するものです。mainメソッドの引数がsの場合は、通常のステートメント(java.sql.Statement インターフェイス)を使用し、引数がpsの場合は、プリペアド・ステートメント(java.sql.PreparedStatement インターフェイス)を使用します。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
実行結果を比べる
上記のコードの実行結果は、次のようになります。同じような処理を2回実行していますが、2回目のほうが高速なことが分かります。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
なぜこのように、実行速度に差がでたのでしょうか。その内容を理解するため、順を追ってコードを見てみましょう。
通常のステートメント
mainメソッドの引数がsの場合には、第2回で紹介したように、ステートメントを使用してselect文を実行しています。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
ステートメントを使用する場合、createStatement()メソッドでStatementオブジェクトを作成するときには、SQL文を渡しません。executeQuery()メソッド(またはexecuteUpdate()、execute()メソッド)でSQL文を実行するときに、そのメソッドのパラメータとしてSQL文を渡します。
データベースは、executeXXX()メソッドから渡されたSQL文を解析し、そのSQL文を最も効率的に実行するための実行計画(表や索引(index)へのアクセス方法や、結合(join)の順序など) を決定します。このステップを、SQL文の「最適化(optimize)」、あるいは「コンパイル」といいます。そして、SQL文の最適化の後に、決定された実行計画に基づいて、SQL文を実行するわけです。
このサンプル・コードでは、SQL文の最適化と実行が1万回繰り返されていることになります。ですが、ここで実行しているSQL文はwhere句のパラメータが異なるだけであり、その実行計画は同一であることは明らかですね。にもかかわらず、SQL文の最適化を毎回行うのは効率が悪いと思うでしょう。この問題を解決するのが、プリペアド・ステートメントなのです。
プリペアド・ステートメント
では、プリペアド・ステートメントは、どのように利用するのでしょう? mainメソッドの引数がpsの場合には、プリペアド・ステートメントを使用して同一の処理を実行しています。
プリペアド・ステートメントを使用する場合、createStatement()メソッドではなくprepareStatement()メソッドを使用します。ここで注目してほしいのは、prepareStatement()メソッドのパラメータでSQL文を渡している点です。これにより、prepareStatement()メソッドが呼び出された時点で、あらかじめSQL文のコンパイルを行うのです。SQL文自体の実行前であるため、このことを指して「プリコンパイルする」、あるいは「準備する(prepare)」といいます。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
prepareStatement()メソッドに渡すSQL文では、パラメータのプレース・ホルダとして“?”を用います。当然ながら、実際にSQL文を実行する前に、すべてのパラメータをセットする必要があります。パラメータのセットには、PreparedStatementオブジェクトのsetXXX()メソッドを用います。setXXX()の第1パラメータは(1から始まる)パラメータの位置番号、第2パラメータはパラメータの値となります。第1回で紹介したように、Javaデータ型とJDBCデータ型の間のマッピングを考慮して、適切なsetXXX()を用いましょう。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
すべてのパラメータをセットしたら、executeXXX()メソッドで、実際にSQL文を実行します。サンプル・コードでは、select文を実行するので、executeQuery()メソッドを用いています。このとき、SQL文を渡す必要はありません。すでにプリコンパイルされたSQL文を単に実行するだけであるため、ステートメントの場合より効率的にSQL文を実行することが可能です。
*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***
筆者の環境では、プリペアド・ステートメントを利用することによって、ステートメントの場合に比べて実行時間が約46%短縮されました。環境によって数値に違いはあるでしょうが、この傾向は変わらないはずです。プリペアド・ステートメントを利用できる場合は、極力利用するようにしましょう。
Copyright © ITmedia, Inc. All Rights Reserved.