はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

Java MVCフレームワークでショッピング風サイトを作る(View編)

Java MVCフレームワークでショッピング風サイトを作る(View編)

Java MVCフレームワークでショッピング風サイトを作って見たのでメモ。今回はViewについてのメモ。

環境

仕様・構成

  1. 「ログイン画面(index.jsp)」からあらかじめDBに保存しているユーザIDをパスワードを使ってログインする。

  2. 「商品一覧画面(itemList.jsp)」へ遷移するので、 商品一覧の中から購入数(あらかじめ決めた在庫数まで)を選択して、購入ボタンを選択。

  3. すると、 「購入確認画面(confirm_result.jsp)」へ遷移し、 購入ボタンをまた選択する。

  4. 商品が購入され、「購入結果画面(result.jsp)」へ遷移し、

  5. 一覧へ戻るを選択して、「商品一覧画面(itemList.jsp)」へ戻ってくる(在庫数は再計算されている)

また、購入履歴を選択した場合は、 過去に何を買ったかを「購入履歴画面(history.jsp)」へ遷移する。

その他の構成

  • 商品一覧の「数量」は在庫数と同期して表示。 例えば在庫が5 であれば、 購入画面では5 までしか選べないようになっている。 今回は、手入力だとチェック処理が必要になるので、プルダウン(selectタグ)を使う。

  • 商品一覧の「購入」ボタンは、在庫がなくなると非表示になる。

  • 買い物カゴ機能はなし。

  • エラー画面は、今回はログインエラーのみ

  • エラー画面は、今回はログインエラーのみ

画面(JSP)ファイルの確認

今回使うjspは、7種類。

ファイル名画面名役割
loin.jsp ログイン画面 ショッピング風サイトのトップページ
loinFailed.jsp ログインエラー画面 ログイン処理失敗時のページ
itemList.jsp 商品一覧画面 買い物対象商品の表示ページ
purchase_confirm.jsp 購入確認画面 購入前の確認ページ
result.jsp 購入結果画面 購入結果の表示ページ
history.jsp 購入履歴画面 ユーザの購入履歴照会ぺージ
header.jsp ヘッダー画面 共通ページ

状態(画面)遷移図

状態遷移図で表すと以下のようになる。

画面(View)の状態遷移図ログイン画面index.jspログイン前商品一覧画面itemList.jspログイン画面index.jspログイン後ログインエラー画面loginFailed.jsp購入履歴画面history.jsp購入確認画面purchase_confirm.jsp購入結果画面result.jspユーザID, パスワードを入力ログアウトログアウトログイン戻るログイン失敗ログイン失敗購入履歴数量と購入を選択一覧に戻るログアウト購入履歴購入するログアウト購入履歴一覧に戻る

DBの準備

使うデータベースは4種類。

テーブル名(論理名)テーブル名(物理名)説明
ユーザ user ユーザIDとパスワードなどの管理
商品 item 商品ID, 商品名などの管理
在庫 stock 商品在庫数の管理
購入履歴 history ユーザ毎の購入履歴の管理

データベース・テーブルの作成

  • データベースの作成。

    
    create database shopping_sample character set utf8;
    
  • ユーザ(user)テーブルの作成: ユーザID、パスワード、名前と年齢のカラムを持つ。

    
    create table shopping_sample.user(id varchar(10) not null primary key, pass varchar(10), name varchar(20), age int) character set utf8;
    
  • 商品(item)テーブルの作成: 商品ID、商品名と値段のカラムを持つ。

    
    create table shopping_sample.item(item_id char(5) not null primary key, item_name varchar(50), price int not null) character set utf8;
    
  • 在庫(stock) テーブルの作成:商品IDと在庫数のカラムを持つ。

    
    create table shopping_sample.stock(item_id char(5) not null primary key, quantity int) character set utf8;
    
  • 購入履歴(history)テーブルの作成:注文ID(購入した時に割り当てられるID), ユーザID、商品ID、購入数のカラムを持つ。

    
    create table shopping_sample.history(order_id int(4) zerofill auto_increment primary key not null, id varchar(10) not null, item_id char(5) not null, quantity int not null) character set utf8;
    

