RM-BLOG

IT系技術職のおっさんがIT技術とかライブとか日常とか雑多に語るブログです。* 本ブログに書かれている内容は個人の意見・感想であり、特定の組織に属するものではありません。/All opinions are my own.*

【Java】OraclePrepaedStatementを使ったバインド変数設定方法について

バインド変数を用いたSQLJavaから動的に値をセットする場合、
Oracleが用意している「OraclePreparedStatement」というのを使うことになる。
(どうもJava標準で用意されてるライブラリやクラス群にはバインド変数に公式に対応しているPreparedStatementは存在しないようだ)


 

 
普通のPreparedStatementを使う分には↓のような実装例になるが

import java.sql.*;
…(中略)…
String sql = "SELECT TEST_NO,TEST_NAME,TEST_DATE,TEST_FLG FROM TEST_TABLE WHERE TEST_NO >= 10";
Connection con = getConnectoion(); // DB接続(省略)
PreparedStatement ps = con.preparedStatement(sql);



これをOraclePreparedStatementにしてSELECTの条件を動的にする場合は

import java.sql.*;
import oracle.jdbc.OraclePreparedStatement;// ←①
…(中略)…
String sql = "SELECT TEST_NO,TEST_NAME,TEST_DATE,TEST_FLG FROM TEST_TABLE WHERE TEST_NO >= :test_no"; // ←②
Connection con = getConnectoion(); // DB接続(省略)
OraclePreparedStatement ps = (OraclePreparedStatement)con.preparedStatement(sql); // ←③
ps.setStringAtName("test_no" , "10"); // ←④


とする。

赤太字にした部分がポイントとなるわけだが
①「oracle.jdbc.OraclePreparedStatement」をインポートする。
 ちなみに同名のクラスが「oracle.jdbc.internal.OraclePreparedStatement」にもいるが(少なくともojdbc5にはいた)、
 こっちはpreparedStatementメソッドが隠蔽されてるらしくて外から呼び出せず、↑の実装はコンパイルエラーになる。
SQL文字列で条件句にあたる部分をバインド変数で記述する。
 この場合は「:test_no」にあたる部分がバインド変数。
③Connection#preparedStatement(String)の結果を「OraclePreparedStatement」にキャストする。
 こんだけでいいらしい。
④OraclePreparedStatement#setStringAtNameメソッドでバインド変数部分に値をセットする。
 第一引数がバインド変数名・第二引数がセットする値。
 重要なのは、バインド変数名指定の際に、SQL内でバインド変数化した部分の「:」(半角コロン)まで含めない事。
 (sqlplusでバインド変数指定して実行するケースと同じなのでよく考えれば当然なのだが)
 このケースでは、SQL内のバインド変数は「:test_no」だが、
 setStringAtNameで値をセットするときの第一引数は「test_no」で、先頭の半角コロンはない。

②のようなSQLを外だし(別ファイルにする等)しておいて、
条件にあたる部分を引数とかでもらい、④でセットするようなつくりにしておけば、
バインド変数を使って動的にSQLを実行する簡易的な仕組みが出来上がる。




ただ正直こんな程度なら別に「OraclePreparedStatement」なんてイカしたもの使う必要もない。
SQLはただの文字列に過ぎないので、SQL内の特定の文字列を適切な値に置換すれば事足りる。

import java.sql.*;
…(中略)…
Connection con = getConnectoion(); // DB接続(省略)
String sql = "SELECT TEST_NO,TEST_NAME,TEST_DATE,TEST_FLG FROM TEST_TABLE WHERE TEST_NO >= :test_no"; // ←②
sql = sql.replace(":test_no" , "10");
PreparedStatement ps = con.preparedStatement(sql);


とかでいいわけだ。

SQL内のバインド変数は、「バインド変数のまま実行計画をたてる(/たてない)」が
Oracleの起動パラメータによって制御されてるようであり、
下手にバインド変数を使うと大量に検索ヒットするケースとごく少量のケースでも
同じ実行計画で動いてしまって無茶苦茶レスポンス悪い…
とかいうケースもあるらしく、
個人的にはできればあまり使いたくない実装である。
動的に条件やセットする値を変えたい、という程度なら無理せず「文字列置換」で出来ることこなすほうが良いと思っている。