MVCモデルに沿ったデータ処理

# はじめに
　初めまして。佐藤佑哉です。
　今回は授業の中でMVCモデルの説明があったので、備忘録として残したいなと思います。気になる箇所があったらコメントなどよろしくお願い致します！
# 環境
Apache NetBEans IDE：15
Java：17.0.4.1
Windows11・バージョン10.0
MySQL：8.0.31

# MVCモデルとは
役割毎にModel,View,Controllerに分割してコーディングを行うモデル
- Model：システム中でビジネスロジックを担当
- View：表示や入出力と行った処理を担当
- Controller：ユーザの入力に基づき、ModelとViewを制御
引用：https://qiita.com/s_emoto/items/975cc38a3e0de462966a

## MVCモデル概念図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2989029/793a9336-2a87-05f0-cae6-ec2dca307e19.png)





# 動作
　今回は授業課題として自分なりのWebページを作るというもので、値段管理リストを作ってみました。
　動作は次のようになります。

1. 値段・商品・商品を買う店名を選択し、登録ボタンを押す
1. 下の表に登録したデータが追加される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2989029/6599a394-8da3-a6bb-e7a1-d7f700cfdeda.png)
↓登録ボタンを押すと･･･
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2989029/74add8ad-8b84-056d-4c04-21261e71f4e0.png)

# 実装
## データベース：MySQL
実際、外部キーとか設定していますが、結局有効活用していないような気がします笑
### 値段テーブル
- management_id：値段管理ID（主キー）
- merchandise_id：商品ID（外部キー）
- shop_id：店（外部キー）

```mysql
mysql> desc price;
+----------------+------+------+-----+---------+----------------+
| Field          | Type | Null | Key | Default | Extra          |
+----------------+------+------+-----+---------+----------------+
| management_id  | int  | NO   | PRI | NULL    | auto_increment |
| merchandise_id | int  | NO   | MUL | NULL    |                |
| shop_id        | int  | NO   | MUL | NULL    |                |
| created_at     | date | NO   |     | NULL    |                |
| price          | int  | NO   |     | NULL    |                |
+----------------+------+------+-----+---------+----------------+
```
### 商品テーブル
- merchandise_id：商品ID
- merchandise_name：商品名
- created_at：商品の登録日
```
mysql> desc merchandise;
+------------------+-------------+------+-----+---------+----------------+
| Field            | Type        | Null | Key | Default | Extra          |
+------------------+-------------+------+-----+---------+----------------+
| merchandise_id   | int         | NO   | PRI | NULL    | auto_increment |
| merchandise_name | varchar(20) | NO   |     | NULL    |                |
| created_at       | date        | NO   |     | NULL    |                |
+------------------+-------------+------+-----+---------+----------------+
```
### 店テーブル
- shop_id：店ID
- shop_name：店名
- created_at：店の登録日
```
mysql> desc shop;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| shop_id    | int         | NO   | PRI | NULL    | auto_increment |
| shop_name  | varchar(20) | NO   |     | NULL    |                |
| created_at | date        | NO   |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+
```
## View：jsp
### 値段管理リスト：（UIは前述の写真の通り）
<details><summary>コードの詳細（クリックすると表示されます）</summary>

```jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!-- 商品テーブルのクラスを宣言 --><jsp:useBean id="merchandise" scope="session" class="final_task.MerchandiseDb"/>
<!-- 店テーブルのクラスを宣言 --><jsp:useBean id="shop" scope="session" class="final_task.ShopDb" />
<!-- 結合テーブルのクラスを宣言 --><jsp:useBean id="combined" scope="session" class="final_task.ConbinedPriceDb" />

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>値段管理リスト</title>
        
        <style>
            p{
                color: blue;
            }
        </style>
        
    </head>
    <body>
        <div >
            <h1>値段管理リスト</h1>

            <!-- 値段を登録するフォーム -->
            <form method="post" action="<%=request.getContextPath()%>/RegisterPriceSer">

                <!-- 値段を記入する項目（Object：値段） -->
                <input type="number" name="register_price" value="0" placeholder="値段を記入して下さい"/>

                <!-- 商品を選択するプルダウンリスト（Object：商品ID） -->
                <select name="register_merchandise_id">
                    <option value="-1">商品を選択して下さい</option>
                    <% for (int i = 0; i < merchandise.getDataNum(); i++) {%>
                    <option value="<%=merchandise.getMerchandiseId(i)%>"><%=merchandise.getMerchandiseName(i)%></option>
                    <%}%>
                </select>

                <!-- 店を選択するプルダウンリスト （Object：店ID）-->
                <select name="register_shop_id">
                    <option value="-1">店を選択して下さい</option>
                    <% for (int i = 0; i < shop.getDataNum(); i++) {%>
                    <option value="<%=shop.getShopId(i)%>"><%=shop.getShopName(i)%></option>
                    <%}%>
                </select>

                <!-- 登録ボタン -->
                <input type="submit" value="登録"/>

                <!-- ページリストに戻るボタン -->
                <input type="button" value="戻る" onclick="location.herf = '../page_list.jsp'"/>
            </form>

            <!-- 値段管理リストの表示 -->
            登録した値段件数：<%= combined.getDataNum()%>

            <table border="1">
                <!-- 属性名 -->
                <tr>
                    <th><p>値段</p></th>
                    <th><p>商品名</p></th>
                    <th><p>店名</p></th>
                    <th><p>登録日</p></th>
                </tr>
                <!-- 値段管理リストデータを埋め込む-->
                <% for (int i = 0; i < combined.getDataNum(); i++) {//データ数だけ表示させる
                %>
                <tr>
                    <td><%=combined.getPrice(i)%>円</td><!-- 値段 -->
                    <td><%=combined.getMerchandiseName(i)%></td><!-- 商品名 -->
                    <td><%=combined.getShopName(i)%></td><!-- 店名 -->
                    <td><%=combined.getCreatedAt(i)%></td><!-- 登録日 -->
                </tr>

                
                

                <%}%>
            </table>
        </div>
    </body>
</html>
```
</details>

## Model：Java
### 店データを取得
- 店ID
- 店名
<details><summary>コードの詳細（クリックすると表示されます）</summary>

```java
import java.sql.*;

/**
 *
 * @author yuya0
 */
public class ShopDb {

    /* フィールドの定義 */
    protected int[] shopId = new int[10];//店UD
    protected String[] shopName = new String[10];//店名
    protected Date[] createdAt = new Date[10];//登録日
    protected int dataNum;//データ取得件数

    /**
     * 店データ取得メソッド
     *
     * @throws Exception
     */
    public void dataload() throws Exception {
        /* データベースに接続 */
        dataNum = 0;
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url = "jdbc:mysql://localhost/kadai15?characterEncoding=UTF-8";
        Connection conn = DriverManager.getConnection(url, "softd", "yuya4649");

        /* SELECT文の実行 */
        String sql = "SELECT *FROM shop";
        PreparedStatement stmt = conn.prepareStatement(sql);//JDBCのステートメント（sql）を作成
        ResultSet resultSet = stmt.executeQuery();//ステートメントを実行しリザルトセットに代入

        /* 結果の取り出しと表示 */
        while (resultSet.next()) {
            this.shopId[dataNum] = resultSet.getInt("shop_id");
            this.shopName[dataNum] = resultSet.getString("shop_name");
            this.createdAt[dataNum] = resultSet.getDate("created_at");
            dataNum++;
        }

        /* データベースからの切断 */
        resultSet.close();
        stmt.close();
        conn.close();
    }
    
    /**
     *店名を登録するメソッド
     * @param registerShopName//登録する店名
     * @throws Exception
     */
    public void insert(String registerShopName) throws Exception{
        /* データベースに接続 */
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url ="jdbc:mysql://localhost/kadai15?characterEncoding=UTF-8";
        Connection conn = DriverManager.getConnection(url,"softd","yuya4649");
        
        /* INSERT文の実行 */
        String sql = "INSERT INTO shop (shop_name,created_at) VALUE(?,curDate())";
        
        /* SQL文の設定 */
        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, registerShopName);//1つ目の？に引数をセット
        
        /* 実行 */
        stmt.executeUpdate();
        
        /* データベースからの切断 */
        stmt.close();
        conn.close();
        
    }

    /* GETアクセッサ */
    public int getShopId(int i) {
        if (i >= 0 && dataNum > i) {
            return shopId[i];
        } else {
            return 0;
        }
    }

    public String getShopName(int i) {
        if (i >= 0 && dataNum > i) {
            return shopName[i];
        } else {
            return null;
        }
    }

    public Date getCreatedAt(int i) {
        if (i >= 0 && dataNum > i) {
            return createdAt[i];
        } else {
            return null;
        }
    }

    public int getDataNum() {
        return dataNum;
    }
}
```

</details>

### 商品データを取得
- 商品Id
- 商品名
<details><summary>コードの詳細（クリックすると表示されます）</summary>

