データの検索
13.3 データの検索
本節では、JDBCを利用してWebアプリケーションからデータを検索する方法について、以下の3つのプログラムを作成しながら学習します。
・ 13.3.1 : 全てのデータを一覧で表示するプログラム
・ 13.3.2 : 絞り込み検索を行いデータを表示するプログラム
・ 13.3.3 : 1件のデータを検索し表示するプログラム
13.3.1と13.3.2で作成するプログラムでは、複数のレコードのデータをデータベースから取得し画面に表示するため、DTOクラスのオブジェクトをArrayListに格納しメソッドの戻り値などに利用しています。
一方13.3.3で作成するプログラムでは、1レコード分のデータのみを扱うため、DTOクラスのオブジェクトをそのままメソッドの戻り値などに利用します。このデータ管理の違いや取得したデータを画面に一覧として表示する処理は重要な部分なので、しっかりと理解してください。
13.3.1 全てのデータを表示する
「全てのデータの検索」は、データ検索を行うプログラムの基本となります。
まずは、全てのデータを検索するプログラムを作成して、WebアプリケーションでのJDBCの利用方法について学習しましょう。
全てのデータを一覧で表示するプログラム
Webアプリケーション上でJDBCを利用して全てのデータを検索し、Webブラウザに一覧として表示します。
このプログラムではデータベースから複数行のデータを取得するため、DTOクラスとArrayListクラスを利用してデータを保持します。
実行結果
アプリケーション構成
➢ AccountDao1.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :AccountDao1
package ch13; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; public class AccountDao1 { //接続用の情報をフィールドに定数として定義 private static String RDB_DRIVE = "org.mariadb.jdbc.Driver"; private static String URL = "jdbc:mariadb://localhost/accountdb"; private static String USER = "root"; private static String PASS = "root123"; //データベース接続を行うメソッド public static Connection getConnection(){ try{ Class.forName(RDB_DRIVE); Connection con = DriverManager.getConnection(URL, USER, PASS); return con; }catch(Exception e){ throw new IllegalStateException(e); } } //データベースから全てのアカウント情報の検索を行うメソッド //戻り値としてArrayList<AccountInfo>型の変数を利用 public ArrayList<AccountInfo> selectAll(){ //変数宣言 Connection con = null; Statement smt = null; //return用オブジェクトの生成 ArrayList<AccountInfo> list = new ArrayList<AccountInfo>(); //SQL文 String sql = "SELECT * FROM account"; try{ con = getConnection(); smt = con.createStatement(); //SQLをDBへ発行 ResultSet rs = smt.executeQuery(sql); //検索結果を配列に格納 while(rs.next()){ AccountInfo accountinfo =new AccountInfo(); accountinfo.setId(rs.getString("id")); accountinfo.setName(rs.getString("name")); accountinfo.setEmail(rs.getString("email")); accountinfo.setAuthority(rs.getString("authority")); list.add(accountinfo); } }catch(Exception e){ throw new IllegalStateException(e); }finally{ //リソースの開放 if(smt != null){ try{smt.close();}catch(SQLException ignore){} } if(con != null){ try{con.close();}catch(SQLException ignore){} } } return list; } }
➢ SelectServlet1.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :SelectServlet1
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :http://localhost:8080/web_basic/SelectServlet1
package ch13; import java.io.IOException; import java.util.ArrayList; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/SelectServlet1") public class SelectServlet1 extends HttpServlet{ public void doGet(HttpServletRequest request ,HttpServletResponse response) throws ServletException ,IOException{ String error = ""; try{ //配列宣言 ArrayList<AccountInfo> list = new ArrayList<AccountInfo>(); //オブジェクト宣言 AccountDao1 objDao = new AccountDao1(); //全検索メソッドを呼び出し list = objDao.selectAll(); //検索結果を持ってlist1.jspにフォワード request.setAttribute("list", list); }catch (IllegalStateException e) { error ="DB接続エラーの為、一覧表示はできませんでした。"; }catch(Exception e){ error ="予期せぬエラーが発生しました。<br>"+e; }finally{ request.setAttribute("error", error); request.getRequestDispatcher("/view/ch13/list1.jsp").forward(request, response); } } }
➢ list1.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch13
② ファイル名 :list1.jsp
③ アクセスURL :SelectServlet1.javaからの画面遷移でアクセスされる
<%@page contentType="text/html; charset=UTF-8"%> <%@page import="java.util.ArrayList,ch13.AccountInfo"%> <% ArrayList<AccountInfo> list = (ArrayList<AccountInfo>)request.getAttribute("list"); String error = (String)request.getAttribute("error"); %> <html> <head> <title>全てのデータを表示する</title> </head> <body> <div style="text-align:center"> <h2 style="text-align:center">全てのデータを表示するプログラム</h2> <hr style="height:3; background-color:#0000FF" /> <br> <%= error %> <table style="margin:0 auto"> <tr> <th style="background-color:#6666FF; width:100">ID</th> <th style="background-color:#6666FF; width:100">名前</th> <th style="background-color:#6666FF; width:250">アドレス</th> <th style="background-color:#6666FF; width:100">権限</th> </tr> <% if(list != null){ for(int i=0;i<list.size();i++){ %> <tr> <td style="text-align:center; width:100"><%= list.get(i).getId() %></td> <td style="text-align:center; width:100"><%= list.get(i).getName() %></td> <td style="text-align:center; width:250"><%= list.get(i).getEmail() %></td> <td style="text-align:center; width:100"><%= list.get(i).getAuthority() %></td> </tr> <% } } %> </table> <br> </div> </body> </html>
解説
このプログラムは、サーブレット内でMariaDBへ接続するためのDAOクラスであるAccountDao1クラスのオブジェクトを生成し、MariaDBからaccountテーブル内の全てのデータを取得します。取得されたデータはArrayListクラスとAccountInfoクラスを利用して管理され、リクエストスコープを利用してJSPで一覧として表示します。
AccountDao1クラスでは、13行目から16行目で接続用の情報をフィールドに定数として定義しています。また、19行目から27行目はデータベースとの接続を行うgetConnection()メソッド、31行目から71行目ではaccountテーブルの全てのデータを検索するselectAll()メソッドが定義されています。このselectAll()の処理については、「Javaデータベース連携(JDBC)」の4.2.4項で詳しく解説していますので、ここでは省略します。
SelectServlet1.javaでは21行目でselectAll()メソッドの戻り値を格納する変数を定義しています。
24行目でDAOクラスをオブジェクト化し、27行目でデータの取得を行なっています。
図 13.3.1: selectAll ()メソッドによるデータ取得の流れ
30行目では取得したデータをリクエストスコープへ登録してします。
list1.jspでは、5行目でリクエストスコープからデータを取得します。
取得されたデータは次のようにArrayListで管理されています。
図 13.3.2: リクエストスコープから取得したArrayList
取得されたデータは28行目から37行目で一覧として表示されます。
list1.jspの28行目のfor文のブロックは37行目までになっており、31行目から34行目のHTMLを繰り返し出力します。
1周目のループではArraylistの0番目の要素、2周目ではArraylistの1番目の要素からAccountInfoクラスのオブジェクトを取得し、オブジェクト内の各データをHTMLタグ内に埋め込んでいます。
図 13.3.3: for文の1周目のHTML内へデータの埋め込み
このループ処理をデータの数だけ繰り返すことで一覧として表示します。
図 13.3.4: ループ処理で生成される画面
このように、Webアプリケーション上からJDBCを利用して取得したデータを、ループ処理によって一覧として表示することが可能です。
また、このプログラムではDAOクラス内でエラーが発生した場合に、catch内の処理で「IllegalStateException」を意図的に発生させて呼び出し元のサーブレットへ例外が発生したことを伝えています。
60行目の「throw」キーワードが、throwの後ろに書かれた例外クラスのオブジェクトを呼び出し元に投げる処理を行なっています。その結果、例外クラスのオブジェクトをサーブレットでcatchし、エラーメッセージを作成、エラー画面に遷移することができます。
DAOクラスのエラー処理内に「throw」が無い場合には、エラーが発生していても、このメソッドの呼び出し元のサーブレットで例外をcatchできません。そのため、サーブレットが処理を継続してしまうことになりますので注意してください。
なお、このプログラムのDAOクラス内では例外として「ClassNotFoundException」や「SQLException」が発生します。どの例外が発生した場合でも、DAOクラスが利用できない不正または不適切な状態でメソッドが呼び出されたものとして、発生した例外は全て「IllegalStateException」をサーブレットへ返すように記述しています。
この「IllegalStateException」をサーブレットへ返す処理は当スクール独自の処理であることに注意してください。「IllegalStateException」が必ず必要なわけではなく、状況によっては別の例外クラスのオブジェクトを返したり、例外クラスのオブジェクトをサーブレットへ返さないという方法も考えられます。
13.3.2 絞り込み検索を行いデータを表示する
一覧画面でデータベースのデータを全て表示してしまうと、必要なデータを見つけるのが困難になります。そのような場合は、範囲検索やあいまい検索などのSQL文を発行し、表示するデータを制限します。
絞り込み検索を行いデータを表示するプログラム
入力フォームからIDに含まれる文字列を入力し、該当するデータをWebブラウザに表示します。
実行結果
アプリケーション構成
➢ AccountDao2.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :AccountDao2
package ch13; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; public class AccountDao2 { //接続用の情報をフィールドに定数として定義 private static String RDB_DRIVE = "org.mariadb.jdbc.Driver"; private static String URL = "jdbc:mariadb://localhost/accountdb"; private static String USER = "root"; private static String PASS = "root123"; //データベース接続を行うメソッド public static Connection getConnection(){ try{ Class.forName(RDB_DRIVE); Connection con = DriverManager.getConnection(URL, USER, PASS); return con; }catch(Exception e){ throw new IllegalStateException(e); } } //データベースから全てのアカウント情報の検索を行うメソッド public ArrayList<AccountInfo> search(String id){ //変数宣言 Connection con = null; Statement smt = null; //return用オブジェクトの生成 ArrayList<AccountInfo> list = new ArrayList<AccountInfo>(); //SQL文 String sql = "SELECT * FROM account WHERE id LIKE '%" + id + "%'"; try{ con = getConnection(); smt = con.createStatement(); //SQLをDBへ発行 ResultSet rs = smt.executeQuery(sql); //検索結果を配列に格納 while(rs.next()){ AccountInfo accountinfo =new AccountInfo(); accountinfo.setId(rs.getString("id")); accountinfo.setName(rs.getString("name")); accountinfo.setEmail(rs.getString("email")); accountinfo.setAuthority(rs.getString("authority")); list.add(accountinfo); } }catch(Exception e){ throw new IllegalStateException(e); }finally{ //リソースの開放 if(smt != null){ try{smt.close();}catch(SQLException ignore){} } if(con != null){ try{con.close();}catch(SQLException ignore){} } } return list; } }
➢ searchForm.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch13
② ファイル名 :searchForm.jsp
③ アクセスURL :http://localhost:8080/web_basic/view/ch13/searchForm.jsp
<%@page contentType="text/html; charset=UTF-8"%> <html> <head> <title>絞込みデータを表示する</title> </head> <body> <div style="text-align:center"> <h2 style="text-align:center">検索ID入力画面</h2> <hr style="height:3; background-color:#0000ff" /> <br> IDに含まれる文字を入力してください。 <form action="<%=request.getContextPath() %>/SelectServlet2"> <input type="text" name="id"> <input type="submit" value="検索"> </form> <br> </div> </body> </html>
➢ SelectServlet2.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :SelectServlet2
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :searchForm.jspからの画面遷移でアクセスされる
package ch13; import java.io.IOException; import java.util.ArrayList; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/SelectServlet2") public class SelectServlet2 extends HttpServlet{ public void doGet(HttpServletRequest request ,HttpServletResponse response) throws ServletException ,IOException{ String error = ""; try{ //パラメータの取得 String id = request.getParameter("id"); //配列宣言 ArrayList<AccountInfo> list = new ArrayList<AccountInfo>(); //オブジェクト宣言 AccountDao2 objDao2 = new AccountDao2(); //全検索メソッドを呼び出し list = objDao2.search(id); //検索結果を持ってlist2.jspにフォワード request.setAttribute("list", list); }catch (IllegalStateException e) { error ="DB接続エラーの為、一覧表示はできませんでした。"; }catch(Exception e){ error ="予期せぬエラーが発生しました。<br>"+e; }finally{ request.setAttribute("error", error); request.getRequestDispatcher("/view/ch13/list2.jsp").forward(request, response); } } }
➢ list2.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch13
② ファイル名 :list2.jsp
③ アクセスURL :SelectServlet2.javaからの画面遷移でアクセスされる
<%@page contentType="text/html; charset=UTF-8"%> <%@page import="java.util.ArrayList,ch13.AccountInfo"%> <% ArrayList<AccountInfo> list = (ArrayList<AccountInfo>)request.getAttribute("list"); String error = (String)request.getAttribute("error"); %> <html> <head> <title>絞込みデータを表示する</title> </head> <body> <div style="text-align:center"> <h2 style="text-align:center">絞込みデータを表示する</h2> <hr style="height:3; background-color:#0000ff" /> <br> <%= error %> <table style="margin:0 auto"> <tr> <th style="background-color:#6666FF; width:100">ID</th> <th style="background-color:#6666FF; width:100">名前</th> <th style="background-color:#6666FF; width:250">アドレス</th> <th style="background-color:#6666FF; width:100">権限</th> </tr> <% if(list != null){ for(int i=0;i<list.size();i++){ %> <tr> <td style="text-align:center; width:100"> <a href="<%=request.getContextPath() %>/SelectServlet3?id=<%=list.get(i).getId()%>"> <%= list.get(i).getId() %></a> </td> <td style="text-align:center; width:100"><%= list.get(i).getName() %></td> <td style="text-align:center; width:250"><%= list.get(i).getEmail() %></td> <td style="text-align:center; width:100"><%= list.get(i).getAuthority() %></td> </tr> <% } } %> </table> <br> </div> </body> </html>
解説
このプログラムは入力フォームから入力された検索文字列を利用し、DAOクラス内であいまい検索を行なっています。
あいまい検索によって取得されたデータは、一覧を表示するプログラムと同じように、DAOクラスからサーブレット、JSPへ渡され画面に一覧表示されます。
このように、一覧機能を持ったプログラムでは、検索機能を実装することでユーザインタフェースの向上を図ることができます。
なお、list2.jspの30行目に記述されたリンクは次の項で利用します。そのため、このプログラムを作成した時点では、リンク切れとなりますが、問題ありません。
13.3.3 1件のデータを検索し表示する
複数のデータを取得する場合、ループ文を利用してDTOクラスのオブジェクトをArrayListへ格納する処理を行うことで、データベースから取得したデータを管理しました。しかし、1件のみ表示したい場合などには、ArrayListへ格納する処理を行うと効率がよくありません。そのような場合は、DTOクラスのオブジェクトのみを利用してデータを管理します。
1件のデータを検索し表示するプログラム
前項13.3.2で作成した画面をクリックすることで、選択されたIDの詳細な情報をWebブラウザに表示します。
このプログラムでは、データベースから取得されるデータは1件です。そのため、DTOクラスのみを利用してデータを保持します。
実行結果
アプリケーション構成
➢ AccountDao3.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :AccountDao3
package ch13; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class AccountDao3 { //接続用の情報をフィールドに定数として定義 private static String RDB_DRIVE = "org.mariadb.jdbc.Driver"; private static String URL = "jdbc:mariadb://localhost/accountdb"; private static String USER = "root"; private static String PASS = "root123"; //データベース接続を行うメソッド public static Connection getConnection(){ try{ Class.forName(RDB_DRIVE); Connection con = DriverManager.getConnection(URL, USER, PASS); return con; }catch(Exception e){ throw new IllegalStateException(e); } } //データベースから指定された1件のアカウント情報の検索を行うメソッド public AccountInfo selectById(String id){ //変数宣言 Connection con = null; Statement smt = null; //return用オブジェクト宣言 AccountInfo accountinfo =new AccountInfo(); //SQL文 String sql = "SELECT * FROM account WHERE id = '" + id + "'"; try{ con = getConnection(); smt = con.createStatement(); //SQLをDBへ発行 ResultSet rs = smt.executeQuery(sql); //取得した結果をDTOオブジェクトに格納 if(rs.next()){ accountinfo.setId(rs.getString("id")); accountinfo.setName(rs.getString("name")); accountinfo.setEmail(rs.getString("email")); accountinfo.setAuthority(rs.getString("authority")); } }catch(Exception e){ throw new IllegalStateException(e); }finally{ //リソースの開放 if(smt != null){ try{smt.close();}catch(SQLException ignore){} } if(con != null){ try{con.close();}catch(SQLException ignore){} } } return accountinfo; } }
➢ SelectServlet3.java① ソース・フォルダ :web_basic/src/main/java
② パッケージ :ch13
③ 名前 :SelectServlet3
④ スーパークラス :jakarta.servlet.http.HttpServlet
⑤ アクセスURL :list2.jspからの画面遷移でアクセスされる
package ch13; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/SelectServlet3") public class SelectServlet3 extends HttpServlet{ public void doGet(HttpServletRequest request ,HttpServletResponse response) throws ServletException ,IOException{ String error = ""; try{ //パラメータの取得 String id = request.getParameter("id"); //DTOオブジェクト宣言 AccountInfo account = new AccountInfo(); //DAOオブジェクト宣言 AccountDao3 objDao3 = new AccountDao3(); //1件検索メソッドを呼び出し account = objDao3.selectById(id); //検索結果を持ってlist3.jspにフォワード request.setAttribute("account", account); }catch (IllegalStateException e) { error ="DB接続エラーの為、一覧表示はできませんでした。"; }catch(Exception e){ error ="予期せぬエラーが発生しました。<br>"+e; }finally{ request.setAttribute("error", error); request.getRequestDispatcher("/view/ch13/list3.jsp").forward(request, response); } } }
➢ list3.jsp① 親フォルダの入力または選択 :web_basic/src/main/webapp/view/ch13
② ファイル名 :list3.jsp
③ アクセスURL :SelectServlet3.javaからの画面遷移でアクセスされる
<%@page contentType="text/html; charset=UTF-8"%> <%@page import="ch13.AccountInfo"%> <% AccountInfo account = (AccountInfo)request.getAttribute("account"); String error = (String)request.getAttribute("error"); %> <html> <head> <title>1件のデータを表示する</title> </head> <body> <div style="text-align:center"> <h2 style="text-align:center">1件のデータを表示するプログラム</h2> <hr style="height:3; background-color:#0000ff" /> <br> <%= error %> <table style="margin:0 auto"> <tr> <th style="background-color:#6666FF; width:100">ID</th> <th style="background-color:#6666FF; width:100">名前</th> <th style="background-color:#6666FF; width:250">アドレス</th> <th style="background-color:#6666FF; width:100">権限</th> </tr> <% if(account != null){ %> <tr> <td style="text-align:center; width:100"><%= account.getId() %></td> <td style="text-align:center; width:100"><%= account.getName() %></td> <td style="text-align:center; width:250"><%= account.getEmail() %></td> <td style="text-align:center; width:100"><%= account.getAuthority() %></td> </tr> <% } %> </table> <br> </div> </body> </html>
解説
このプログラムでは、前項13.3.2で作成した画面のID部分のリンクをクリックすることで、選択されたIDのみの情報を表示する画面に遷移します。このサンプルでは、IDに対する情報量は1件のデータを表示する画面と絞り込みした一覧情報を表示する画面とで変わりはありません。一覧に表示する必要のない細かな情報を表示するための画面と思って頂ければ結構です。
データベースからデータを取得し、画面に表示するという大きなプログラムの流れは前項13.3.1で作成したプログラムと変わりませんが、データを保持する方法が若干異なります。
このプログラムはlist1.jspでクリックされたIDの1件分のデータのみを表示する画面です。MariaDBから取得されるデータは必ず1行分のデータになるため、データベースから取得された値を管理する変数はArrayListクラスを利用せず、AccountDao3.javaの30行目やSelectServlet3.javaの23行目のように、AccountInfoクラスのみで生成されています。
このように、データベースから取得するデータが必ず1件になるような場合には、ArrayListクラスなどを利用せずに、データの管理を行うこともできます。