0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring入門】第12章 社員変更

0
Last updated at Posted at 2026-10-01

社員一覧の各行に「変更」ボタンを付け、その社員のデータが入った変更画面 → 確認画面 → 完了画面の流れを作ります。仕組みは第10・11章の新規登録とほとんど同じです。違うところに注目しながら進めましょう。

この章で学ぶこと

  • データベースのデータをフォームに詰め替えて、変更画面に表示する方法
  • URL に社員ID を入れて、どの社員を変更するかを伝える方法(@PathVariable)
  • save メソッドでデータを更新する方法

12.1 この章で作るもの

社員一覧の各行に「変更」ボタンを付けます。ボタンを押すと、その社員の今のデータが入った変更画面が開きます。内容を直して「確認」を押すと確認画面へ、確認画面で「変更」を押すと完了画面へ進みます。

ch12-1.png
図12-1 社員変更の画面の流れ

新規登録との違い

画面の流れは新規登録と同じですが、次の点が違います。

項目 新規登録(第10・11章) 変更(この章)
入力画面の最初の状態 空欄 その社員の 今のデータが入っている
社員ID まだ決まっていない(登録時に自動で振られる) 決まっている(表示するだけで変更はできない)
URL /employees/register など /employees/1/edit のように 社員ID が入る
セッションに保存する名前 registerForm updateForm
save で実行される SQL INSERT(登録) UPDATE(更新)

12.2 一覧画面に「変更」ボタンを付ける

src/main/resources/templates/employee/list.html を開き、「削除」ボタンのフォームの前に「変更」ボタンのリンクを追加します。

src/main/resources/templates/employee/list.html(操作の列のみ抜粋)
      <td>
        <a class="button" th:href="@{/employees/{empId}/edit(empId=${employee.empId})}">変更</a>
        <form class="inline" th:action="@{/employees/{empId}/delete(empId=${employee.empId})}" method="post">
          <button type="submit">削除</button>
        </form>
      </td>

変更画面を 表示するだけ なので、「新規登録」ボタン(第10章)と同じように、リンクで作っています(データを変えるのは、確認画面で「変更」ボタンを押したときです)。社員ID 1 の行では、リンク先が /employees/1/edit になります。

12.3 フォームと Controller を作る

UpdateForm を作る

変更画面の入力を受け取るフォームクラスを作ります。変更画面の項目は、新規登録の項目に 社員ID を足したものです。そこで、第10章の RegisterForm を 継承 して、社員ID だけを追加します。

  1. com.example.employeeapp.form パッケージを右クリックし、新規 → クラス を選びます。名前に UpdateForm と入力して 完了 を押します。
  2. 次のコードを入力します。getter と setter は、ソース → getter および setter の生成 で作ります。
src/main/java/com/example/employeeapp/form/UpdateForm.java
package com.example.employeeapp.form;

public class UpdateForm extends RegisterForm {

    private static final long serialVersionUID = 1L;

    private Integer empId;

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }
}

氏名や住所などのフィールドと getter・setter は、RegisterForm から引き継ぎます。RegisterForm は Serializable を実装しているので、UpdateForm もセッションに保存できます。

Controller を作る

com.example.employeeapp.controller パッケージを右クリックし、新規 → クラス を選びます。名前に EmployeeUpdateController と入力して 完了 を押し、次のように書きます。

src/main/java/com/example/employeeapp/controller/EmployeeUpdateController.java
package com.example.employeeapp.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.PostMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import com.example.employeeapp.entity.Department;
import com.example.employeeapp.entity.Employee;
import com.example.employeeapp.form.UpdateForm;
import com.example.employeeapp.repository.DepartmentRepository;
import com.example.employeeapp.repository.EmployeeRepository;

@Controller
@SessionAttributes("updateForm")
public class EmployeeUpdateController {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    /** セッションに UpdateForm オブジェクトがないとき呼ばれる
     * @return 空の UpdateForm オブジェクト */
    @ModelAttribute
    public UpdateForm createForm() {
        return new UpdateForm();
    }

