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

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

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

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

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

cartman0.hatenablog.com

環境

仕様・構成

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

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

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

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

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

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

商品を買う場合のActivity図

商品を買う場合のActivity図は、以下のようになる。

ログイン側では、モデルLoginUserBean とDAOクラスLoginDao を使用する。

商品一覧側では、モデルItemBean とDAOクラスShoppingDaoを使う。

購入履歴側では、モデルHistoryBean とDAOクラスShoppingDaoを使う。

商品を買う場合のActivity図ログイン商品一覧購入確認購入結果購入履歴ログイン画面LoginServletLoginDBLoginDaoLoinUserBeanShoppingServletShoppingShoppingDaoItemBeanHistoryBean商品一覧画面BuyItemServlet購入確認画面ResultServlet購入結果画面購入履歴画面

状態(画面)遷移図

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

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

Model(Beanクラス)とDAOファイルの確認

今回使うModel は3種類、 DAOクラスは2種類。

クラス名説明
LoginUserBean.java ログインユーザ情報を格納
ItemBean.java 商品情報を格納
HistoryBean.java 購入履歴を格納
クラス名説明
LoginDao.java ログイン処理用DB処理
ShoppingDAO.java 購入処理用DB処理

Model(Bean)の作成

LoginUserBean.java

ログインユーザ情報を格納するモデル。 以前作ったものと同じ。

cartman0.hatenablog.com

フィールドとして、以下の3つ。

  • ユーザID(id_)

  • ユーザの名前(name_)

  • ユーザの年齢(age_)

ログイン処理は、コントローラであるLoginServletで行う。


package login;

import java.io.Serializable;

public class LoginUserBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id_;
    private String name_;
    private int age_;

    public LoginUserBean() {
        this.id_ = "";
        this.name_ = "";
        this.age_ = 0;
    }

    public LoginUserBean(String id, String name, int age) {
        this.id_ = id;
        this.name_ = name;
        this.age_ = age;
    }

    public void setId(String id){
        this.id_ = id;
    }

    public String getId(){
        return this.id_;
    }

    public void setName(String name){
        this.name_ = name;
    }

    public String getName(){
        return this.name_;
    }

    public void setAge(int age){
        this.age_ = age;
    }

    public int getAge(){
        return this.age_;
    }

}

ItemBean.java

商品情報を格納する。 フィールドは、以下の4つ。

  • 商品ID(item_id_)

  • 商品名(item_name_)

  • 商品の値段(price_)

  • 商品の在庫数(quantity_)

商品情報のリストは、ArrayList<ItemBean> などで格納できる。


package shopping;

import java.io.Serializable;

public class ItemBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private String item_id_;
    private String item_name_;
    private int price_;
    private int quantity_;

    public ItemBean() {
        this.item_id_ = "";
        this.item_name_ = "";
        this.price_ = 0;
        this.quantity_ = 0;
    }

    public ItemBean(String item_id, String item_name, int price, int quantity) {
        this.item_id_ = item_id;
        this.item_name_ = item_name;
        this.price_ = price;
        this.quantity_ = quantity;
    }

    public void setItemId(String item_id) {
        this.item_id_ = item_id;
    }

    public String getItemId() {
        return this.item_id_;
    }

    public void setItemName(String item_name) {
        this.item_name_ = item_name;
    }

    public String getItemName() {
        return this.item_name_;
    }

    public void setPrice(int price) {
        this.price_ = price;
    }

    public int getPrice() {
        return this.price_;
    }

    public void setQuantity(int quantity) {
        this.quantity_ = quantity;
    }

    public int getQuantity() {
        return this.quantity_;
    }

}

HistoryBean.java

購入履歴を格納する。 フィールドは、以下の3つ。

  • 商品ID(item_id_)

  • 商品名(item_name_)

  • 購入した数(quantity_)


package shopping;

import java.io.Serializable;

/**
 * HistoryBean
 */