テストデータの追加

  • ユーザ(user)テーブルへの追加例

    
    insert into shopping_sample.user(id, name, pass, age) values ("web01", "Taro", "password", 20);
    
    
    insert into shopping_sample.user(id, name, pass, age) values ("web02", "Jiro", "pass", 17);
    
    
    insert into shopping_sample.user(id, name, pass, age) values ("web03", "Saburo", "password", 19);
    
    
    select * from shopping_sample.user;
    +-------+----------+--------+------+
    | id    | pass     | name   | age  |
    +-------+----------+--------+------+
    | web01 | password | Taro   |   20 |
    | web02 | pass     | Jiro   |   17 |
    | web03 | password | Saburo |   19 |
    +-------+----------+--------+------+
    
  • 商品(item)テーブルへの追加例

    
      insert into shopping_sample.item(item_id, item_name, price) values ("s0001", "鋼のSword", 120);
    
    
      insert into shopping_sample.item(item_id, item_name, price) values ("s0002", "ボロのメイル", 80);
    
    
    insert into shopping_sample.item(item_id, item_name, price) values ("s0003", "ボロのシューズ", 40);
    
    
    select * from shopping_sample.item;
    +---------+-----------------------+-------+
    | item_id | item_name             | price |
    +---------+-----------------------+-------+
    | s0001   | 鋼のSword             |   120 |
    | s0002   | ボロのメイル          |    80 |
    | s0003   | ボロのシューズ        |    40 |
    +---------+-----------------------+-------+
    
  • 在庫(stock)テーブルの追加例。

    
    insert into shopping_sample.stock(item_id,  quantity) values ("s0001",  10);
    
    
    insert into shopping_sample.stock(item_id,  quantity) values ("s0002",  10);
    
    
    insert into shopping_sample.stock(item_id,  quantity) values ("s0003",  10);
    
    
    select * from shopping_sample.stock;
    +---------+----------+
    | item_id | quantity |
    +---------+----------+
    | s0001   |       10 |
    | s0002   |       10 |
    | s0003   |       10 |
    +---------+----------+
    
  • 購入履歴(history)テーブルは、 購入したときに自動的に追加される。

View(jspファイル)の作成

ログイン画面(index.jsp)

form(POST) でユーザIDとパスワードをLoginServlet へ 送る。

LoginServlet でログイン処理を行い、 ログインが成功すれば、ShoppingServlet へforwardする。

ログイン画面


<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<jsp:useBean id="login_user_bean" scope="session" class="login.LoginUserBean" />

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>ショッピング風サイト ログイン画面</title>
        <link href="css/shopping.css" rel="stylesheet" type="text/css" />
    </head>

    <body>
        <main class="login_pane">
            <h1>ようこそショッピング風サイトへ!</h1>
            <p>ログインIDとパスワードを入力して下さい</p>

            <form action="./LoginServlet" method="post">
                <table class="table_form">
                    <tbody>
                        <tr>
                            <%-- ログイン済みの場合はIDを表示 --%>
                            <th>ログインID</th>
                            <td><input type="text" name="id" value="<%=login_user_bean.getId()%>"/></td>
                        </tr>
                        <tr>
                            <th>パスワード</th>
                            <td><input type="password" name="pass"/></td>
                        </tr>
                    </tbody>
                </table>

                <div class="buttons">
                    <input class="common_button" type="submit" name="submit"  value="ログイン"/>

                    <%-- ログイン済みの場合はログアウトボタンを表示 --%>
                    <% if ("login".equals(session.getAttribute("login_state"))) { %>
                    <input class="common_button" type="submit" name="submit" value="ログアウト"/>
                    <% }%>
                </div>
            </form>
        </main>
    </body>
</html>

ログインエラー画面(loginFailed.jsp)

LoginServlet の中のログイン処理に失敗した場合、 loginFailed.jsp へforward される。

ログインエラー画面


<%@ page language="java" contentType="text/html; charset=UTF-8" %>

<%-- ログインエラー画面 --%>

<!DOCTYPE html>
<html>
    <head>
        <title>ショッピング風サイト エラー画面</title>
        <link href="css/shopping.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <h1>ログインエラー</h1>
        <p>入力されたユーザは存在しません...</p>
        <form>
            <a class="common_button" href="./LoginServlet?submit=logout">戻る</a>
        </form>
    </body>
</html>

ヘッダー画面(header.jsp)

ログイン画面(index.jsp)以外に追加している。 ログイン名の表示、購入履歴(history.jsp)へのリンク、ログアウトのリンクがある。

ログアウトは、LoginServlet へのGETパラメータ(?submit=logout) で判断させている。

購入履歴も同様に、ShoppingServlet へのGETパラメータ (?submit=history) で判断させている。