    /** 社員変更の入力画面を表示
     * その社員の今のデータをフォームに入れて表示
     * @param empId 変更する社員ID(取得元:URL)
     * @param model 表示する社員情報と部署一覧
     * @return 社員変更の入力画面 */
    @GetMapping("/employees/{empId}/edit")
    public String input(@PathVariable Integer empId, Model model) {
        Employee employee = employeeRepository.findByEmpId(empId);
        UpdateForm updateForm = new UpdateForm();
        updateForm.setEmpId(employee.getEmpId());
        updateForm.setName(employee.getName());
        updateForm.setGender(employee.getGender());
        updateForm.setAge(employee.getAge());
        updateForm.setBirthDate(employee.getBirthDate());
        updateForm.setDeptId(employee.getDepartment().getDeptId());
        updateForm.setAddress(employee.getAddress());
        model.addAttribute("updateForm", updateForm);
        List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
        model.addAttribute("departments", departments);
        return "employee/update/input";
    }

    /** 社員変更の確認画面を表示
     * @param updateForm 入力された社員情報
     * @param model 表示する部署情報
     * @return 社員変更の確認画面 */
    @PostMapping("/employees/{empId}/edit/confirm")
    public String confirm(@ModelAttribute UpdateForm updateForm, Model model) {
        Department department = departmentRepository.findByDeptId(updateForm.getDeptId());
        model.addAttribute("department", department);
        return "employee/update/confirm";
    }

    /** 社員変更の確認画面から入力画面に戻る
     * @param updateForm 入力された社員情報
     * @param model 表示する部署一覧
     * @return 社員変更の入力画面 */
    @GetMapping("/employees/{empId}/edit/back")
    public String back(@ModelAttribute UpdateForm updateForm, Model model) {
        List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
        model.addAttribute("departments", departments);
        return "employee/update/input";
    }

    /** 社員変更の完了画面を表示
     * @param updateForm 入力された社員情報
     * @param sessionStatus セッション破棄時に利用
     * @return 社員変更の完了画面 */
    @PostMapping("/employees/{empId}/edit/complete")
    public String complete(@ModelAttribute UpdateForm updateForm, SessionStatus sessionStatus) {
        Employee employee = employeeRepository.findByEmpId(updateForm.getEmpId());
        employee.setName(updateForm.getName());
        employee.setGender(updateForm.getGender());
        employee.setAge(updateForm.getAge());
        employee.setBirthDate(updateForm.getBirthDate());
        employee.setDepartment(departmentRepository.findByDeptId(updateForm.getDeptId()));
        employee.setAddress(updateForm.getAddress());
        employeeRepository.save(employee);

        // セッションに保存していた入力内容を破棄する
        sessionStatus.setComplete();
        return "redirect:/employees/edit/done";
    }

    /** 社員変更の完了画面を表示
     * @return 社員変更の完了画面 */
    @GetMapping("/employees/edit/done")
    public String done() {
        return "employee/update/complete";
    }
}

第11章の EmployeeRegisterController と見比べてみてください。createForm と、確認・戻る・完了のメソッド構成はほぼ同じです。新しく登場したのは、次のものです。

  • @PathVariable … URL に含まれる社員ID を受け取る(12.6 で解説)

【再確認】クラス名やアノテーションは補完で入力する

@PathVariable、Model などは、途中まで打って Ctrl + Space で補完すると、import 文も自動で追加されます(第4章「入力は補完で行う」)。Model は Model - org.springframework.ui を選んでください。手で打ち込んで赤い波線が出たときは、語の末尾で Ctrl + Space を押して選び直します。

12.4 テンプレートを作る

src/main/resources/templates/employee フォルダーを右クリックし、新規 → フォルダー で update フォルダーを作ります。その中に、次の3つのファイルを作ります(新規 → ファイル)。

ファイル 画面
update/input.html 変更画面
update/confirm.html 確認画面
update/complete.html 完了画面

変更画面(input.html)

新規登録の入力画面(register/input.html)をもとにしています。違うのは、見出し、送信先の URL に社員ID を入れていること、社員ID を表示する行があること です。

src/main/resources/templates/employee/update/input.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員変更</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
  <h1>社員変更</h1>

  <form th:action="@{/employees/{empId}/edit/confirm(empId=${updateForm.empId})}" th:object="${updateForm}" method="post">
    <table>
      <tr>
        <th>社員ID</th>
        <td th:text="*{empId}"></td>
      </tr>
      <tr>
        <th>氏名</th>
        <td>
          <input type="text" th:field="*{name}">
        </td>
      </tr>
      <tr>
        <th>性別</th>
        <td>
          <label><input type="radio" th:field="*{gender}" value="1"> 男</label>
          <label><input type="radio" th:field="*{gender}" value="2"> 女</label>
        </td>
      </tr>
      <tr>
        <th>年齢</th>
        <td>
          <input type="text" th:field="*{age}">
        </td>
      </tr>
      <tr>
        <th>生年月日</th>
        <td>
          <input type="date" th:field="*{birthDate}">
        </td>
      </tr>
      <tr>
        <th>部署</th>
        <td>
          <select th:field="*{deptId}">
            <option value="">選択してください</option>
            <option th:each="department : ${departments}"
                    th:value="${department.deptId}"
                    th:text="${department.deptName}"></option>
          </select>
        </td>
      </tr>
      <tr>
        <th>住所(都道府県)</th>
        <td>
          <input type="text" th:field="*{address}">
        </td>
      </tr>
    </table>
    <button type="submit">確認</button>
  </form>

  <p><a th:href="@{/employees}">一覧へ戻る</a></p>
