Spring Boot

Spring Boot + Thymeleafで編集画面を作成する

はじめに

Javaの人気フレームワーク「Spring Boot」と「Spring Boot」と相性の良いテンプレートエンジン「Thymeleaf」を使用して編集画面を作成し、編集画面から更新処理を行う方法を紹介します。

スポンサーリンク

本記事で作成する画面のイメージは以下の通り。

編集画面

入力項目

項目 チェック内容
名前 必須、100桁以内
住所 255桁以内
電話番号 必須、電話番号形式

 

各イベント処理

「キャンセル」リンク押下:詳細画面へ戻る

「保存」ボタン押下時:下記の挙動とする

  • 入力チェックOK:データを更新し詳細画面へ遷移
  • 入力チェックNG:ユーザー編集画面の画面上部にエラー内容を表示する

 

詳細画面については「Spring Boot + Thymeleafで詳細画面を作成する」の記事を参照してください。

 

テーブル定義

物理名 論理名 データ型 NOT NULL 説明
id ID BIGINT(20) 主キー(AUTO_INCREMENT)
name 名前 VARCHAR(100) ユーザーの名前
address 住所 VARCHAR(255) ユーザーの住所
phone 電話番号 VARCHAR(50) ユーザーの電話番号
update_date 更新日時 DATETIME 最終更新日時
create_date 更新日時 DATETIME 登録日時
delete_date 更新日時 DATETIME 論理削除した日時

 

バックエンド(サーバー)側のソースコード

コントローラークラス(UserController.java)

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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.example.demo.dto.UserUpdateRequest;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;

/**
 * ユーザー情報 Controller
 */
@Controller
public class UserController {

    /**
     * ユーザー情報 Service
     */
    @Autowired
    UserService userService;

    /**
     * ユーザー情報詳細画面を表示
     * @param id 表示するユーザーID
     * @param model Model
     * @return ユーザー情報詳細画面
     */
    @GetMapping("/user/{id}")
    public String displayView(@PathVariable Long id, Model model) {
        User user = userService.findById(id);
        model.addAttribute("userRequest", user);
        return "user/view";
    }

    /**
     * ユーザー編集画面を表示
     * @param id 表示するユーザーID
     * @param model Model
     * @return ユーザー編集画面
     */
    @GetMapping("/user/{id}/edit")
    public String displayEdit(@PathVariable Long id, Model model) {
        User user = userService.findById(id);

        UserUpdateRequest userUpdateRequest = new UserUpdateRequest();
        userUpdateRequest.setId(user.getId());
        userUpdateRequest.setName(user.getName());
        userUpdateRequest.setPhone(user.getPhone());
        userUpdateRequest.setAddress(user.getAddress());
        model.addAttribute("userUpdateRequest", userUpdateRequest);
        return "user/edit";
    }

    /**
     * ユーザー更新
     * @param userRequest リクエストデータ
     * @param model Model
     * @return ユーザー情報詳細画面
     */
    @RequestMapping(value="/user/update", method=RequestMethod.POST)
    public String update(@Validated @ModelAttribute UserUpdateRequest userUpdateRequest, BindingResult result, Model model) {
        if (result.hasErrors()) {
            List<String> errorList = new ArrayList<String>();
            for(ObjectError error : result.getAllErrors()) {
                errorList.add(error.getDefaultMessage());
            }

              model.addAttribute("validationError", errorList);
              return "user/edit";
            }

        // ユーザー情報の更新
        userService.update(userUpdateRequest);
        return String.format("redirect:/user/%d", userUpdateRequest.getId());
    }
}

 

ユーザー編集画面の初期表示は「displayEdit」メソッドで行っています。処理の流れは下記の通り

  1. リクエストデータ(HTTP GET)から表示対象のIDを取得
  2. データベースからIDをキーにデータを抽出
  3. 抽出したデータを「UserUpdateRequest」クラスへ設定します
  4. ユーザー編集画面を表示します

ユーザーデータの更新は「update」メソッドで行っています。処理の流れは下記の通り

  1. 入力チェックを行う
  2. 入力チェックNGの場合は、エラー内容を設定しユーザー編集画面へ
  3. 入力チェックOKの場合は、入力データをデータベースに更新します
  4. ユーザー詳細画面へ遷移します

スポンサーリンク

サービスクラス(UserService.java)

データベース接続はJPA、トランザクション管理は@Transactionalのアノテーションを使用しています。@Transactionalを使う事で「create」メソッドの登録に成功すればコミット、失敗してExceptionが発生すればロールバックを自動で行ってくれます。

ただ@Transactionalは、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラス)が発生した場合はロールバックされずコミットされるので、「@Transactional(rollbackOn = Exception.class)」と定義することで、検査例外もロールバックされるようにしています。

package com.example.demo.service;

import java.util.Date;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dto.UserUpdateRequest;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;

/**
 * ユーザー情報 Service
 */