```java
import java.sql.*;

/**
 *
 * @author yuya0
 */
public class MerchandiseDb {

    /* フィールドの定義 */
    protected int[] merchandiseId = new int[10];//商品ID
    protected String[] merchandiseName = new String[10];//商品名
    protected Date[] createdAt = new Date[10];//登録日
    protected int dataNum;//データ取得件数

    /**
     * 商品データ取得メソッド
     *
     * @throws Exception
     */
    public void dataload() throws Exception {
        /* データベースに接続 */
        dataNum = 0;
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url = "jdbc:mysql://localhost/kadai15?characterEncoding=UTF-8";
        Connection conn = DriverManager.getConnection(url, "softd", "yuya4649");//上記URL設定でユーザ名とパスワードを使って接続

        /* SELECT文の実行 */
        String sql = "SELECT *FROM merchandise";
        PreparedStatement stmt = conn.prepareStatement(sql);//JDBCのステートメント（sql）を作成
        ResultSet resultSet = stmt.executeQuery();//ステートメントを実行しリザルトセットに代入

        /* 結果の取り出しと表示 */
        while (resultSet.next()) {
            this.merchandiseId[dataNum] = resultSet.getInt("merchandise_id");
            this.merchandiseName[dataNum] = resultSet.getString("merchandise_name");
            this.createdAt[dataNum] = resultSet.getDate("created_at");
            dataNum++;
        }

        /* データベースからの切断 */
        resultSet.close();
        stmt.close();
        conn.close();

    }

    public void insert(String registerMerchandiseName) throws Exception {
        /* データベースに接続 */
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url = "jdbc:mysql://localhost/kadai15?characterEncoding=UTF-8";
        Connection conn = DriverManager.getConnection(url, "softd", "yuya4649");

        /* INSERT文の実行 */
        String sql = "INSERT INTO merchandise (merchandise_name,created_at) VALUE (?,curDate())";

        /* SQL文の設定 */
        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, registerMerchandiseName);//1つ目の？に引数をセット

        /* 実行 */
        stmt.executeUpdate();

        /* データベースからの切断 */
        stmt.close();
        conn.close();

    }

    /* GETアクセッサ */
    public int getMerchandiseId(int i) {
        if (i >= 0 && dataNum > i) {
            return merchandiseId[i];
        } else {
            return 0;
        }
    }

    public String getMerchandiseName(int i) {
        if (i >= 0 && dataNum > i) {
            return merchandiseName[i];
        } else {
            return null;
        }
    }

    public Date getCreatedAt(int i) {
        if (i >= 0 && dataNum > i) {
            return createdAt[i];
        } else {
            return null;
        }
    }

    public int getDataNum() {
        return dataNum;
    }
}
```
</details>

### それぞれのテーブルを結合してデータを取得
- 管理ID
- 値段
- 商品名
- 店名
- 登録日

<details><summary>コードの詳細（クリックすると表示されます）</summary>

```java
import java.sql.*;

/**
 *
 * @author yuya0
 */
public class ConbinedPriceDb {

    /* フィールドの定義 */
    protected int[] managementId = new int[10];//管理ID
    protected int[] price = new int[10];//値段
    protected String[] merchandiseName = new String[10];//商品名
    protected String[] shopName = new String[10];//店名
    protected Date[] createdAt = new Date[10];//値段の登録日
    protected int dataNum;//データ数
    protected int i;
    
//データ更新メソッド
    public void dataLoad() throws Exception {
        /* データベースに接続 */
        dataNum = 0;
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        String url = "jdbc:mysql://localhost/kadai15?characterEncoding=UTF-8";
        Connection conn = DriverManager.getConnection(url, "softd", "yuya4649");

        /*SELECT分の実行*/
        String sql = "SELECT "
                + "price.management_id,"//管理ID
                + "price.price,"//値段
                + "merchandise.merchandise_name,"//商品名
                + "shop.shop_name,"//店名
                + "price.created_at "//値段の登録日
                + "FROM price "
                + "INNER JOIN merchandise "
                + "ON price.merchandise_id = merchandise.merchandise_id "//merchandiseテーブルから商品名を抽出
                + "INNER JOIN shop "
                + "ON price.shop_id = shop.shop_id ";//shopテーブルから店名を抽出
        PreparedStatement stmt = conn.prepareStatement(sql);
        ResultSet resultSet = stmt.executeQuery();

        /* 結果の取り出しと表示 */
        while (resultSet.next()) {
            this.managementId[dataNum] = resultSet.getInt("management_id");
            this.price[dataNum] = resultSet.getInt("price");
            this.merchandiseName[dataNum] = resultSet.getString("merchandise_name");
            this.shopName[dataNum] = resultSet.getString("shop_name");
            this.createdAt[dataNum] = resultSet.getDate("created_at");
            dataNum++;
        }

        /* データベースからの切断 */
        resultSet.close();
        stmt.close();
        conn.close();
    }

    /* GETアクセッサ */
//    管理IDGETメソッド
    public int getManagementId(int i) {
        if (i >= 0 && dataNum > i) {
            return managementId[i];
        } else {
            return 0;
        }
    }

//    値段GETメソッド
    public int getPrice(int i) {
        if (i >= 0 && dataNum > i) {
            return price[i];
        } else {
            return 0;
        }
    }

//    商品名GETメソッド
    public String getMerchandiseName(int i) {
        if (i >= 0 && dataNum > i) {
            return merchandiseName[i];
        } else {
            return null;
        }
    }
//    店名GETメソッド

    public String getShopName(int i) {
        if (i >= 0 && dataNum > i) {
            return shopName[i];
        } else {
            return null;
        }
    }

//    値段の登録日GETメソッド
    public Date getCreatedAt(int i) {
        
        if (i >= 0 && dataNum > i) {
            return createdAt[i];
        } else {
            return null;
        }
    }
    
//    データ数GETメソッド
    public int getDataNum() {
        return dataNum;
    }
}
```
</details>