</body>
</html>

確認画面(confirm.html)

src/main/resources/templates/employee/update/confirm.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員変更(確認)</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
  <h1>社員変更(確認)</h1>
  <p>次の内容に変更します。よろしければ「変更」ボタンを押してください。</p>

  <table th:object="${updateForm}">
    <tr><th>社員ID</th><td th:text="*{empId}"></td></tr>
    <tr><th>氏名</th><td th:text="*{name}"></td></tr>
    <tr><th>性別</th><td th:switch="*{gender}"><span th:case="1">男</span><span th:case="2">女</span></td></tr>
    <tr><th>年齢</th><td th:text="*{age}"></td></tr>
    <tr><th>生年月日</th><td th:text="*{#dates.format(birthDate, 'yyyy/MM/dd')}"></td></tr>
    <tr><th>部署</th><td th:text="${department?.deptName}"></td></tr>
    <tr><th>住所(都道府県)</th><td th:text="*{address}"></td></tr>
  </table>

  <a class="button" th:href="@{/employees/{empId}/edit/back(empId=${updateForm.empId})}">戻る</a>
  <form class="inline" th:action="@{/employees/{empId}/edit/complete(empId=${updateForm.empId})}" method="post">
    <button type="submit">変更</button>
  </form>
</body>
</html>

完了画面(complete.html)

src/main/resources/templates/employee/update/complete.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>社員変更(完了)</title>
<link rel="stylesheet" th:href="@{/css/app.css}">
</head>
<body>
  <h1>社員変更(完了)</h1>
  <p>社員の変更が完了しました。</p>
  <p><a th:href="@{/employees}">一覧へ戻る</a></p>
</body>
</html>

12.5 動作確認

  1. すべてのファイルを保存します(Ctrl + Shift + S で全部保存できます)。アプリを停止してから起動し直します。
  2. Chrome で http://localhost:8080/employees を開きます。各行に「変更」ボタンが表示されていることを確認します。

ch12-list-edit-button.png
図12-2 「変更」ボタンが付いた社員一覧

  1. 雷禅 海皇(社員ID 1)の行の「変更」ボタンを押します。雷禅 海皇の今のデータが入った変更画面が表示されます。

ch12-update-input.png
図12-3 変更画面(今のデータが入っている)

  1. 年齢を 27、住所を 神奈川県 に書き換えて、「確認」ボタンを押します。

ch12-update-confirm.png
図12-4 確認画面

  1. 「戻る」ボタンを押すと、書き換えた内容のまま変更画面に戻ることも確かめましょう。確かめたら、もう一度「確認」を押します。
  2. 確認画面で「変更」ボタンを押します。完了画面が表示されます。

ch12-update-complete.png
図12-5 完了画面

  1. 「一覧へ戻る」を押します。雷禅 海皇の年齢が 27、住所が神奈川県に変わっていれば成功です。

ch12-list-after-update.png
図12-6 変更後の社員一覧

Eclipse のコンソールを見ると、「変更」ボタンを押したときに、次のような UPDATE 文が実行されていることがわかります(表示は環境によって少し異なります)。

コンソール(抜粋)
Hibernate:
    update
        employee
    set
        address=?,
        age=?,
        birth_date=?,
        dept_id=?,
        gender=?,
        name=?
    where
        emp_id=?

12.6 解説:変更の流れ

変更画面を表示する(input メソッド)

変更画面では、最初からその社員のデータを入力欄に表示する必要があります。そこで、input メソッドでは次の順に処理しています。

  1. employeeRepository.findByEmpId(empId) で、データベースからその社員の Entity を取り出す
  2. 新しい UpdateForm を作り、Entity の値を1つずつフォームに詰め替える
  3. フォームを updateForm という名前で Model に入れる(@SessionAttributes により、セッションにも保存される)