public class HistoryBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private String item_id_; // 商品ID
    private String item_name_; // 商品名
    private int quantity_; // 購入した数

    public HistoryBean() {
        this.item_id_ = "";
        this.item_name_ = "";
        this.quantity_ = 0;
    }

    public HistoryBean(String item_id, String item_name, int quantity) {
        this.item_id_ = item_id;
        this.item_name_ = item_name;
        this.quantity_ = quantity;
    }

    public void setItemId(String item_id){
        this.item_id_ = item_id;
    }

    public String getItemId(){
        return this.item_id_;
    }

    public void setItemName(String item_name){
        this.item_name_ = item_name;
    }

    public String getItemName(){
        return this.item_name_;
    }

    public void setQuantity(int quantity){
        this.quantity_ = quantity;
    }

    public int getQuantity(){
        return this.quantity_;
    }

}

DAOクラスの作成

DAOクラスは、ユーザ情報を持つuserテーブルにアクセスするLoginDao と 商品情報(itemテーブル)と購入履歴(historyテーブル)にアクセスするShoppingDao の2種類。

LoginDao.java

userテーブルにアクセスする。

コンストラクタで、 JDBCドライバでMySQLデータベースに接続する。

ResultSet selectUser(String id, String pass)メソッドで、 ユーザIDとパスワードの照会を行う。


package login;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * ログインDAOクラス.
 */
public class LoginDao {

    private Connection con_ = null;
    private ResultSet rs_ = null;
    private PreparedStatement ps_ = null;

    public LoginDao() throws SQLException {
        try {
            // JDBCドライバのロード
            // 「com.mysql.jdbc.Driver」クラス名
            Class.forName("com.mysql.jdbc.Driver");

            // データベースと接続(本来はユーザやパスワードも別管理にしておくのが理想)
            this.con_ = DriverManager.getConnection("jdbc:mysql://localhost:3306/shopping_sample",
                    "user",
                    "password");
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
    }

    /**
     * データベースから指定されたIDとパスワードを使ってユーザ情報を検索します.
     *
     * @param id    ログインID
     * @param pass  パスワード
     * @return  ユーザ情報(ResultSet)
     * @throws SQLException
     */
    public ResultSet selectUser(String id, String pass) throws SQLException {
        // SQL文を生成
        this.ps_ = this.con_.prepareStatement("select name, age from user where id = ? and pass = ?");

        // 生成したSQL文の「?」の部分にIDとパスワードをセット
        this.ps_.setString(1, id);
        this.ps_.setString(2, pass);

        // SQLを実行
        this.rs_ = this.ps_.executeQuery();

        return this.rs_;
    }

    /**
     * コネクションをクローズします.
     */
    public void close() {
        try {
            // データベースとの接続を解除する
            if (this.con_ != null) {
                this.con_.close();
            }
            if (this.ps_ != null) {
                this.ps_.close();
            }
            if (this.rs_ != null) {
                this.rs_.close();
            }
        } catch (SQLException se) {
            // データベースとの接続解除に失敗した場合
            se.printStackTrace();
        }
    }
}

ShoppingDao.java

商品情報(itemテーブル)、在庫(stockテーブル)と購入履歴(historyテーブル)にアクセスする。

ResultSet selectItem() メソッドで、itemテーブルに登録されているすべての商品とstockテーブルから商品情報とその在庫数を返す。

ResultSet selectItem(String item_id) メソッドで、 その商品IDを元に、itemテーブルとstockテーブルから商品情報とその在庫数を返す。

ResultSet selectHistory(String user_id)メソッドで、 ユーザIDを元に、historyテーブルからそのユーザ購入履歴情報を返す。

void updateItem(String item_id, int purchased_num)メソッドで、 商品が購入された場合に、 stockテーブルの在庫数を更新する。

void updateHistory(String user_id, String item_id, int purchased_num) メソッドで、商品が購入された場合に、historyテーブルで商品履歴を更新する。


package shopping;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * ショッピング風DAOクラス. DBの商品(item)テーブルと購入履歴(history)のテーブルを扱う
 */
public class ShoppingDao {
    private Connection con_ = null;
    private ResultSet rs_ = null;
    private PreparedStatement ps_ = null;

