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-19

社員一覧の各行に「変更」ボタンを付け、その社員のデータが入った変更画面 → 確認画面 → 完了画面の流れを作ります。仕組みは第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(更新)

【知識メモ】MyBatis コースの場合

MyBatis の Mapper には save メソッドがありません。新規登録では insert メソッド(INSERT)、この章の変更では update メソッド(UPDATE)を呼びます。どちらも、第7章 MyBatis 入門②で XML に書いた SQL が実行されます。

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 で解説)

【知識メモ】MyBatis コースの場合

complete メソッドの最後で、save の代わりに update を呼びます。読み替え(employeeRepository → employeeMapper、departmentRepository → departmentMapper)に加えて、次の1行が違います。

EmployeeUpdateController.java(MyBatis コースで書き換える行)
        employeeMapper.update(employee);

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

@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=?

【知識メモ】MyBatis コースの場合

MyBatis コースでは、次のように表示されます(行の先頭の日時などは省略しています)。社員と部署を取り出し直す SELECT のあとに、XML に書いた UPDATE 文が実行されます。

コンソール(MyBatis コースの例。抜粋)
c.e.e.mapper.EmployeeMapper.update       : ==>  Preparing: UPDATE EMPLOYEE SET NAME = ?, GENDER = ?, AGE = ?, BIRTH_DATE = ?, DEPT_ID = ?, ADDRESS = ? WHERE EMP_ID = ?
c.e.e.mapper.EmployeeMapper.update       : ==> Parameters: 雷禅 海皇(String), 1(Integer), 27(Integer), 2000-06-22 00:00:00.0(Timestamp), 2(Integer), 神奈川県(String), 1(Integer)
c.e.e.mapper.EmployeeMapper.update       : <==    Updates: 1

Updates: 1 は、更新した行の数です。? には、Parameters: の値が順に入ります。最後の 1(Integer) が、WHERE EMP_ID = ? の社員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 メソッドで、登録にも更新にも使えるのです。

【知識メモ】MyBatis コースの場合

MyBatis コースでは、INSERT にするか UPDATE にするかを、呼ぶメソッドで自分で決めます。employeeMapper.update(employee) を呼ぶと、第7章 MyBatis 入門②で書いた次の SQL が実行されます。

EmployeeMapper.xml(update の部分)
<update id="update">
    UPDATE EMPLOYEE
    SET NAME = #{name},
        GENDER = #{gender},
        AGE = #{age},
        BIRTH_DATE = #{birthDate},
        DEPT_ID = #{department.deptId},
        ADDRESS = #{address}
    WHERE EMP_ID = #{empId}
</update>

#{empId} には、データベースから取り出し直した社員の社員IDが入るので、その社員の行だけが更新されます。新規登録(第11章)では、insert メソッドで INSERT 文を実行しました。

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

12.7 よくあるエラー

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

その他のエラーは、付録B「画面表示時のエラー」 も参考にしてください。

12.8 まとめと確認問題

まとめ

  • 変更画面では、findByEmpId で取り出した Entity の値をフォームに詰め替えて表示する
  • どの社員を変更するかは、URL の {empId} で表し、@PathVariable で受け取る
  • save は、主キーが入っている Entity なら UPDATE を実行する(MyBatis コースでは update メソッドで 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 になります。MyBatis コースでは save を使わないので、新規登録では 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?