Spring BootのWebアプリケーションの応用① ~Spring Bootで注文フォームを作成する手順~


<h2>はじめに</h2>
Spring BootのWebアプリケーションの基本編の①~④では、IntelliJの使い方やMVCモデルを使ったWebアプリケーションの使い方を学習しました。

今回は応用編として、今まで学習した内容をもとに、注文フォームを作成する手順を説明をしていきたいと思います。

前回まではdata.sqlに追加されていた情報を一覧表示をしていましたが、今回は注文フォームを作成し、Webアプリケーションから新たにデータを追加できるようにしたいと思います。

<details>
<summary>クリックして展開</summary>
<pre>
<code>
.
├── .gradle
├── .idea
├── build
├── gradle
└── src
     ├── main
     |      ├── java
     |      |      └── com
     |      |           └── example
     |      |                 └── practice 
     |      |                          PracticeApplication.java
     |      |                          ├── web 
     |      |                          |    ├── order
     |      |                          |    |    ├── OrderForm.java
     |      |                          |    |      └── OrderController.java
     |      |                          |    |                
     |      |                          |    └── IndexController.java
     |      |                          └── domain
     |      |                                 └── order 
     |      |                                       └── OrderEntity.java
     |      |                                       └── OrderService.java
     |      |                                       └── OrderRepository.java
     |      |                               
     |      └── resources
     |              ├── static
     |              ├── templates
     |              |        └── order
     |              |                  ├── form.html 
     |              |                  └── list.html
     |              └── index.html
     |              schema.sql
     |              data.sql 
     | 　　　　　　　　　　　　       application.properties
     └── test
.gitignore
build.gradle
gradlew.bat
HELP.md
settings.gradle
</code>
</pre>
</details>

<h2>Modelの作成</h2>
では、まずはModelの作成から始めていきたいと思います。

Modelは、アプリケーションのビジネスロジックやデータを担当する部分です。


この層は、データの取得、変更、操作、バリデーションなどが行われ、「Service」や「Repository」として表現されることがあります。


データモデルをEntityで定義をした上で、「Service」や「Repository」の実装をしていきます。

OrderFormクラスを用意します。データモデルはOrderEntityと変わらないため、Order Entityをコピペします。

ただし、フォームのデータを受け取る際には通常のコンストラクタを用意する必要がないため、@AllArgsConstructorは不要です。

```Java:OrderForm.java
package com.example.practice.web.order;

import lombok.Data;
@Data
public class OrderForm {
        //注文番号
        private int orderId;
        //日付
        private String orderDate;
        //会社番号
        private int companyNo;
        //会社名
        private String companyName;
        //品物番号
        private int itemNo;
        //品物
        private String item;
        //数量
        private int quantity;
        //単価
        private int unitPrice;
        //金額
        private int price;
}
```

<h3>Repositoryの作成</h3>
Repositoryは、データベースとのやり取りを担当します。注文情報をデータベースに保存するための処理を記述します。

OrderRepositoryにInsert文を追加します。

```Java:OrderRepository.java
package com.example.practice.domain.order;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Insert;
import java.util.List;
import com.example.practice.web.order.OrderForm;

@Mapper
public interface OrderRepository {
    @Select("select * from orders")
    List<OrderEntity> findAll();
　　　　　　
    @Insert("insert into ORDERS(order_id,order_date,company_no,company_name,item_no,item,quantity,unit_price,price) values (#{orderId}, #{orderDate}, #{companyNo}, #{companyName}, #{itemNo}, #{item}, #{quantity}, #{unitPrice}, #{price})")
    void insert(OrderForm orderForm);
}
```
簡単にコードの説明をします。
この insert メソッドは、フォームから送信された注文情報をデータベースの ORDERS テーブルに挿入するための SQL クエリを実行します。その際、MyBatis のプレースホルダを使用して、OrderForm オブジェクトのデータを適切にバインドします。


<b>1.@Insertアノテーション</b>
このアノテーションは、MyBatis によって提供されるもので、SQL クエリを実行するメソッドであることを示します。引用符内の SQL 文は、実際のデータベース操作を指定します。

