Apache + Tomcat + PostgreSQL(JSP 編)

[サーバの実験室 Slackware]

作成 : 2004/01/03
修正 : 2004/01/05

"サーバの実験室"の検索


Apache + Tomcat + PostgreSQL

Apache(WEB サーバ)と Tomcat(サーブレットコンテナ)と PostgreSQL(データベースサーバ)を使用して、所有している本のデータベースから題名や著者で検索ができるようにしてみる。 完成したときのイメージは、http://www.nina.jp/collection/ をみてね。

検索フォーム(/var/webapps/collection/dbaccess.html)

キーワードを入力させて dbaccess.jsp に渡す、検索フォーム dbaccess.html を作成する。 「送信」ボタンをクリックすると、題名(title)を dbaccess.jsp に渡す。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html">
<title>検索フォーム</title>
</head>
<body>
<form action="dbaccess.jsp" action="post">
<p>
題名<input type="text" name="title">
かつ著者<input type="text" name="author">を含む<br>
<input type="submit" value="送信">
<input type="reset" value="クリア">
</p>
</form>
</body>
</html>

これをブラウザで見ると下のようになる。

検索フォーム

データベースへの接続と結果の表示(/var/webapps/collection/dbaccess.jsp)

データベースに接続して、検索フォームから渡された題名(title)を元に検索した結果を表示する dbaccess.jsp を作成する。 今回は、データベースとのインターフェースもすべて JSP でやってしまう。

<%@ page contentType="text/html; charset=EUC-JP" %>
<%@ page import="java.sql.*, java.util.regex.*" %>
<%!
    private String nullCheck(String str) {
        return str == null ? "" : str;
    }
%>
<%
    request.setCharacterEncoding("EUC-JP");

    String title = request.getParameter("title");
    StringBuffer sb = new StringBuffer();

    if(title == null) {
        title = "";
    }
    title = title.replaceAll("[\'%_]","");

    String sql = "select * from mybook"
        + " where title like '%" + title + "%'";

    Class.forName("org.postgresql.Driver");
    Connection db = DriverManager.getConnection("jdbc:postgresql:collection","guest","guest");
    Statement st = db.createStatement();
    ResultSet rs = st.executeQuery(sql);

    sb.append("<table>\n");
    sb.append("<tr><td>題名</td><td>著者</td><td>訳者</td><td>出版社</td><td>購入日</td></tr>");

    while(rs.next()) {
        sb.append("<tr>");
        sb.append("<td>" + nullCheck(rs.getString("title")) + "</td>");
        sb.append("<td>" + nullCheck(rs.getString("author")) + "</td>");
        sb.append("<td>" + nullCheck(rs.getString("translator")) + "</td>");
        sb.append("<td>" + nullCheck(rs.getString("publisher")) + "</td>");
        sb.append("<td>" + nullCheck(rs.getString("purchase")) + "</td>");
        sb.append("</tr>\n");
    }
    rs.close();
    st.close();
    db.close();

    sb.append("</table>\n");
%>
<!DOCTYPE PUBLIC HTML "-//W3C//DTD HTML 4.01//EN">
<html lang="ja">
<head>
<meta http-equiv="content-type" content="text/html">
<title>検索結果</title>
</head>
<body>
<%= sb%>
</body>
</html>

作成したら、Tomcat と Apache を再起動しておく。 検索結果が下のように表示されれば大成功。

検索結果

dbaccess.jsp についての覚え書き

Java の API については、Sun の JavaTM 2 Platform, Standard Edition, 1.4.0 API 仕様 を参照。 JSP の基本的な構成は下のようになっている。

<%@
    ディレクティブ
%>
<%!
    宣言部
%>
<%
    スクリプトレット    
%>
<%@ page contentType="text/html; charset=EUC-JP" %>

contentType で、ページの MIME タイプが text/html であり、日本語 EUC-JP で記述されていることを示す。

<%@ page import="java.sql.*, java.util.regex.*" %>

import で利用するクラス(java.sql.* と java.util.regex.*)をロードする。

<%!
    private String nullCheck(String str) {
        return str == null ? "" : str;
    }
%>

nullCheck メソッドを定義している。 文字列を引数に取り、文字列が null(未定義)なら長さ 0 の文字列を返し、null でなければ文字列をそのまま返す。

    request.setCharacterEncoding("EUC-JP");