## Controller：Servlet
### 登録ボタンを押した後のデータの処理
<details><summary>コードの詳細（クリックすると表示されます）</summary>

```java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author yuya0
 */
public class RegisterPriceSer extends HttpServlet {

    /*フィールドの定義*/
    private int price;//値段
    private int merchandiseId;//商品ID
    private int shopId;//店ID
    private final PriceDb registerPrice = new PriceDb();//値段テーブルのインスタンスを宣言

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
     * methods.
     *
     * @param request servlet request
     * @param response servlet response
     * @param errorMessage
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response, String[] errorMessage)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try ( PrintWriter out = response.getWriter()) {
            /* TODO output your page here. You may use following sample code. */
            out.println("<!DOCTYPE html>");
            out.println("<html>");
            out.println("<head>");
            out.println("<title>エラー</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>入力に誤りがあります</h1>");
            
            //エラーを表示させる
            for(int i = 0; errorMessage[i]!=null; i++){
                out.println("<p>"+errorMessage[i]+"</p>");
            }
            
            out.println("</body>");
            out.println("</html>");
        }
    }

    /*
    * doPostメソッド
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        //文字コード等　基本設定
        response.setContentType("text/html; charset=UTF-8");
        request.setCharacterEncoding("UTF-8");
        
        //値段管理リストから受け取ったデータをそれぞれの変数に取得
        price = Integer.parseInt(request.getParameter("register_price"));
        merchandiseId = Integer.parseInt(request.getParameter("register_merchandise_id"));
        shopId = Integer.parseInt(request.getParameter("register_shop_id"));
        //エラーを格納する変数
        String[] errorMessage = new String[3];
        int i = 0;
        
        try {
            if (price <= 0) {//価格が0以上だとエラーメッセージが格納される
                errorMessage[i] = "値段は0円以上です";
                i++;
            }

            if (merchandiseId == -1) {//商品が未選択だと－1が代入され、エラーメッセージが格納される
                errorMessage[i] = "商品を選んで下さい";
                i++;
            }
            
            if (shopId == -1) {//店が未選択だと－1が代入され、エラーメッセージが格納される
                errorMessage[i] = "店を選んで下さい";
                i++;
            }
            
            if (i==0) {//エラーメッセージが一つも格納されていない場合は、正常にデータベースに登録される
                
                //値段テーブルに登録
                registerPrice.insert(price,merchandiseId,shopId);
                
                //値段管理リストに遷移
                response.sendRedirect("/Web/final/price_list/price_list_page_db_connection.jsp");
                
            }else{
                processRequest(request,response,errorMessage);//エラー画面に遷移
                
            }
            
        } catch (Exception e) {
            processRequest(request, response, null);//エラー表示
        }

    }
}
```
</details>

# 終わりに
今回はMVCモデルの学習を授業を通して学び、備忘録として残しました。これから、Androidアプリ開発においてMVVMモデルを学ぶので、個人的にはMVCモデルを知っておいて良かったなと感じました。

最後まで読んでいただき、ありがとうございました！