<b>2.#{} プレースホルダ</b>
OrderForm クラスの対応するフィールドとバインドされます。
つまり、#{orderId} は orderForm オブジェクトの getOrderId() メソッドの戻り値に置き換えられます。

<b>3.引数 OrderForm orderForm</b>
メソッドの引数として OrderForm オブジェクトが受け取られます。
このオブジェクトには、フォームから送信された注文情報がバインドされています。
OrderForm クラスの各フィールドの値が、SQL クエリ内の対応するプレースホルダにバインドされます。

<h3>ビジネスロジックの作成(Service層)</h3>
次にアプリケーションのビジネスロジックの部分を作成します。


```Java:OrderService.java
package com.example.practice.domain.order;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.example.practice.web.order.OrderForm;
import java.util.List;
import org.springframework.ui.Model;

@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository repository;
    public List<OrderEntity> findAll() {
        return repository.findAll();
    }

    public void create(OrderForm form, Model model) {
        //OrderRepository の insert メソッドを呼び出してデータベースに新しい注文情報を挿入する
        repository.insert(form);
    }
}
```

簡単にコードの説明をします。

この create メソッドは、注文情報をフォームから受け取り、それをデータベースに挿入するためのメソッドです。

<b>1.メソッドの引数 form</b>

この引数は、OrderForm クラスのオブジェクトです。このオブジェクトは、フォームから送信された注文情報を保持しています。フォームに入力されたデータは、このオブジェクトに自動的にバインディングされます。

<b>2.メソッドの引数 model</b>

この引数は、Thymeleaf テンプレートエンジンを通じてビューにデータを渡すための Model オブジェクトです。

<h2>Viewの作成</h2>
Viewは、ユーザーに情報を表示するための部分です。Thymeleafテンプレートを使用して、ユーザーに注文情報を入力させるフォームを作成します。

```html:form.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>注文フォーム | 注文管理アプリケーション</title>
</head>
<body>
<h1>注文フォーム</h1>
<form th:action="@{/orders}" th:method="post" th:object="${orderForm}">
    <div>
        <label for="orderId">注文番号</label>
        <input type="text" id="orderId" th:field="*{orderId}">
    </div>
    <div>
        <label for="orderDate">日付</label>
        <input type="text" id="orderDate" th:field="*{orderDate}">
    </div>
    <div>
        <label for="companyNo">会社番号</label>
        <input type="text" id="companyNo" th:field="*{companyNo}">
    </div>
    <div>
        <label for="companyName">会社名</label>
        <input type="text" id="companyName" th:field="*{companyName}">
    </div>
    <div>
        <label for="itemNo">品物番号</label>
        <input type="text" id="itemNo" th:value="*{itemNo}">
    </div>
    <div>
        <label for="item">品物</label>
        <input type="text" id="item" th:field="*{item}">
    </div>
    <div>
        <label for="quantity">数量</label>
        <input type="text" id="quantity" th:field="*{quantity}">
    </div>
    <div>
        <label for="unitPrice">単価</label>
        <input type="number" id="unitPrice" th:field="*{unitPrice}">
    </div>
    <div>
        <label for="price">金額</label>
        <input type="number" id="price" th:field="*{price}">
    </div>
    <div>
        <button type="submit">作成</button>
    </div>
</form>
</body>
</html>
```

<b>th:action</b>
この属性は、フォームが送信されたときのアクションを指定します。@{/orders} は、コンテキストパス /orders に対してPOSTリクエストを行うことを示しています。

<b>th:method</b>
この属性は、フォームのHTTPメソッドを指定します。ここではPOSTメソッドが指定されています。

<b>th:object</b>
この属性は、フォームにバインドされるオブジェクトを指定します。${orderForm} は、コントローラでモデルに追加された orderForm オブジェクトを指します。

<b>th:field</b>
この属性は、入力フィールドとバックエンドオブジェクトのプロパティをバインドするために使用されます。*{property} の形式で指定され、指定したプロパティの値を表示および更新します。

<b>th:value</b>
この属性は、入力フィールドの値を指定しますが、バックエンドオブジェクトのプロパティとは直接バインドされません。そのため、この属性を使用して表示する値を設定します。