ヘッダー画面


<%@ page language="java" contentType="text/html charset=UTF-8" %>
<jsp:useBean id="login_user_bean" scope="session" class="login.LoginUserBean"/>

<link href="css/shopping.css" rel="stylesheet" type="text/css" />

<%-- header --%>
<header>
    <%-- ログイン済みの場合はID を表示 --%>
    <p>
        ようこそ「<jsp:getProperty name="login_user_bean" property="name" />」さん!
        <%-- Getのクエリで購入履歴かログアウトか判断させる --%>
        <a href="ShoppingServlet?submit=history">購入履歴</a> 
        <a href="LoginServlet?submit=logout">ログアウト</a>
    </p>
</header>

商品一覧画面(itemList.jsp)

ログインが成功した場合、ShoppingServlet側で商品(item)テーブルにアクセスし、全商品のデータをrequest にsetAttribute し、このitemList.jsp へforward する。

itemList.jsp は、request に付加された商品情報をgetAttribute し、 表(tableタグ) で表示する。

各商品の購入ボタンを選択すると、 商品ID と購入数を BuyItemServlet へform でPOSTする。

共通ページの読み込みは、jsp:include タグを使えばいい。


<jsp:include page="header.jsp"></jsp:include>

商品一覧画面


<%@ page import="java.util.ArrayList"%>
<%@ page import="shopping.ItemBean"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" %>

<%-- 商品一覧画面 --%>
<!DOCTYPE html>
<html>
<head>
<title>ショッピング風サイト 商品一覧画面</title>
<link href="css/shopping.css" rel="stylesheet" type="text/css" />
</head>
<body>
<%-- TODO:2-1 jsp:includeでヘッダー画面を読み込む --%>
<jsp:include page="header.jsp"></jsp:include>

    <main>
        <h1>商品一覧</h1>
    <%-- リクエストスコープからBeanクラスの配列を取得 --%>
    <% ArrayList<ItemBean> itemList = (ArrayList<ItemBean>) request.getAttribute("itemList"); %>

    <form action="./BuyItemServlet">
        <table class="shopping_table">
            <tbody>
                <tr>
                    <th>商品ID</th>
                    <th>商品名</th>
                    <th>価格</th>
                    <th>在庫数</th>
                    <th>数量</th>
                </tr>

                <%-- Beanの要素数分(商品の種類分)テーブルを作成 --%>
                <% for (ItemBean bean : itemList) {%>
                <tr>
                    <%-- 商品ID --%>
                    <td><%= bean.getItemId()%></td>
                    <%-- 商品名 --%>
                    <td><%= bean.getItemName()%></td>
                    <%-- 価格 --%>
                    <td class="int"><%= bean.getPrice()%></td>
                    <%-- 数量(在庫) --%>
                    <td class="int"><%= bean.getQuantity()%></td>

                    <%-- TODO:2-2 在庫が0の場合はリストボックスと購入ボタンを表示しない処理を入れる --%>
                    <% if (bean.getQuantity() != 0) {%>
                    <td>
                        <select class="list" name="<%= bean.getItemId()%>list">
                            <% for (int i = 0; i <= bean.getQuantity(); i++) {%>
                            <option value="<%= i%>"><%= i%></option>
                            <% }%>
                        </select>
                    </td>
                    <td class="button">
                        <input class="common_button" type="submit" value="購入" name="<%= bean.getItemId()%>">
                    </td>
                    <% } else { %>
                    <td class="button">売り切れ!</td>
                    <% } %>
                </tr>
                <% }%>
            </tbody>
        </table>
        <a class="common_button" href="./">戻る</a>
    </form>
</main>
</body>
</html>

購入確認画面(purchase_confirm.jsp)

商品ID と購入数の情報を持っているBuyItemServlet から、 商品情報と購入数をrequest にsetAttribure してpurchase_confirm.jsp へforward される。

商品ID と購入数を<input type="hidden"> で、ResultServlet へform(POST)で送信する。

購入確認画面


<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<jsp:useBean id="item_bean" scope="request" class="shopping.ItemBean"/>

<%-- 購入確認画面 --%>
<!DOCTYPE html>
<html>
<head>
<title>ショッピング風サイト 購入確認画面</title>
<link href="css/shopping.css" rel="stylesheet" type="text/css" />
</head>
<body>
<jsp:include page="header.jsp"/>

