質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

JSP

JSP(Java Server Pages)とは、ウェブアプリケーションの表示レイヤーに使われるサーバーサイドの技術のことです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

9036閲覧

【Java】フォームの検索項目の入力有無によってSQL文を生成してデータベースから情報取得したい

RogerFederer

総合スコア19

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

JDBC

JDBC(Java DataBase Connectivity)は、Javaとリーレーショナルデータベースに接続させる基本的なAPIです。Java上でSQLステートメントを発行することで、データベースの種類に影響を受ないDB操作を可能とします。

JSP

JSP(Java Server Pages)とは、ウェブアプリケーションの表示レイヤーに使われるサーバーサイドの技術のことです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

3クリップ

投稿2018/08/21 20:31

編集2018/08/21 23:14

前提・実現したいこと

jspのフォーム画面の検索項目3つ(氏名、誕生月、職種)に入力された値をサーブレットで取得し、SQL文のwhere句以降に連結してmysqlデータベースのテーブル(usertable)から検索結果を取得したいです。

条件:
① JDBCのPreparedStatementを利用する
② AND検索をする
③ 検索項目が空欄の場合は、その項目は絞り込み対象外とする。
④ 全て空欄の場合、全レコードを取得する

上記条件③のせいで、

「SQL文の生成方法」
「SQL文の"?"に割り当てるパラメータインデックス値の決め方」

について分からなくて悩んでいます。

該当のソースコードの一部

JAVA

1     Connection con = null; 2 PreparedStatement st = null; 3 try{ 4 con = 省略; 5 6 String sql = "SELECT * FROM usertable"; 7 boolean flag = false; 8    //名前が入力されていればwhere句で連結、されなければスキップ 9 if (!user.getName().equals("")) { 10 sql += " WHERE name like ?"; 11 flag = true; 12 } 13    //誕生月が入力されていれば処理を実施 14 if (user.getBirthday()!=null) { 15     //名前が入力されていなかった場合はWHERE、されていたらAND 16 if(!flag){ 17 sql += " WHERE birthday like ?"; //名前が空欄の時はこの?が1番目 18 flag = true; 19 }else{ 20 sql += " AND birthday like ?"; //名前が入力されればこの?は2番目 21 } 22 } 23    //職業が入力されていれば処理を実施 24 if (user.getType()!=0) { 25     //名前も誕生月も空欄だった場合はwhere、どちらかもしくは両方入力されていたらAND 26 if(!flag){ 27 sql += " WHERE type like ?"; 28 }else{ 29 sql += " AND type like ?"; 30 } 31 } 32 st = con.prepareStatement(sql); 33 st.setString(1, "%"+user.getName()+"%"); 34 st.setString(2, "%"+ new SimpleDateFormat("yyyy").format(user.getBirthday())+"%"); 35 st.setInt(3, user.getType());

分からないこと

① 入力項目の有無によってSQL文を連結していく方法はこれで良いのでしょうか? ifがネストになっている点がとても拙く感じるのですが・・・。

② ★で示す、?に対応するパラメータインデックス値はどのように決めればよいでしょうか。
具体的に悩む点は、名前だけ空欄の場合、名前も誕生月も空欄の場合、誕生月だけ空欄の場合などどこが空欄のまま検索されるかで?の個数も変わるので、例えば名前だけ空欄にするとインデックス範囲は2までだと怒られます。

java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).

?の個数が選択次第で変わる状態でどうパラメータインデックス値を当て込めばいいかがわかりません。

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2018/08/21 22:37

birthdayの検索条件をフォーマットでyyyy固定にされてますけど、年しか入力されないのですか?
m.ts10806

2018/08/21 22:38

あとdate型はどうだったか失念しましたが、int型はlikeで検索できなかったような・・。
RogerFederer

2018/08/21 22:58

本質問を確認いただきありがとうございます。「年」しか入力しません。 調べて確認してみたらint型はlikeで検索できないのですね・・・。そこの前でエラーになっていたため気づけませんでした。
m.ts10806

2018/08/22 00:08

いずれにしても%使わないならlikeではなく=で良いと思います。
guest

回答3

0

動的SQL(ダイナミックSQL)を作成したいんですよね。ORマッパーを使用した方がよい気がしますが。

1, boolean flag = false;を削除して以下のような形にすると、全部andで結合できるかと。

Java

1StringBuilder sql = new StringBuilder("SELECT * FROM usertable WHERE 1=1 "); 2if (!user.getName().equals("")) { 3 sql.append(" AND name like ?"); 4}