<h2>Controllerの作成</h2>
Controllerは、ModelとViewを結びつける役割を果たします。注文フォームの表示やデータの受け渡しを行います。

```java:OrderController
package com.example.practice.web.order;

import com.example.practice.domain.order.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

@Controller
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
    private final OrderService orderService;
    @GetMapping
    public String showList(Model model) {
        //orderServiceクラスのfindAllメソッドで取得されたオーダーリストを、orderListでビューに渡す
        model.addAttribute("orderList", orderService.findAll());
        // "order/list"という名前のThymeleafビューを表示する
        return "order/list";
    }
    @GetMapping("/form")
    public String showForm(@ModelAttribute OrderForm form) {
        return "order/form";
    }
    @PostMapping
    public String create(@ModelAttribute OrderForm form, Model model) {
        orderService.create(form, model); 
        return showList(model);
    }
}
```

<b>showFormメソッド</b>
このメソッドは、GETリクエストが /orders/form に送信されたときに実行されます。@GetMapping("/form") アノテーションがこのメソッドをマッピングします。

このメソッドの目的は、注文フォームを表示するためのデータを用意し、それをビューに渡すことです。

<b>@ModelAttribute OrderForm form</b>
この引数は、OrderForm オブジェクトを自動的に作成し、そのオブジェクトをビューに渡します。フォームの入力フィールドに表示される初期値が含まれています。
 "order/form" を返すことによって、Thymeleafビューのパスを示しています。これにより、フォームのビューが表示されます。

<b>createメソッド</b>
このメソッドは、POSTリクエストが /orders に送信されたときに実行されます。@PostMapping アノテーションがこのメソッドをマッピングします。
注文フォームから送信されたデータを処理し、新しい注文を作成されます。

<b>@ModelAttribute OrderForm form</b>
この引数は、フォームから送信されたデータを OrderForm オブジェクトとして自動的に作成します。フォームの各入力フィールドの値がこのオブジェクトにバインドされます。

<b>Model model</b> 

この引数は、ビューに渡すためのデータを保持するための Model オブジェクトです。

<b>orderService.create(form, model)</b>
OrderService の create メソッドを呼び出し、フォームデータをもとに新しい注文を作成します。作成した注文はデータベースに追加されます。

<b>return showList(model)</b>
showList メソッドを呼び出し、注文リストを更新した後、再度リストを表示することによって、新しい注文が反映された注文一覧を表示します。

<h2>まとめ</h2>
この記事では、Spring BootのWebアプリケーション開発における基本的な概念や手順を学び、それを実践する応用編として、注文フォームの作成手順を紹介しました。

まず、モデル層の作成について説明しました。

モデル層はビジネスロジックやデータを担当し、Entityを定義し、それを元にServiceやRepositoryを実装します。

OrderFormクラスを作成し、フォームのデータを受け取るために使用します。

次に、Repository層の作成について説明しました。

Repositoryはデータベースとのやり取りを担当し、データの取得や保存を行います。

OrderRepositoryには、INSERT文を追加して新しい注文情報をデータベースに挿入する処理を記述しました。この際、MyBatisのプレースホルダを使用してバインドされたOrderFormオブジェクトのデータをクエリに組み込みます。

その後、ビジネスロジックを担当するService層の作成について説明しました。

OrderServiceクラスは、Repositoryを介してデータベースへの操作を行います。createメソッドでは、フォームから受け取った注文情報をRepositoryを通じてデータベースに挿入します。

最後に、View層の作成について説明しました。

Thymeleafテンプレートを使用して、ユーザーが注文情報を入力するためのフォームを作成しました。Thymeleafの属性を使用して、フォームのアクションやメソッド、バインディングを設定しました。

これにより、Controller層ではModelとViewを結びつけ、注文フォームの表示やデータの受け渡しを担当します。

showFormメソッドでは、フォームの初期値を設定して表示し、createメソッドではフォームデータを受け取って新しい注文を作成します。

この記事を通じて、Spring BootにおけるMVCモデルの活用と、それを用いた注文フォームの作成手順を説明しました。