PreparedStatement
3.2 PreparedStatement
今までのプログラムでは、Statementを利用しSQL文を実行していました。JDBCには、効率よくSQLを実行するためにStatementを拡張したPreparedStatementが存在します。本節では、PreparedStatementについて学習していきます。
3.2.1 PreparedStatementとは
PreparedStatementは、前節で学習したStatementと同じSQL文をデータベースへ送信するための準備を行う機能の1つです。実行するSQL文を先に解析しておくことで、処理速度の向上を図ります。
StatementとPreparedStatementの違いは以下に示す通りになります。
・ Statement
① SQL文を受け取って解析し、すぐに実行します。
・ PreparedStatement
① SQL文を受け取って解析し、値があればいつでも実行できる状態にします。
② SQL文に必要な値をセットします。
③ SQL文を実行します。
上記の手順を比べると、手順が増えたPreparedStatementの方が、手間がかかって遅くなる印象を受けると思います。PreparedStatementの最大の特徴はSQL文の解析と実行の処理を別々に行う点で、これによって処理速度を向上させることができます。1回のSQLを実行する場合には、処理速度はほとんど変わることはありませんが、検索条件や更新する値のみが異なるSQL文を大量に実行する場合、実行する度にSQL文の解析を行うStatementと、初めの1回しかSQL文の解析を行わないPreparedStatementとでは処理速度に大きな違いが出てきます。
3.2.2 PreparedStatementを利用したデータベースアクセスの基本構文
PreparedStatementを利用した処理の流れは次のようになります。
① JDBC(java.sqlパッケージ)をインポートする。
② JDBCドライバを読み込む。
③ データベースへ接続する。
④ 基準となるSQL文を作成する。
⑤ SQL文をデータベースに送るための準備を行う。
⑥ パラメータ変数に値をセットする。
⑦ SQL文をデータベースへ送信し、結果を受け取る。
PreparedStatementを利用する場合、基準となるSQL文を作成します。SQL文は、動的に変化する値の部分に「?」(はてなマーク)を使用します。この「?」(はてなマーク)のことをパラメータ変数やプレースホルダーと呼びます。また、⑤の処理ではSQLをデータベースに送る準備を行う際に、createStatement()メソッドの代わりにprepareStatement()メソッドを利用します。この時、引数に渡されたSQL文を解析をします。⑥の処理では、SQL文のパラメータ変数に値をセットしています。int型の値をセットする場合には「setInt()メソッド」、String型の文字列をセットする場合には「setString()メソッド」を利用します。⑦の処理で、SQL文をデータベースへ送信し実行します。⑤の時点でSQL文を渡されているので、executeUpdate()メソッドの引数にSQL文を渡す必要はありません。
では、実際にPreparedStatementを利用したプログラムを作成してみましょう。
PreparedStatementを利用したプログラム
このプログラムは、PreparedStatement を利用してデータの更新を行うプログラムです。
➢ SamplePrepared.java① ソース・フォルダー :myjdbc_kanda/src
② パッケージ :jp.co.f1.jdbc.ch03
③ 名前 :SamplePrepared
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.jdbc.ch03; import java.sql.*; public class SamplePrepared { private static String RDB_DRIVE="org.mariadb.jdbc.Driver"; private static String URL="jdbc:mariadb://localhost/mybookdb"; private static String USER="bms"; private static String PASSWD="bms123"; public static void main(String[] args) { String sql = null; int num = 0; Connection con = null; PreparedStatement ps = null; try { Class.forName(RDB_DRIVE); con = DriverManager.getConnection(URL,USER,PASSWD); sql = "UPDATE bookinfo SET price=? WHERE isbn=?"; System.out.println("■更新前の書籍一覧を表示"); selectAll(); ps = con.prepareStatement(sql); ps.setInt(1, 3000); ps.setString(2,"00001"); num = ps.executeUpdate(); System.out.println("\n" + num + "件データを更新しました。\n"); System.out.println("■更新後の書籍一覧を表示"); selectAll(); }catch(Exception e){ System.out.println("JDBCデータベース接続エラー"); }finally{ if(ps != null){ try{ps.close();}catch(SQLException ignore){} } if(con != null){ try{con.close();}catch(SQLException ignore){} } } } private static void selectAll(){ Connection con = null; PreparedStatement ps = null; try{ Class.forName(RDB_DRIVE); con = DriverManager.getConnection(URL,USER,PASSWD); String sql = "SELECT * FROM bookinfo WHERE isbn LIKE ?"; ps = con.prepareStatement(sql); ps.setString(1, "000%"); ResultSet rs = ps.executeQuery(); while (rs.next()) { System.out.println("isbn -> " + rs.getString("isbn") + "\t title -> " + rs.getString("title") + "\t price-> " + rs.getInt("price")); } }catch (Exception e) { System.out.println("JDBCデータベース接続エラー"+e); }finally{ if(ps != null){ try{ps.close();}catch(SQLException ignore){} } if(con != null){ try{con.close();}catch(SQLException ignore){} } } } }
実行結果
解説
このプログラムの大きな構成として、12行目から46行目でデータの更新処理を記述したmain()メソッドを定義しています。更に48行目から76行目ではデータを表示するselectAll()メソッドを定義しています。この2つのメソッドではUPDATE処理とSELECT処理を行なっていますが、どちらもPreparedStatementを利用してSQL文の実行を行なっています。
PreparedStatementを利用するために23行目でパラメータ変数を用いたSQL文を定義しています。「?」(はてなマーク)の部分が動的に変えることのできるパラメータ変数になっています。それ以外の部分は、実行する度に使い回される定型文となります。
28行目でprepareStatement()メソッドの引数にSQL文を渡しています。この時SQL文の解析が行われます。
29行目と30行目でSQL文の「?」(はてなマーク)で記述されていたパラメータ変数に値をセットしています。
各パラメータ変数にメソッドを利用して値をセットするため、パラメータ変数の数だけメソッドを実行します。このプログラムでははてなマークが2つあるため、メソッドを2回実行しています。
なお、値をセットするメソッドには引数が2つあり、第1引数にはパラメータのインデックス、第2引数にはパラメータ変数にセットする値を記述します。そのため、「ps.setInt(1, 3000);」は「1個目のはてなマークに3000をセットする。」という処理になり、「ps.setString(2, “00001”);」は「2個目のはてなマークに”00001″をセットする。」という処理になります。
このSQL文を作成する手順は以下のようになります。
SQL文の中に記述された「?」に対して、先頭から順番に1、2、3・・・と番号が割り当てられます。このプログラムではpriceの値が1番、isbnの値が2番になります。
図 3.1.3初期データとSQL実行後のデータ
次に、セットメソッドの第一引数の値に対応する「?」に第二引数の値をセットします。
図 3.2.2 パラメータ変数への値の設定
その結果、以下のSQL文が生成され、実行されます。
実行結果を確認すると、isbnが00001のデータのpriceが変更されているのが確認できます。
更にselectAll()メソッド内でも同様の処理が行われています。
56行目で基準となるパラメータ変数を用いたSQL文定義しています。
59行目でSQLのパラメータ変数に値をセットしています。
この処理でSQL文のLIKEの後ろのはてなマークに値がセットされるため、次のようなSQL文になります。
ポイント
- PreparedStatementを利用するとSQL文を予め解析しておき、使いまわすことができるため処理速度の向上を図ることができる。
- PreparedStatementを利用する場合のSQL文は値の部分をパラメータ変数と呼ばれる「?」(はてなマーク)で記述する。
- パラメータ変数に値をセットする場合、セットメソッドの引数にパラメータ変数のインデックスと値を記述する。
StatementとPreparedStatementの処理のタイミング以下の図はStatementとPreparedStatementを利用した処理の流れをまとめた図です。
簡単に目を通しておきましょう。