169
第
第
1
1
2
2
章
章
.
.
デ
デ
ー
ー
タ
タ
ベ
ベ
ー
ー
ス
ス
を
を
利
利
用
用
し
し
た
た
We
W
eb
b
ア
ア
プ
プ
リ
リ
ケ
ケ
ー
ー
シ
シ
ョ
ョ
ン
ン
【学習のねらい】
① Web アプリケーション上でデータベースを利用する方法を学習する。<先週の復習>
講義で示された【基礎課題12-1】に解答して下さい。12-1.データベース上のデータを表示する
Web アプリケーション
11-2 節では、テーブル「account」のデータを表示する Java アプリケーションを学習し ました。本節では、同様の処理をサーブレットから行う方法を学習します。Web アプリケ ーションの特徴に起因する違いはありますが、基本的な部分はJava アプリケーションの場 合と変わりません。 作成するのは、所定の URL に接続すると、次のように「account」のデータを表示する Web アプリケーションです。以下の手順にしたがって作成してください。 ① 「DBServlet.java」を第 11 章で作成したパッケージ「dbsample」内に下のように新 規作成してください。プログラムの内容は次ページの通りです。170 package dbsample; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class DBServlet extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=Windows-31J"); PrintWriter out= response.getWriter();
String sql="select * from account"; Connection con=null; Statement smt=null; try { con=DBManager.getConnection(); smt=con.createStatement(); ResultSet rs=smt.executeQuery(sql); while(rs.next()) { out.println("ID="+rs.getInt("ID") +", 氏名="+rs.getString("Name") +", 貯蓄額="+rs.getInt("Money") ); } } catch(SQLException e) {
throw new ServletException(e); } finally {
if(smt != null) {
try{smt.close();} catch(SQLException ignore) {} }
if(con != null) {
try{con.close();} catch(SQLException ignore) {} }
} } }
171 【解説】 基本的には 11-2 節で学習した「DBOperate.java」と同じです。違いは、データ ベースと接続するために用意した Connection(コネクション)オブジェクトを、ク ライアントからのリクエスト毎に確実にクローズ(解放)している点です。その理由 を簡単に説明しましょう。 Java アプリケーションの場合は、データベースとの接続は(複数のデータベースを 同時利用するなどの特殊な用途を除いて)基本的に一つですが、Web アプリケーショ ンの場合、サーバに接続しているクライアントの数だけコネクション(接続)が発生 します。そのため、一つのクライアントがコネクションを維持してしまうと、サーバ が管理するコネクションの数が増大し、すぐに処理できなくなってしまいます。そこ で、クライアントからのリクエストが終了すると即座にコネクション(およびステー トメント)をクローズして(次の接続のために)解放する、という処理が必須になる のです。その解放処理を確実に行うため、try~catch~finally 文を用いています。 一般に try~catch~finally 文は次の形で記述します。 ここに、例外(エラー)とはデータベース接続時のエラーや接続後に SQL 文の記述ミ スなどで処理が中断してしまう場合を指します。そのような場合でも、コネクション (データベースへの接続)が実現していれば、finally 文の中で確実にクローズ(解 放)するようにします。最初は複雑に見えるかも知れませんが、これは Web アプリケ ーションでデータベースに接続する場合の決まり文句と思ってください。 ② サーブレットを作成したので、web.xml 文で当該サーブ レットを登録する必要があります。「WEB-INF」フォル ダ内に web.xml を新規作成(「新規」→「ファイル」と 選択)し、次ページのように記述してください。 try { 例外(エラー)が発生する可能性のある処理 } catch (例外クラス型 引数名) { 例外が発生した場合の処理 } finally { 最後に必ず実行する処理 }
172 作成したら、p.169 のように結果が表示されることを確認してください。 ※ Tomcat の起動を忘れないでください。 ※ また、p.153~154 で説明した通り、MySQL が Windows サービスとして起動してい るか確認し、もし停止状態だったら開始させてください。
【基礎課題 12-2】
この Web アプリケーションの URL は何ですか?空欄部分を記述して提出してください。http://localhost:8080/
<補足> web.xml をきちんと記述しても 404 エラーが出る場合Web アプリケーション「DBWeb」が Tomcat に認識されていない可能性があります。そ こで、パッケージエクスプローラ上で「DBweb」を右ボタンクリックし、「Tomcat プロジ ェクト」→「コンテキスト定義を更新」を選択してください。 web.xml になければ、その後所定の URL に接続するときちんと表示されるはずです。 <web-app> <servlet> <servlet-name>DBServlet</servlet-name> <servlet-class>dbsample.DBServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DBServlet</servlet-name> <url-pattern>/DBServlet</url-pattern> </servlet-mapping> </web-app> <web.xml>
173
12-2.サーブレットと JSP の連携
前節では、テキスト形式でテーブルの内容(データ)を表 示しましたが、これを右のように HTML の表の形式で表示す るように改善しましょう。 これを、サーブレットの中で HTML 形式の文書を記述して も良いのですが、「第7章.サーブレット間の連携」で学習 した方法を用いて、出力を JSP ファイルで行うようにしま しょう。次の手順にしたがって、作成してください。 ① 「DBServlet.java」を次ページのように修正して「DBServlet2.java」と別名保 管してください。下線部が修正部分です(DBServlet.java の結果表示部分は削除さ れています)。リクエスト属性の forward 文による受け渡しについては、第7章で学 習しています。ここで新たに出てきた内容は※部分のみです。これについては、下の 【解説】を参照してください。 ② 次に、右のように「dbsample」というフォルダを作成しそ の中に「display.jsp」を作成し、p.175 のように記述し ます。これにより、結果が HTML 形式で表示されます。 ③ web.xml に DBServlet2.java に関する部分を、p.175 のように追加します。 【解説】「DBServlet2.java」の※部分について 要点を簡潔にまとめます。 ResultSet オ ブ ジ ェ ク ト を リ ク エ ス ト 属 性 に 保 管 し forward 文で JSP ファイルに転送する、ということが p.174 の「DBServlet2.java」のポイントです。 しかし、p.171 の解説で説明した通り、コネクションおよびステートメントオブジェ クトは、リクエストの終了と共に、つまり JSP ファイルへ転送する前にクローズしな ければなりません。 ResultSet オブジェクトは、「コネクション+ステートメント」オブジェクトがクロ ーズされると、自動的にクローズされてしまいます。つまり破棄されてしまいます。 そこで、ResultSet オブジェクトの中身を、“別の入れ物”に移し替える必要が生じ ます。その“入れ物”が、「CachedRowSetImpl」クラスのオブジェクトです。 プログラムでは、「CachedRowSetImpl」クラスのオブジェクト「crs」を用いて、 「crs.populate(rs);」としています。これにより、ResulSet オブジェクト「rs」 の内容が CachedRowSetImpl オブジェクト「crs」の中にコピーされます。 この「crs」は、コネクションをクローズしても残りますから、forward 文により JSP ファイルに転送することができます。174 package dbsample; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sun.rowset.CachedRowSetImpl;
public class DBServlet2 extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=Windows-31J"); PrintWriter out= response.getWriter();
String sql="select * from account"; Connection con=null; Statement smt=null; try { con=DBManager.getConnection(); smt=con.createStatement(); ResultSet rs=smt.executeQuery(sql);
CachedRowSetImpl crs=new CachedRowSetImpl(); crs.populate(rs);
request.setAttribute("crs",crs); } catch(SQLException e) {
throw new ServletException(e); } finally {
if(smt != null) {
try{smt.close();} catch(SQLException ignore) {} }
if(con != null) {
try{con.close();} catch(SQLException ignore) {} } } RequestDispatcher dispatcher =request.getRequestDispatcher("/dbsample/display.jsp"); dispatcher.forward(request,response); } }
※
<DBServlet2.java>175
<%@page contentType="text/html; charset=Windows-31J"%> <%@page import="java.sql.ResultSet" %> <HTML> <BODY> <TABLE BORDER="1"> <TR> <TH>ID<TH>氏名<TH>貯蓄金額 <%
ResultSet rs=(ResultSet) request.getAttribute("crs"); while(rs.next()) { %> <TR> <TD><%=rs.getInt("ID") %> <TD><%=rs.getString("Name") %> <TD><%=rs.getInt("Money") %> <% } %> </TABLE> </BODY> </HTML> <web-app> <servlet> <servlet-name>DBServlet</servlet-name> <servlet-class>dbsample.DBServlet</servlet-class> </servlet> <servlet> <servlet-name>DBServlet2</servlet-name> <servlet-class>dbsample.DBServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>DBServlet</servlet-name> <url-pattern>/DBServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>DBServlet2</servlet-name> <url-pattern>/DBServlet2</url-pattern> </servlet-mapping> </web-app> <display.jsp> <web.xml> リ ク エ ス ト 属 性 に 保 管 し た ( CachedRowSetImpl ) オ ブ ジ ェ ク ト を ResultSet オブジェクトして受け取る。 追加部分
176
【基礎課題 12-3】
p.173 のように結果が表示されることを確認したら、「ResultSet オブジェクトを CachedRowSetImpl オブジェクトに詰め替えることにより、サーブレットから JSP へ ResulSet オブジェクトの中身を転送することができました。」と記述して提出してくださ い。12-3.データベースへのデータの追加、検索および更新
前節までのプログラムを利用すれば、データベースの検索や更新等の操作は容易に行え ます。以下、順にデータの追加、検索、更新を行ってみましょう。【基礎課題
12-4】 データの追加
以下のように、新たに口座を開設するページを作成しましょう。 次の手順にしたがって作成してください。 ① 「DBServlet2.java」を修正して次ページの様に記述し、「DBServletOpen.java」と別 名保管してください。下線部が修正部分です。また、氏名と金額は、JSP ファイルか ら受け取ることにしています。 ② 氏名と金額の入力画面を用意し、それらを上のサーブレット「DBServletOpen.java」 に受け渡すJSP ファイル「open.jsp」を p.178 のように記述してください。作成する 場所はフォルダ「dbsample」の中です。p.178 の図を確認してください。 所定のURL に接続し、氏名と振り込み金額を入力して[送信]ボタンをクリックすると、 新たに口座が開設(追加)され、全 データが表示される。 追加されたデータ177 package dbsample;
import java.io.IOException; ・・・以下 import 文変更なし
public class DBServletOpen extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=Windows-31J"); request.setCharacterEncoding("Windows-31J");
PrintWriter out= response.getWriter(); int ID=0;
String Shimei=request.getParameter("Shimei"); int Kingaku=
Integer.parseInt(request.getParameter("Kingaku")); String sql="select * from account";
Connection con=null; Statement smt=null; try { con=DBManager.getConnection(); smt=con.createStatement(); ResultSet rs=smt.executeQuery(sql); while(rs.next()) { ID=rs.getInt("ID"); } ID=ID+1;
String sql2="insert into account values("+ID +",'"+Shimei+"',"+Kingaku+")"; smt.executeUpdate(sql2);
rs=smt.executeQuery(sql);
CachedRowSetImpl crs=new CachedRowSetImpl(); crs.populate(rs);
request.setAttribute("crs",crs); } catch(SQLException e) {
throw new ServletException(e); } finally { ・・・ } RequestDispatcher dispatcher =request.getRequestDispatcher("/dbsample/display.jsp"); dispatcher.forward(request,response); } } <DBServletOpen.java>
データ挿入のため、「insert into account values(ID の値,氏名,金額)」という SQL 文を発行
178 open.jsp の作成場所は次の通りです。現時点でパッケー ジエクスプローラを見ると、右のようになっているはず です。 ③ 最後に、web.xml を下のように記述し、新たに作成した サーブレットを登録します。 作成したら、JSP「open.jsp」(下のURL)に接続して p.176 のように動作を確認してください。 http://localhost:8080/DBWeb/dbsample/open.jsp 新しい口座を追加できたら、「新たに口座をデータベースに 追加し、表示する事ができました。」記述して提出してくだ さい。 <web-app> ・・・ <servlet> <servlet-name>DBServletOpen</servlet-name> <servlet-class>dbsample.DBServletOpen</servlet-class> </servlet> ・・・ <servlet-mapping> <servlet-name>DBServletOpen</servlet-name> <url-pattern>/DBServletOpen</url-pattern> </servlet-mapping> </web-app>
<%@page contentType="text/html; charset=Windows-31J"%> <HTML>
<BODY>
<H2>口座開設</H2>
氏名と金額を入力して下さい。<BR> <FORM ACTION="../DBServletOpen">
氏名<INPUT TYPE="TEXT" NAME="Shimei"> 金額<INPUT TYPE="TEXT" NAME="Kingaku"> <INPUT TYPE="SUBMIT" VALUE="送信"> </FORM> </BODY> </HTML> <web.xml> <open.jsp> ※追加部分のみ記述しています。
179
【基礎課題
12-5】 データの検索
今度は、指定したID(口座番号)の氏名と貯蓄金額を表示するページを作成しましょう。 以下の手順にしたがって作成してください。 ① 「DBServletOpen.java」を次ページのように修正して、「DBServletSelect.java」と別 名保管してください。下線部が修正箇所です。 ② 次に、ID の入力画面を用意しその値をサーブレットに受け渡す JSP ファイル 「select.jsp」を p.181 のように記述してください。作成場所は、open.jsp と同じくフ ォルダ「dbsample」の中です。 ③ 最後に web.xml に p.181 のように新たに作成したサーブレット「DBServletSelect.java」 登録部分を追加します。 作成したら、指定したID の口座が表示されるかどうかを確認してください。確認できた ら「指定した ID の氏名と金額を表示させることを確認しました。」と記述して提出してく ださい。 ID を入力して[送信]ボタンをクリック すると・・・ 指定したID の氏名と貯蓄金額を表示 する。180 package dbsample;
import java.io.IOException; ・・・import 文は変更なし
public class DBServletSelect extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=Windows-31J"); request.setCharacterEncoding("Windows-31J");
PrintWriter out= response.getWriter();
int ID=Integer.parseInt(request.getParameter("ID")); String sql="select * from account";
Connection con=null; Statement smt=null; try { con=DBManager.getConnection(); smt=con.createStatement(); ResultSet rs;
sql="select * from account where ID="+ID; rs=smt.executeQuery(sql);
CachedRowSetImpl crs=new CachedRowSetImpl(); crs.populate(rs);
request.setAttribute("crs",crs); } catch(SQLException e) {
throw new ServletException(e); } finally { ・・・変更なし } RequestDispatcher dispatcher =request.getRequestDispatcher("/dbsample/display.jsp"); dispatcher.forward(request,response); } } <DBServletSelect.java>
181
<%@page contentType="text/html; charset=Windows-31J"%> <HTML>
<BODY>
<H2>口座確認</H2>
ID(口座番号)を入力して下さい。<BR> <FORM ACTION="../DBServletSelect">
ID<INPUT TYPE="TEXT" NAME="ID"> <INPUT TYPE="SUBMIT" VALUE="送信"> </FORM> </BODY> </HTML> <web-app> ・・・ <servlet> <servlet-name>DBServletSelect</servlet-name> <servlet-class>dbsample.DBServletSelect</servlet-class> </servlet> ・・・ <servlet-mapping> <servlet-name>DBServletSelect</servlet-name> <url-pattern>/DBServletSelect</url-pattern> </servlet-mapping> </web-app> <select.jsp> <web.xml> ※追加部分のみ記述しています。
182
【基礎課題
12-6】 データの更新
最後に、次のように口座への入金あるいは引き出しを行えるページを作成しましょう。 次の手順にしたがって、このページを作成してください。 ① DBServletSelect.java を次ページの様に修正し、DBServletUpdate.java と別名保管し てください。下線部と点線枠部分が修正箇所です。 ② select.jsp を p.184 のように修正し、update.jsp と別名保管してください。 ③ 最 後 に web.xml に p.184 の よ う に 新 た に 作 成 し た サ ー ブ レ ッ ト 「DBServletUpdate.java」登録部分を追加します。 作成したら、上の様に動作を確認してください。確認できたら、「指定した口座(ID)に 入金(あるいは引き出し)を行えることを確認しました。」と記述して提出してください。 次の画面で、例えば入金と選択しID と金額を入力して[送信]ボタンをクリックす ると・・・ 指定した口座に所定 の料金が入金されて 表示される。ネットワークプログラミング論 平成27 年 12 月 21 日
183 package dbsample;
import java.io.IOException; ・・・
public class DBServletUpdate extends HttpServlet { public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain;charset=Windows-31J"); request.setCharacterEncoding("Windows-31J");
PrintWriter out= response.getWriter(); String InOut=request.getParameter("InOut");
int ID=Integer.parseInt(request.getParameter("ID")); int Kingaku=
Integer.parseInt(request.getParameter("Kingaku")); String sql="select * from account where ID="+ID;
Connection con=null; Statement smt=null; try { con=DBManager.getConnection(); smt=con.createStatement(); ResultSet rs=smt.executeQuery(sql); rs.next(); if(InOut.equals("Nyukin")) { Kingaku=rs.getInt("Money")+Kingaku; } else { Kingaku=rs.getInt("Money")-Kingaku; } String sql2=
"update account set Money="+Kingaku+" where ID="+ID; smt.executeUpdate(sql2);
sql="select * from account where ID="+ID; ・・・
} catch(SQLException e) {
throw new ServletException(e); } finally { ・・・ } RequestDispatcher dispatcher =・・・(以下変更なし) } }
184
<%@page contentType="text/html; charset=Windows-31J"%> <HTML> <BODY> <H2>入金・引き出し</H2> 入金あるいは引き出しのいずれかを選択し、ID(口座番号)と金額を入力して下さい。 <BR> <FORM ACTION="../DBServletUpdate">
<INPUT TYPE="RADIO" NAME="InOut" VALUE="Nyukin">入金
<INPUT TYPE="RADIO" NAME="InOut" VALUE="HikiDashi"> 引 き 出 し <BR>
ID<INPUT TYPE="TEXT" NAME="ID">
金額<INPUT TYPE="TEXT" NAME="Kingaku"> <INPUT TYPE="SUBMIT" VALUE="送信"> </FORM> </BODY> </HTML> <web-app> ・・・ <servlet> <servlet-name>DBServletUpdate</servlet-name> <servlet-class>dbsample.DBServletUpdate</servlet-class> </servlet> ・・・ <servlet-mapping> <servlet-name>DBServletUpdate</servlet-name> <url-pattern>/DBServletUpdate</url-pattern> </servlet-mapping> </web-app> <update.jsp> <web.xml> ※追加部分のみ記述しています。