目次
はじめに
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 へアクセスします。
ユーザー情報一覧画面が表示されるので、一覧のデータを編集し「更新」ボタンを押下。データベースに画面で編集した内容が反映され、ユーザー情報一覧が最新化すれば完了です。
また「名前」や「住所」を未入力で「更新」ボタンを押下すると、画面上部に入力チェックエラーの内容が表示されます。