<main>
    <h1>購入確認</h1>
    <p>次の商品を購入しますか?</p>

    <form action="./ResultServlet" method="post">
        <table class="shopping_table">
            <tbody>
                <tr>
                    <th>商品ID</th>
                    <th>商品名</th>
                    <th>価格</th>
                    <th>在庫数</th>
                    <th>購入数</th>
                </tr>
                <tr>
                    <%-- リクエストスコープから表示する値を取得 --%>
                    <td><jsp:getProperty name="item_bean" property="itemId" /></td>
                    <td><jsp:getProperty name="item_bean" property="itemName" /></td>
                    <td class="int"><jsp:getProperty name="item_bean" property="price" /></td>
                    <td class="int"><jsp:getProperty name="item_bean" property="quantity" /></td>
                    <td class="int"><%=request.getAttribute("purchased_num")%></td>
                    <td class="button">
                        <input class="common_button" type="submit" value="購入する">
                        <% //TODO:2-5 hiddenでパラメータをセットしておく %>
                        <%-- 購入処理を行うため、hidden(画面には表示されない情報)に商品IDと購入数を設定しておく --%>
                        <input type="hidden" name="item_id" value="<jsp:getProperty property="itemId" name="item_bean"/>">
                        <input type="hidden" name="item_quantity" value="<%= request.getAttribute("purchased_num")%>">
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
    <form action="./ShoppingServlet" method="post">
        <input class="common_button" type="submit" value="一覧に戻る">
    </form>
</main>
</body>
</html>

購入結果画面(result.jsp)

ResultServlet の中で、 購入数から在庫数を再計算して在庫(stock)テーブルをupdate する。 result.jsp へforward する。

購入結果画面


  <%@ page language="java" contentType="text/html; charset=UTF-8" %>

<%-- 購入結果画面 --%>
<!DOCTYPE html>
<html>
<head>
  <title>ショッピング風サイト 購入結果</title>
  <link href="css/shopping.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <jsp:include page="header.jsp"/>
  <h1>購入結果</h1>
  <p>購入しました。<img src="img/Thankyou.jpg" width="100" height="100"/></p>
  <form action="./ShoppingServlet" method="post">
      <input class="common_button" type="submit" value="一覧に戻る">
  </form>
</body>
</html>

購入履歴画面(history.jsp)

header.jsp にある購入履歴リンクを選択すると、 ShoppingServlet?submit=history のGETパラメータで処理される。

ShoppingServlet では、以下のように処理され、history.jsp へ遷移される。

  1. session に付加されているユーザ情報(モデルLoginUserBean) から ユーザIDを取得.

  2. ユーザIDを使って、DB の購入履歴(history)テーブルにアクセスし、

  3. そのユーザの購入履歴情報を取得。

  4. request に購入履歴情報をsetAttribute してhistory.jsp へforwardする。

購入履歴画面


<%@ page import="java.util.ArrayList"%>
<%@ page import="shopping.HistoryBean"%>

<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<jsp:useBean id="login_user_bean" scope="session" class="login.LoginUserBean"/>

<%-- 購入履歴画面 --%>
<!DOCTYPE html>
<html>
    <head>
        <title>ショッピング風サイト 購入履歴</title>
        <link href="css/shopping.css" rel="stylesheet" type="text/css" />
    </head>

    <body>
        <jsp:include page="header.jsp"/>

        <h1><jsp:getProperty name="login_user_bean" property="name" />さんの購入履歴</h1>
        <%-- ShoppingServletで付加したattribute: historyをリクエストスコープからBeanクラスのListを取得 --%>
        <% ArrayList<HistoryBean> historyList = (ArrayList<HistoryBean>) request.getAttribute("history"); %>
        <table class="table_list">
            <tbody>
                <tr>
                    <th>商品ID</th>
                    <th>商品名</th>
                    <th>購入数</th>
                </tr>

                <%-- リクエストスコープから表示する値を取得 --%>
                <% for (HistoryBean bean : historyList) {%>
                <tr>
                    <%-- 商品ID --%>
                    <td><%= bean.getItemId()%></td>
                    <%-- 商品名 --%>
                    <td><%= bean.getItemName()%></td>
                    <%-- 数量(在庫) --%>
                    <td class="int"><%= bean.getQuantity()%></td>
                </tr>
                <% }%>
            </tbody>
        </table>

        <form action="./ShoppingServlet" method="post">
            <input class="common_button" type="submit" value="一覧に戻る">
        </form>
    </body>
</html>

コード

github.com