JSP が受け取るデータが EUC-JP エンコードであることを指定する。 これを指定しないと、ISO-8859 と認識してしまうので日本語は文字化けする。

注意! Tomcat-5 では GET メソッドで送信されたデータ(たとえば http://xxxx/xxx.jsp?data1=value1&data2=value2 とか)はデコードしてくれない。 これはバグではなく、そういう仕様なんだそうな。 Tomcat-5 を使用しており、GET メソッドで日本語のデータを渡したい場合は、次のようにして自分でデコードしなければならない。

// Tomcat-4 では GET/POST とも setCharacterEncoding() で OK
// Tomcat-5 では POST メソッドはデコードされる
request.setCharacterEncoding("EUC-JP");
String data = request.getParameters("data");

// Tomcat-5 では GET メソッドだけ自分でデコード
if(request.getMethod().equals("GET")) {
    data = new String(data.getBytes("8859_1"),"EUC_JP");
}
    String title = request.getParameter("title");

request.getParameter() でフォームから渡されたデータを読み込んで、String クラスのオブジェクト title を作成する。

    StringBuffer sb = new StringBuffer();

StringBuffer クラスのオブジェクト sb を作成する。 データを sb にどんどん追加していくので、String クラスでなく StringBuffer クラスとした。 StringBuffer クラスは String クラスと違って、文字列を変更(append メソッドとか insert メソッドとか)することができる。

    if(title == null) {
        title = "";
    }

フォームから title データが渡されなかった場合(たとえば、JSP ファイルを直接リクエストした場合とか)は、title は null になってしまう。 null のままだと後の処理でエラー(NullPointerException)になってしまうので、title を長さ 0 の文字列で作成しなおしている。

    title = title.replaceAll("[\'%_]","");

'(シングルクォート)は SQL 文の文字列定数を囲むときに使用される。 また、SQL 文の LIKE キーワードで % は任意の 0 文字以上の文字、_ は任意の 1 文字をあらわすときに使用する特殊文字。 これらが検索文字列に含まれていると予期しない動作をする可能性があるため、replaceAll で長さ 0 の文字列に置き換えている。 replaceAll の第 1 引数で指定した正規表現にマッチした文字列が、第 2 引数の文字列で置き換えられる。

    String sql = "select * from mybook"
        + " where title like '%" + title + "%'";

SQL 文を格納する sql を作成する。 WHERE 句 で LIKE キーワードを使ってフォームから渡された文字列を含むレコードを検索する。 % は任意の 0 文字以上の文字をあらわす。

    Class.forName("org.postgresql.Driver");

PostgreSQL の JDBC ドライバをロードする。 CLASSPATH に JDBC ドライバが見つからないとエラーになる。 postgresql.jar が CLASSPATH にあるか確認しておくこと (Apache + Tomcat + PostgreSQL(PostgreSQL 編)の最後を参照)

    Connection db = DriverManager.getConnection("jdbc:postgresql:collection","guest","guest");

URL "jdbc:postgresql:collection" データベースへ、ユーザ "guest"・パスワード "guest" で接続する。 URL は、"jdbc:postgresql://host/database"(リモートホスト)または "jdbc:postgresql:database"(ローカルホスト)のように指定する。

    Statement st = db.createStatement();

SQL 文をデータベースに送るための Statement クラスオブジェクト st を作成する。

    ResultSet rs = st.executeQuery(sql);

SQL 文をデータベースに送り、実行結果を ResultSet クラスオブジェクト rs で受け取る。

    sb.append("<table>\n");

文字列バッファの最後に指定した文字列を追加する。

    while(rs.next()) {
        ....
    }

ResultSet クラスの next メソッドを最初にコールすると、SQL を実行した結果の最初の行がカレント行になる。 カレント行の列の値を取得するには、getString メソッドや getInt メソッドを使用する。 その後、next メソッドをコールするたびに次の行がカレント行になる。 次の行がない場合は、false を返す。

        sb.append("<td>" + nullCheck(rs.getString("title")) + "</td>");

文字列バッファの最後に指定した文字列を追加するのだが、単純に列の値を追加するのではなく、nullCheck メソッドをコールした戻り値を追加している。 列の値が null の場合、"null" という文字が追加されるのを防ぐため。

<%= sb%>

sb の内容を表示する。 out.print(sb); と同じ。 <%= 〜 %> の中に書く式の最後には ;(セミコロン)をつけない。


[サーバの実験室 Slackware]