詰め替えで1つだけ注意が必要なのが部署です。Entity の Employee は部署を Department オブジェクトとして持っていますが(第6章)、フォームの UpdateForm は部署ID(deptId)を持っています。そのため、employee.getDepartment().getDeptId() で部署ID を取り出してフォームに入れています。こうしておくと、プルダウンで今の部署が選ばれた状態で表示されます。

URL に社員ID を入れる

変更の画面では、どの社員を変更しているのかを URL で表します。

書き方 意味
@GetMapping("/employees/{empId}/edit") URL の {empId} の部分は、どんな値でも受け付ける。/employees/1/edit も /employees/2/edit もこのメソッドが処理する
@PathVariable Integer empId URL の {empId} の部分の値を、引数 empId で受け取る。引数の名前 empId から、URL のどの部分かが決まる。/employees/1/edit なら 1 が入る
@{/employees/{empId}/edit/confirm(empId=${updateForm.empId})} テンプレートで URL を組み立てる。{empId} の部分に、フォームの社員ID の値が入る

@PathVariable と、テンプレートでの URL の組み立て方は、第9章の削除でも使いました。変更では、変更画面・確認画面・戻る・完了のすべての URL に社員ID を入れて、どの社員の変更かが URL で分かるようにしています。完了画面を表示する /employees/edit/done だけは、変更が終わって社員ID が不要なので入れていません。

社員ID は表示するだけ

変更画面の社員ID は、th:text="*{empId}" で 表示しているだけ で、入力欄にはしていません。社員ID は主キーなので、変えてしまうと別の社員のデータになってしまうからです。

データを更新する(complete メソッド)

確認画面で「変更」ボタンを押すと、complete メソッドが動きます。

  1. findByEmpId で、データベースから 今の 社員のデータを取り出し直す
  2. 取り出した Entity の値を、フォームの値で上書きする
  3. employeeRepository.save(employee) で保存する
  4. sessionStatus.setComplete() でセッションの入力内容を破棄し、完了画面にリダイレクトする

第7章で学んだとおり、save メソッドは 主キーが入っていない Entity なら INSERT、主キーが入っている Entity なら UPDATE を実行します。データベースから取り出した Entity には社員ID(主キー)が入っているので、UPDATE になります。新規登録(第11章)と同じ save メソッドで、登録にも更新にも使えるのです。

変更が終わったあと、ブラウザの「戻る」で確認画面に戻って、もう一度「変更」を押すと、Whitelabel エラー画面になります。この教材では、この操作への対処はしていません。

12.7 よくあるエラー

症状 主な原因と対処
Whitelabel エラー画面(400 Bad Request) URL の社員ID の部分が数字になっていません。@{...} の書き方を確認してください
Whitelabel エラー画面(404 Not Found) @GetMapping/@PostMapping の URL と、テンプレートの送信先 URL が一致しているか確認してください
変更画面で部署のプルダウンが「選択してください」になっている input メソッドで updateForm.setDeptId(employee.getDepartment().getDeptId()) を書き忘れていないか確認してください

12.8 まとめと確認問題

まとめ

  • 変更画面では、findByEmpId で取り出した Entity の値をフォームに詰め替えて表示する
  • どの社員を変更するかは、URL の {empId} で表し、@PathVariable で受け取る
  • save は、主キーが入っている Entity なら UPDATE を実行する
  • 変更の保存時は、データベースから社員を取り出し直して値を上書きする

確認問題

問1 変更画面を開いたとき、入力欄に今のデータが入っているのはなぜですか。

解答を見る

input メソッドで、findByEmpId でデータベースから取り出した社員の Entity の値を UpdateForm に詰め替え、そのフォームを Model に入れているからです。th:field は、フォームに入っている値を入力欄に表示します。

問2 @GetMapping("/employees/{empId}/edit") のメソッドに /employees/4/edit でアクセスしたとき、@PathVariable Integer empId には何が入りますか。

解答を見る

4 が入ります。URL の {empId} の部分の値が、引数に渡されます。

問3 新規登録でも変更でも、同じ save メソッドを使っています。INSERT になるか UPDATE になるかは、何で決まりますか。

解答を見る

Entity に主キー(社員ID)が入っているかどうかで決まります。入っていなければ INSERT、入っていれば UPDATE になります。

前へ

第11章 社員新規登録② 確認画面と完了画面

次へ

第13章 入力チェック① 新規登録

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?