2, bindの部分はSet parameters dynamically to prepared Statement in JDBCを参考にしてくださいな。


以下は質問文のコードをみて気づいた点です。

1, Stringで文字列連結(+=)するのは避けて、StringBuilderを使ってくださいな。
参考:[Java] Stringの結合について
2,SELECT文にORDER BY句が存在しないため、SELECTの結果順序は保証されていません。
ORDER BY句を付けるようにしてくださいな。
3, Connectionオブジェクトはtry~with~Resources文を使ってclose忘れを防いでくださいな。

投稿2018/08/22 02:11

umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

かなり冗長に感じます。項目が増えたらその分、コードもかなり追記しなければなりません。
WHEREをつけるのは「1つ以上検索条件が入力されたとき」ですよね?
検索条件を配列に追記していって、その配列の要素個数によってWHEREつけたり、prepareStatementの個数を決めては?
確かjoinもあったはずなので条件の結合も容易かなと。

さっと書けるのがPHPなので、流れの参考程度にしかならないと思いますが。

php

1 2$where = []; //検索条件の配列 3$stValues = []; //プリペアドステートメントにセットするValue 4if(!empty($username)){ 5 $where[] = " name like ? "; 6 $valueinfo["value"] = $username; 7 $valueinfo["type"] = "string"; 8 $stValues[] = $valueinfo; 9} 10if(!empty($birthday)){ 11 $where[] = " birthday like ? "; 12 $valueinfo["value"] = $birthday; 13 $valueinfo["type"] = "yeardate"; 14 $stValues[] = $valueinfo; 15} 16 17//// 省略 18 19$sql = "SELECT * FROM usertable"; 20if(count($where) > 0){ //検索条件があれば配列をandで結合 21 $sql .= " where ".implode(" and ",$where); 22} 23 24 25$stmt = $dbh->prepare($sql); 26for($i=0;$i<count($stValues);$i++){ //パラメータをそれぞれの型で判定してバインド 27 $paramset = $stValues[$i]; 28 $setType = PDO::PARAM_STR; //カラムの型 29 switch($paramset["type"]){ 30 case "string": 31 $setvalue = "%".$paramset["value"]."%"; 32 break; 33 case "yeardate": 34 $setvalue = $paramset["value"]."%"; 35 break; 36 case "integer": 37 $setvalue = $paramset["value"]; 38 $setType = PDO::PARAM_INT; 39 break; 40 } 41 $stmt->bindParam(($i+1), $setvalue,$setType); 42} 43 44$stmt->execute();

Javaとかはセットする型とかそのあたりで呼び出すメソッドがかわるのでそこは読み替えていただくとしておおよそこのような流れかなと。
型やイレギュラーな検索が増えれば追記していく形にはなりますが。

userがBeanっぽいので、そこにカラムの型まで持っているのでしたらそちらを引っ張ってくればそこでSetTypeが判定できますね。

追記。

コードコピペして思ったんですが、コメント対象じゃないところに全角空白がたくさんありますね。
Connectionの前とか//の前とか。
これではそもそも問題とは別の箇所で動かないコードを提示していることになります。

投稿2018/08/22 00:16

編集2018/08/22 02:06
m.ts10806

総合スコア80850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

PreparedStatementと項目が動的に変化するSQLを記述するときに、かつて以下のようなやり方をするのがよく使われていました。(今どきはO/Rマッピングフレームワークがあるので、こういった記述は廃れましたが…。)

java

1String sql = "SELECT * FROM usertable WHERE 1=1 "; 2 3List<String> parameters = new ArrayList<>(); 4 5if (!user.getName().equals("")) { 6 sql += "AND name like ? "; 7 parameters.add("name"); 8} 9 10if (user.getBirthday()!=null) { 11 sql += "AND birthday like ? "; 12 parameters.add("birthday"); 13} 14 15...(中略)... 16 17PreparedStatement statement = connection.preparedStatement(sql); 18int columnIndex = 0; 19if (parameters.contains("name")) { 20 statement.setString(++columnIndex, "%"+user.getName()+"%"); 21} 22if (parameters.contains("birthday")) { 23 statement.setString(++columnIndex, "%"+ new SimpleDateFormat("yyyy").format(user.getBirthday())+"%"); 24} 25 26...(中略)... 27 28ResultSet resultSet = statement.executeQuery();

parametersに格納するときは定数にするか、列挙型使っておくのが安全かもしれませんが、元コードに準拠しました。
Java8以降なら、SQLの組み立て部分でもう少し簡潔にかけるかと思います。

投稿2018/08/22 02:42

A-pZ

総合スコア12011

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問