    public ShoppingDao() throws SQLException {
        // JDBCドライバのロード
        try {
            // 「com.mysql.jdbc.Driver」クラス名
            Class.forName("com.mysql.jdbc.Driver");

            // データベースと接続(本来はユーザやパスワードも別管理にしておくのが理想)
            this.con_ = DriverManager.getConnection("jdbc:mysql://localhost:3306/shopping_sample",
                    "user",
                    "password");
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
    }

    /**
     * データベースの全商品と在庫を取得します.
     *
     * @return  商品情報(ResultSet)
     * @throws SQLException
     */
    public ResultSet selectItem() throws SQLException {
        // SQL文を生成
        this.ps_ = this.con_.prepareStatement(
                "select item.item_id, item.item_name, item.price, stock.quantity from item inner join stock on item.item_id = stock.item_id"
        );

        // SQLを実行
        this.rs_ = this.ps_.executeQuery();

        return this.rs_;
    }

    /**
     * 商品IDを基にデータベースの商品情報と在庫を取得します.
     *
     * @return  商品情報(ResultSet)
     * @param item_id 商品ID
     * @throws SQLException
     */
    public ResultSet selectItem(String item_id) throws SQLException {
        // SQL文を生成
        this.ps_ = this.con_.prepareStatement(
                "select item.item_name, item.price, stock.quantity from item inner join stock on item.item_id = stock.item_id where item.item_id = ?"
        );
        // SQL文に商品IDを設定
        this.ps_.setString(1, item_id);
        // SQLを実行
        this.rs_ = this.ps_.executeQuery();

        return this.rs_;
    }

    /**
     * ユーザの購入履歴を取得します.
     *
     * @param user_id
     * @return  購入履歴(ResultSet)
     * @throws SQLException
     */
    public ResultSet selectHistory(String user_id) throws SQLException {
        // SQL文を生成
        this.ps_ = this.con_.prepareStatement("select history.item_id, item.item_name, history.quantity from history inner join item on history.id = ? and history.item_id = item.item_id");
        this.ps_.setString(1, user_id);

        // SQLを実行
        this.rs_ = this.ps_.executeQuery();
        return this.rs_;
    }

    /**
     * 商品IDを基にデータベースの在庫を更新(マイナス)します.
     *
     * @param item_id;
     * @param purchased_num 購入数
     * @throws SQLException
     */
    public void updateItem(String item_id, int purchased_num) throws SQLException {
         // SQL文を生成
        /* update文を追加
         在庫から購入数を引く
         */
        this.ps_ = this.con_.prepareStatement("update stock set quantity = quantity - ? where item_id = ?");

        // SQL文に数量を設定
        this.ps_.setInt(1, purchased_num);
        // SQL文に商品IDを設定
        this.ps_.setString(2, item_id);
        // SQLを実行
        this.ps_.executeUpdate();
    }

    /**
     * 購入履歴テーブルを更新します.
     *
     * @param user_id   ユーザID
     * @param item_id 商品ID
     * @param purchased_num 購入数
     * @throws SQLException
     */
    public void updateHistory(String user_id, String item_id, int purchased_num) throws SQLException {
         // SQL文を生成
        this.ps_ = this.con_.prepareStatement("insert into history(id, item_id, quantity) values (?, ?, ?)");

        this.ps_.setString(1, user_id);
        this.ps_.setString(2, item_id);
        this.ps_.setInt(3, purchased_num);

        this.ps_.executeUpdate();
    }

    /**
     * コネクションをクローズします.
     */
    public void close() {
        try {
            // データベースとの接続を解除する
            if (this.con_ != null) {
                this.con_.close();
            }
            if (this.ps_ != null) {
                this.ps_.close();
            }
            if (this.rs_ != null) {
                this.rs_.close();
            }
        } catch (SQLException se) {
            // データベースとの接続解除に失敗した場合
            se.printStackTrace();
        }
    }
}

コード

github.com