目次
はじめに
Javaのフレームワーク「Spring Boot」とテンプレートエンジン「Thymeleaf」を使用して、List<Object>をPOSTする方法を紹介します。
本記事で作成する画面は次のとおりです。
[ユーザー情報一覧]
ユーザー情報一覧画面では一覧に表示されている「名前」「住所」「電話番号」を一括で更新できるようにしています。
スポンサーリンク
また、入力チェックも実装しているため「更新」ボタン押下時に入力チェックエラーが発生した場合は、画面上部にエラーメッセージが表示されます。
テーブル定義
本画面で使用するテーブルのテーブル定義は次のとおりです。
[ユーザー情報テーブル]
物理名 | 論理名 | データ型 | NOT NULL | 説明 |
id | ID | BIGINT | 〇 | 主キー(AUTO_INCREMENT) |
name | 名前 | VARCHAR(100) | 〇 | ユーザーの名前 |
address | 住所 | VARCHAR(255) | ユーザーの住所 | |
phone | 電話番号 | VARCHAR(50) | ユーザーの電話番号 | |
update_date | 更新日時 | DATETIME | 〇 | 最終更新日時 |
create_date | 作成日時 | DATETIME | 〇 | 登録日時 |
delete_date | 削除日時 | DATETIME | 論理削除した日時 |
※本記事ではユーザー情報テーブルにデータが登録されていることを前提としています。
[CREATE文]
CREATE TABLE `sampledb`.`user` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `address` VARCHAR(255) NULL, `phone` VARCHAR(50) NULL, `update_date` DATETIME NOT NULL, `create_date` DATETIME NOT NULL, `delete_date` DATETIME NULL, PRIMARY KEY (`id`));
ディレクトリ構成
ディレクトリ構成は次のとおりです。
springSample | |___src.main.java | |___com.example.demo | | | |___controller | | | | | |__UserController.java | | | |___dto | | | | | |__UserData.java | | | | | |__UserListParam.java | | | |___entity | | | | | |___User.java | | | |___repository | | | | | |___UserRepository.java | | | |___service | | | |___UserService.java | |___src.main.resources | |___templates | | | |___user | | | |___list.html | |___application.properties
バックエンド(サーバー)側のソースコード
コントローラークラス(UserController)
コントローラクラスは次のとおりです。
package com.example.demo.controller; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.example.demo.dto.UserListParam; import com.example.demo.service.UserService; /** * ユーザー情報 Controller */ @Controller public class UserController { /** * ユーザー情報 Service */ @Autowired private UserService userService; /** * ユーザー情報一覧画面を表示 * @param model Model * @return ユーザー情報一覧画面 */ @GetMapping(value = "/user/list") public String displayList(Model model) { UserListParam userListParam = userService.searchAll(); model.addAttribute("userListParam", userListParam); return "user/list"; } /** * ユーザー情報一覧更新 * @param userRequest リクエストデータ * @param model Model * @return ユーザー情報詳細画面 */ @RequestMapping(value = "/user/listUpdate", method = RequestMethod.POST) public String listUpdate(@Validated @ModelAttribute UserListParam userListParam, BindingResult result, Model model) { if (result.hasErrors()) { List<String> errorList = new ArrayList<String>(); for (ObjectError error : result.getAllErrors()) { if (!errorList.contains(error.getDefaultMessage())) { errorList.add(error.getDefaultMessage()); } } model.addAttribute("validationError", errorList); return "user/list"; } // ユーザー情報の更新 userService.updateAll(userListParam); return "redirect:/user/list"; } }
コントローラークラスには、ユーザー情報一覧画面の初期表示時に動作する「displayList」メソッドと、「更新」ボタン押下時に動作する「listUpdate」メソッドを用意しています。
「更新」ボタン押下時には、入力チェックを実装しており、引数のresultに入力チェックの結果が格納されています。
- 入力チェックNG:エラー内容を設定して一覧画面に戻る
- 入力チェックOK:画面からの更新内容をテーブルに反映し一覧画面を最新化する(リダイレクトすることで、一覧画面の初期表示処理を呼び出す)
スポンサーリンク
サービスクラス(UserService.java)
ビジネスロジックを記述するサービスクラスは次のとおりです。
package com.example.demo.service; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.example.demo.dto.UserData; import com.example.demo.dto.UserListParam; import com.example.demo.entity.User; import com.example.demo.repository.UserRepository; /** * ユーザー情報 Service */ @Service @Transactional(rollbackFor = Exception.class) public class UserService { /** * ユーザー情報 Repository */ @Autowired private UserRepository userRepository; /** * ユーザー情報 全検索 * @return 検索結果 */ public UserListParam searchAll() { // ユーザー情報の取得 List<User> userList = userRepository.findAll(); UserListParam userListParam = new UserListParam(); List<UserData> list = new ArrayList<UserData>(); // エンティティを画面データに詰め替える for(User user : userList) { UserData data = new UserData(); data.setId(user.getId()); data.setName(user.getName()); data.setAddress(user.getAddress()); data.setPhone(user.getPhone()); data.setUpdateDate(user.getUpdateDate()); list.add(data); } userListParam.setUserDataList(list); return userListParam; } /** * ユーザー情報更新 * @param param 画面パラメータ */ public void updateAll(UserListParam param) { List<User> userList = new ArrayList<User>(); // 画面パラメータをエンティティに詰め替える for (UserData data : param.getUserDataList()) { User user = userRepository.findById(data.getId()).get(); user.setAddress(data.getAddress()); user.setName(data.getName()); user.setPhone(data.getPhone()); user.setUpdateDate(new Date()); userList.add(user); } userRepository.saveAll(userList); } }
サービスクラスでは、初期表示時に呼び出す「searchAll」メソッドと、更新処理時に呼び出す「updateAll」メソッドを用意しています。
エンティティクラス(User.java)
ユーザー情報テーブルのエンティティクラスは次のとおりです。
package com.example.demo.entity; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; /** * ユーザー情報 Entity */ @Entity @Data @Table(name = "user") public class User implements Serializable { /** * ID */ @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 名前 */ @Column(name = "name") private String name; /** * 住所 */ @Column(name = "address") private String address; /** * 電話番号 */ @Column(name = "phone") private String phone; /** * 更新日時 */ @Column(name = "update_date") private Date updateDate; /** * 登録日時 */ @Column(name = "create_date") private Date createDate; /** * 削除日時 */ @Column(name = "delete_date") private Date deleteDate; }
テーブルと1対1の関係性になるエンティティクラスでは「@Data」アノテーションを使用して、getter、setterの定義を省略しています。
Data Transfer Objectクラス
データ転送用のDTOクラスは次のとおりです。
[UserListParam]
package com.example.demo.dto; import java.io.Serializable; import java.util.List; import javax.validation.Valid; import lombok.Data; /** * ユーザー情報一覧画面用 データクラス */ @Data public class UserListParam implements Serializable { /** * ユーザー情報リスト */ @Valid private List<UserData> userDataList; }
[UserData]
package com.example.demo.dto; import java.io.Serializable; import java.util.Date; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import org.springframework.format.annotation.DateTimeFormat; import lombok.Data; /** * ユーザー情報 */ @Data public class UserData implements Serializable { /** * ID */ private Long id; /** * 名前 */ @NotEmpty(message = "名前を入力してください") @Size(max = 100, message = "名前は100桁以内で入力してください") private String name; /** * 住所 */ @NotEmpty(message = "住所を入力してください") @Size(max = 255, message = "住所は255桁以内で入力してください") private String address; /** * 電話番号 */ @Pattern(regexp = "0\\d{1,4}-\\d{1,4}-\\d{4}", message = "電話番号の形式で入力してください") private String phone; /** * 更新日時 */ @DateTimeFormat(pattern="yyyy/MM/dd") private Date updateDate; }
本記事では「Bean Validation」を使って入力チェックしています。
上記のとおり「名前」と「住所」に必須チェック(@NotEmpty)と桁数チェック(@Size)、「電話番号」に形式チェック(@Pattern)を実装しています。
スポンサーリンク
「Bean Validation」はSpring Boot 2.3.1以降を使っている場合「spring-boot-starter-validation」の追加が必要です。
gradleの場合は「build.gradle」に以下を追加します。
dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' }
mavenの場合は「pom.xml」に以下を追加します。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
リポジトリクラス(UserRepository.java)
リポジトリクラスは次のとおりです。
package com.example.demo.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.example.demo.entity.User; /** * ユーザー情報 Repository */ @Repository public interface UserRepository extends JpaRepository<User, Long> {}
フロントエンド(クライアント)側のソースコード
ユーザー情報一覧画面(list.html)
ユーザー情報一覧画面のHTMLファイルは次のとおりです。画面デザインには「Bootstrap」を使っています。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="ユーザー情報一覧"></title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <meta charset="utf-8" /> </head> <body> <div class="container"> <div th:if="${validationError}" th:each="error : ${validationError}"> <label class="text-danger" th:text="${error}"></label> </div> <h1>ユーザー情報一覧</h1> <form th:action="@{/user/listUpdate}" th:object="${userListParam}" th:method="post"> <div class="text-end"> <input type="submit" class="btn btn-primary" value=" 更新 "> </div><br /> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>名前</th> <th>住所</th> <th>電話番号</th> <th>更新日時</th> <th></th> </tr> </thead> <tbody> <tr th:each="user, st : *{userDataList}" class="align-middle"> <td> <label th:text="${user.id}"></label> <input type="hidden" th:field="*{userDataList[__${st.index}__].id}"> </td> <td> <input type="text" class="form-control" th:field="*{userDataList[__${st.index}__].name}"> </td> <td> <input type="text" class="form-control" th:field="*{userDataList[__${st.index}__].address}"> </td> <td> <input type="text" class="form-control" th:field="*{userDataList[__${st.index}__].phone}"> </td> <td> <label th:text="${#dates.format(user.updateDate, 'yyyy/MM/dd')}"></label> <input type="hidden" th:field="*{userDataList[__${st.index}__].updateDate}"> </td> </tr> </tbody> </table> </form> </div> </body> </html>
formタグに「th:object="${userListParam}"」を定義し、「th:each="user, st : *{userDataList}"」でUserListParamクラスが持っているuserDataListを繰り返し表示することでユーザー情報の一覧を生成しています。
入力項目ではない「id」と「更新日時」を"hidden"でデータを隠し持つことで、入力チェックエラーが発生したときに表示内容が消えないようにしています。
動作確認
Spring Bootプロジェクトを実行して http://localhost:8080/user/list へアクセスします。
ユーザー情報一覧画面が表示されるので、一覧のデータを編集し「更新」ボタンを押下。データベースに画面で編集した内容が反映され、ユーザー情報一覧が最新化すれば完了です。
また「名前」や「住所」を未入力で「更新」ボタンを押下すると、画面上部に入力チェックエラーの内容が表示されます。