Webサイトに入力機能を追加する (5)

前回作成した入力情報の確認画面で登録ボタンを押した後の処理部分を作っていきます。

まず、MatchDAOクラスに試合情報を追加するメソッドを準備します。

public void store(Match match)
  throws InternalErrorException
{

  Connection conn = null;

  PreparedStatement pstmt = null;

  try {

    Class.forName(JDBC_DRIVER);

    conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

    pstmt = conn.prepareStatement(

      "INSERT INTO matches "

      + "(section, date, home, away, goals_for, goals_against) "

      + "VALUES "

      + "(?, ?, ?, ?, ?, ?)"

    );

    pstmt.setInt(1, match.getSection());

    pstmt.setDate(2, Date.valueOf(match.getDate()));

    pstmt.setString(3, match.getHome());

    pstmt.setString(4, match.getAway());

    pstmt.setInt(5, match.getGoalsFor());

    pstmt.setInt(6, match.getGoalsAgainst());

    pstmt.executeUpdate();

  } catch (Exception ex) {

    throw new InternalErrorException("試合情報のデータベースへの登録でエラーが発生しました", ex);

  } finally {

    try {

      if (pstmt != null) pstmt.close();

      if (conn != null) conn.close();

    } catch (SQLException ex) {

    }

  }

}

データベースへの新規レコードの登録はSQLのINSERT文で実行できます。

SQL文の2番目のパラメータは日付型ですが、データベースアクセスに使用するjdbcは日付型としてjava.sql.Date型を使用し、javaの標準ライブラリではjava.time.LocalDate型が使われています。

このため、Date.valueOfメソッドによる変換が入っています。

Matchクラスにもこれに対応したメソッドを用意します。

public void store()
  throws InternalErrorException
{

  MatchDAO match_dao = new MatchDAO();

  match_dao.store(this);

}

このメソッドはMatchDAOクラスを外に見せたくないという理由だけで作ったものでそれ以上の意味はありません。

public void doPost(HttpServletRequest req, HttpServletResponse resp)
{

  HttpSession session = null;

  Match new_match = null;

  try {

    resp.setContentType("text/html; charset=UTF-8");

    session = req.getSession(false);

    if (session == null || (new_match = (Match)session.getAttribute("match")) == null) {

      // 登録試合情報が取得できないエラー

      if (session == null)

        session = req.getSession(true);

      session.setAttribute("message", "登録する試合情報を受け取れませんでした");

      resp.sendRedirect("internal_error.jsp");

    } else {

      // 試合をデータベースに登録する

      new_match.store();

      resp.sendRedirect("register_match.jsp");

    }

  } catch (Exception ex) {

    if (session == null)

      session = req.getSession(true);

    session.setAttribute("message", ex.getMessage());

    resp.sendRedirect("internal_error.jsp");

  }

}

register_match.jspとinternal_error.jspはセッションから試合情報、あるいはメッセージを取り出してHTMLとして出力するだけですので、中身は省略します。

これで基本的には動くのですが、一つ問題があります。

ブラウザの戻るボタンで直前のページに戻り、再度登録ボタンをクリックすると試合情報を重複して登録できてしまいます。

直前のページに対応したservletとこのページに対応したservletはともにリダイレクトでJSPページに移動しています。

このような場合、試合情報登録完了のJSPページでブラウザの戻るボタンをクリックすると直前に表示されていたJSPページが表示され、JSPページには重複検査のコードは含まれていない (場合によってはブラウザに保存されているHTMLが表示されるだけ) ので、確認簿tンをクリックするとセッション情報に記録されていた試合情報が有効な情報として再度登録処理されてしまいます。

これを避けるには、上で示した登録完了表示用のサーブレットでセッション情報の付け替えを行うことが有効です。

public void doPost(HttpServletRequest req, HttpServletResponse resp)
{
  HttpSession session = null;

  Match new_match = null;

  try {

    resp.setContentType("text/html; charset=UTF-8");

    session = req.getSession(false);

    if (session == null || (new_match = (Match)session.getAttribute("match")) == null) {

      // 登録試合情報が取得できないエラー

      if (session == null)

        session = req.getSession(true);

      session.setAttribute("message", "登録する試合情報を受け取れませんでした");

      resp.sendRedirect("internal_error.jsp");

    } else {

      // 試合をデータベースに登録する

      new_match.store();

      session.removeAttribute("match");

      session.setAttribute("stored_match", new_match);

      resp.sendRedirect("register_match.jsp");

    }

  } catch (Exception ex) {

    if (session == null)

      session = req.getSession(true);

    session.setAttribute("message", ex.getMessage());

    resp.sendRedirect("internal_error.jsp");

  }

}

removeAttributeメソッドをセッションに適用することでmatchアトリビュートがセッションから削除されるので、このservletが2回目に起動された場合にはmatchアトリビュートがないというエラーになります。

そしてstored_matchアトリビュートに試合情報を設定しなおすことでregister_match.jspはこのセッションアトリビュートから試合情報を取り出して表示することができます。

ただ、このservletが2回目呼び出されるとエラーになるのは気持ち悪いので、少しだけ改良しておきます。

public void doPost(HttpServletRequest req, HttpServletResponse resp)
{
  HttpSession session = null;

  Match new_match = null;

  try {

    resp.setContentType("text/html; charset=UTF-8");

    session = req.getSession(false);

    if (session == null || (new_match = (Match)session.getAttribute("match")) == null) {

      if (session != null && (new_match = (Match)session.getAttribute("stored_match")) != null) {

        // 2回目の呼び出し

        resp.sendRedirect("register_match.jsp");

      } else {

        // 登録試合情報が取得できないエラー

        if (session == null)

          session = req.getSession(true);

        session.setAttribute("message", "登録する試合情報を受け取れませんでした");

        resp.sendRedirect("internal_error.jsp");

      }

   } else {

      // 試合をデータベースに登録する

      new_match.store();

      session.removeAttribute("match");

      session.setAttribute("stored_match", new_match);

      resp.sendRedirect("register_match.jsp");

    }

  } catch (Exception ex) {

    if (session == null)

      session = req.getSession(true);

    session.setAttribute("message", ex.getMessage());

    resp.sendRedirect("internal_error.jsp");

  }

}

上で示したようにstored_matchアトリビュートが有効になっていて2回目の呼び出しであることを検出した場合はデータベースへの登録を行わずにしれっと登録完了画面を表示する、あるいは登録済み画面を作成して表示することで無駄なエラーを避けることが可能です。

以上で試合情報の表示と追加がひとわたり作れました。

ということで次回からは少し違うことをやりたいと思います。


0コメント

  • 1000 / 1000