@Service
@Transactional(rollbackOn = Exception.class)
public class UserService {

    /**
     * ユーザー情報 Repository
     */
    @Autowired
    private UserRepository userRepository;

    /**
     * ユーザー情報 主キー検索
     * @return 検索結果
     */
    public User findById(Long id) {
        return userRepository.findById(id).get();
    }

    /**
     * ユーザー情報 更新
     * @param user ユーザー情報
     */
    public void update(UserUpdateRequest userUpdateRequest) {
        User user = findById(userUpdateRequest.getId());

        user.setAddress(userUpdateRequest.getAddress());
        user.setName(userUpdateRequest.getName());
        user.setPhone(userUpdateRequest.getPhone());
        user.setUpdateDate(new Date());
        userRepository.save(user);
    }
}

エンティティクラス(User.java)

データベースから取得したデータを格納するエンティティクラスの内容は以下の通り。@Dataアノテーションを使用して、getter、setterの定義を省略しています。

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;
}

リポジトリクラス(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> {}

Data Transfer Object(UserRequest.java)

編集画面からのリクエストデータを格納するオブジェクトクラスは以下の通り。

編集画面は登録画面と項目がほとんど同じになる為(ユーザーIDが追加されるだけ)、「UserUpdateRequest」クラスは「UserRequest」クラスを継承して作成しています。

入力チェックは「Bean Validation」を使用しています。@NotEmptyや@Sizeなどを定義することで入力チェックを実装することができます。

package com.example.demo.dto;

import java.io.Serializable;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

import lombok.Data;

/**
 * ユーザー情報 リクエストデータ
 */
@Data
public class UserRequest implements Serializable {

  /**
   * 名前
   */
  @NotEmpty(message = "名前を入力してください")
  @Size(max = 100, message = "名前は100桁以内で入力してください")
  private String name;

  /**
   * 住所
   */
  @Size(max = 255, message = "住所は255桁以内で入力してください")
  private String address;

  /**
   * 電話番号
   */
  @Pattern(regexp = "0\d{1,4}-\d{1,4}-\d{4}", message = "電話番号の形式で入力してください")
  private String phone;
}
package com.example.demo.dto;

import java.io.Serializable;

import javax.validation.constraints.NotNull;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * ユーザー情報更新リクエストデータ
 *
 */
@Data
@EqualsAndHashCode(callSuper=false)
public class UserUpdateRequest extends UserRequest implements Serializable {

  /**
   * ユーザーID
   */
  @NotNull
  private Long id;
}

 

スポンサーリンク

フロントエンド(クライアント)側のソースコード

HTML(edit.html)

以下が編集画面のHTMLです。入力チェックエラーが発生した場合は、画面上部へエラー内容を表示するようにしています。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>ユーザー編集</title>
    <link href="/css/add.css" rel="stylesheet"></link>
    <meta charset="utf-8" />
  </head>
  <body>
  <table>
    <tr th:if="${validationError}" th:each="error : ${validationError}">
        <td style="color:red;" th:text="${error}"></td>
    </tr>
  </table>
    <h1>ユーザー編集</h1>
    <form th:action="@{/user/update}" th:object="${userUpdateRequest}" th:method="post">
        <a th:href="@{/user/{id}(id=*{id})}">キャンセル</a>
        <input type="hidden" th:field="*{id}" />
        <table>
            <tr>
                <th class="cell_title">名前</th>
                <th class="cell_required">※</th>
                <td><input type="text" th:field="*{name}"></td>
            </tr>
            <tr>
                <th class="cell_title">住所</th>
                <th class="cell_required"></th>
                <td><input type="text" th:field="*{address}"></td>
            </tr>
            <tr>
                <th class="cell_title">電話番号</th>
                <th class="cell_required">※</th>
                <td><input type="text" th:field="*{phone}"></td>
            </tr>
        </table>
        <div class="btn_area_center"><input type="submit" value="保存" class="btn"></div>
    </form>
  </body>
</html>

スタイルシート(add.css)

body {
  width: 80%;
}
table{
    width: 100%;
    border-collapse: collapse;
  font-size: 13px;
}

table th, table td {
    border: 1px solid #ddd;
    padding: 6px;
}

table th {
  background-color: #F2F2F2;
}

table input {
  width:90%;
}
.cell_title {
  border-right: 0;
}
.cell_required{
    color: red;
    font-size: 12px;
    width: 10px;
    border-left: 0;
}
.btn {
  width: 100px;
    text-align: center;
}
.btn_area_center {
  margin-top: 20px;
  text-align: center;
}

 

動作確認

Spring Bootプロジェクトを実行して http://localhost:8080/user/1/edit (※1はユーザーID)へアクセスします。

編集画面が表示されます。データを編集し更新できれば完成です。

編集画面

よろしければ記事の評価をお願いします

© 2020 ITを分かりやすく解説