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入門】付録A 完成版ソースコード

0
Last updated at Posted at 2026-10-06

第15章までを終えたときの、社員管理アプリのすべてのファイルです。うまく動かないときは、自分のコードと見比べてみましょう。

【知識メモ】このソースコードについて

ここに掲載したコードは、実際に Oracle Database 21c XE に接続して、すべての機能が動くことを確認したものです。同じファイルは、この教材を打ち込んだフォルダーの sample/employee-app にも入っているはずです。

A.0 フォルダーの構成

employee-app
employee-app
├─ src/main/java
│   └─ com.example.employeeapp
│       ├─ EmployeeAppApplication.java
│       ├─ component
│       │   └─ GreetingMessage.java(練習用)
│       ├─ controller
│       │   ├─ EmployeeListController.java
│       │   ├─ EmployeeRegisterController.java
│       │   ├─ EmployeeUpdateController.java
│       │   └─ HelloController.java(練習用)
│       ├─ entity
│       │   ├─ Department.java
│       │   └─ Employee.java
│       ├─ form
│       │   ├─ RegisterForm.java
│       │   └─ UpdateForm.java
│       └─ repository
│           ├─ DepartmentRepository.java
│           └─ EmployeeRepository.java
├─ src/main/resources
│   ├─ application.properties
│   ├─ messages.properties
│   ├─ static
│   │   └─ css
│   │       └─ app.css
│   └─ templates
│       ├─ hello.html(練習用)
│       └─ employee
│           ├─ list.html
│           ├─ register
│           │   ├─ input.html
│           │   ├─ confirm.html
│           │   └─ complete.html
│           └─ update
│               ├─ input.html
│               ├─ confirm.html
│               └─ complete.html
├─ src/test/java
│   └─ com.example.employeeapp
│       ├─ EmployeeAppApplicationTests.java(Eclipse が作成)
│       └─ RepositoryCheckTest.java
└─ pom.xml

A.1 データベース(SQL)

第3章で SQL Developer から実行した SQL です。

ワークシート(XE_SYSTEM)
-- アプリ専用のユーザー empapp を作成する
CREATE USER empapp IDENTIFIED BY empapp;

-- empapp にすべての権限を与える(学習用)
GRANT ALL PRIVILEGES TO empapp;
ワークシート(XE_EMPAPP)
-- 部署IDの番号を振るシーケンス
CREATE SEQUENCE SEQ_DEPT NOCACHE;

-- 社員IDの番号を振るシーケンス
CREATE SEQUENCE SEQ_EMP NOCACHE;

-- 部署テーブル
CREATE TABLE DEPARTMENT (
  DEPT_ID    NUMBER(4)  CONSTRAINT PK_DEPARTMENT PRIMARY KEY,
  DEPT_NAME  VARCHAR2(20 CHAR)  NOT NULL
);

-- 社員テーブル
CREATE TABLE EMPLOYEE (
  EMP_ID      NUMBER(6)  CONSTRAINT PK_EMPLOYEE PRIMARY KEY,
  NAME        VARCHAR2(20 CHAR)  NOT NULL,
  GENDER      NUMBER(1)  NOT NULL,
  AGE         NUMBER(3)  NOT NULL,
  BIRTH_DATE  DATE       NOT NULL,
  DEPT_ID     NUMBER(4)  NOT NULL
    CONSTRAINT FK_EMPLOYEE_DEPARTMENT REFERENCES DEPARTMENT (DEPT_ID),
  ADDRESS     VARCHAR2(10 CHAR)  NOT NULL
);

-- 部署(DEPT_ID は SEQ_DEPT から 1, 2, 3 と振られる)
INSERT INTO DEPARTMENT (DEPT_ID, DEPT_NAME) VALUES (SEQ_DEPT.NEXTVAL, '総務部');
INSERT INTO DEPARTMENT (DEPT_ID, DEPT_NAME) VALUES (SEQ_DEPT.NEXTVAL, '営業部');
INSERT INTO DEPARTMENT (DEPT_ID, DEPT_NAME) VALUES (SEQ_DEPT.NEXTVAL, '開発部');

-- 社員(EMP_ID は SEQ_EMP から 1, 2, 3 と振られる)
INSERT INTO EMPLOYEE (EMP_ID, NAME, GENDER, AGE, BIRTH_DATE, DEPT_ID, ADDRESS)
  VALUES (SEQ_EMP.NEXTVAL, '雷禅 海皇', 1, 26, DATE '2000-06-22', 2, '大阪府');
INSERT INTO EMPLOYEE (EMP_ID, NAME, GENDER, AGE, BIRTH_DATE, DEPT_ID, ADDRESS)
  VALUES (SEQ_EMP.NEXTVAL, '院照 心愛', 2, 27, DATE '1999-07-11', 1, '京都府');
INSERT INTO EMPLOYEE (EMP_ID, NAME, GENDER, AGE, BIRTH_DATE, DEPT_ID, ADDRESS)
  VALUES (SEQ_EMP.NEXTVAL, '林檎 星飛', 1, 24, DATE '2001-12-24', 3, '東京都');

-- 登録を確定する
COMMIT;

A.2 設定ファイル

ファイル 役割 作る章
application.properties データベースの接続設定など 第4章
messages.properties 入力チェックのエラーメッセージ 第13章
pom.xml プロジェクトの設定と依存関係(Eclipse が作成。必要に応じて編集することもあるが、この教材では編集しない) 第4章

application.properties

src/main/resources/application.properties
spring.application.name=employee-app
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
spring.datasource.username=empapp
spring.datasource.password=empapp
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.open-in-view=false
spring.thymeleaf.cache=false
server.servlet.session.tracking-modes=cookie

messages.properties

src/main/resources/messages.properties
# 入力チェックのエラーメッセージ
name.notBlank=氏名を入力してください。
name.size=氏名は20文字以内で入力してください。
gender.notNull=性別を選択してください。
age.notNull=年齢を入力してください。
age.range=年齢は15〜99の範囲で入力してください。
birthDate.notNull=生年月日を入力してください。
birthDate.past=生年月日には過去の日付を入力してください。
deptId.notNull=部署を選択してください。
address.notBlank=住所(都道府県)を入力してください。
address.size=住所(都道府県)は10文字以内で入力してください。

# 入力された値の形式が正しくないとき(数値の欄に文字を入力したときなど)のエラーメッセージ
typeMismatch.age=年齢は半角数字で入力してください。
typeMismatch.birthDate=生年月日を正しく入力してください。

pom.xml

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>4.1.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>employee-app</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>employee-app</name>
	<description/>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webmvc</artifactId>
		</dependency>

		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc17</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webmvc-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

A.3 Entity

ファイル 役割 作る章
Department.java 部署テーブルに対応するクラス 第6章
Employee.java 社員テーブルに対応するクラス 第6章

Department.java

src/main/java/com/example/employeeapp/entity/Department.java
package com.example.employeeapp.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;

@Entity
@Table(name = "DEPARTMENT")
public class Department {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_DEPT")
    @SequenceGenerator(name = "SEQ_DEPT", sequenceName = "SEQ_DEPT", allocationSize = 1)
    private Integer deptId;

    @Column
    private String deptName;

    public Integer getDeptId() {
        return deptId;
    }

    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
}

Employee.java

src/main/java/com/example/employeeapp/entity/Employee.java
package com.example.employeeapp.entity;

import java.util.Date;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;

@Entity
@Table(name = "EMPLOYEE")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_EMP")
    @SequenceGenerator(name = "SEQ_EMP", sequenceName = "SEQ_EMP", allocationSize = 1)
    private Integer empId;

    @Column
    private String name;

    @Column
    private Integer gender;

    @Column
    private Integer age;

    @Column
    private Date birthDate;

    @ManyToOne
    @JoinColumn(name = "DEPT_ID", referencedColumnName = "deptId")
    private Department department;

    @Column
    private String address;

    public Integer getEmpId() {
        return empId;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

A.4 Repository

ファイル 役割 作る章
DepartmentRepository.java 部署テーブルを操作する 第7章
EmployeeRepository.java 社員テーブルを操作する 第7章

DepartmentRepository.java

src/main/java/com/example/employeeapp/repository/DepartmentRepository.java
package com.example.employeeapp.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.employeeapp.entity.Department;

@Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {

    List<Department> findAllByOrderByDeptIdAsc();

    Department findByDeptId(Integer deptId);
}

EmployeeRepository.java

src/main/java/com/example/employeeapp/repository/EmployeeRepository.java
package com.example.employeeapp.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.employeeapp.entity.Employee;

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {

    List<Employee> findAllByOrderByEmpIdAsc();

    Employee findByEmpId(Integer empId);
}

A.5 フォーム

ファイル 役割 作る章
RegisterForm.java 新規登録の入力内容(入力チェック付き) 第10章
UpdateForm.java 変更の入力内容(RegisterForm に社員IDを足したもの) 第12章

RegisterForm.java

src/main/java/com/example/employeeapp/form/RegisterForm.java
package com.example.employeeapp.form;

import java.io.Serializable;
import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;

public class RegisterForm implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotBlank(message = "{name.notBlank}")
    @Size(max = 20, message = "{name.size}")
    private String name;

    @NotNull(message = "{gender.notNull}")
    private Integer gender;

    @NotNull(message = "{age.notNull}")
    @Min(value = 15, message = "{age.range}")
    @Max(value = 99, message = "{age.range}")
    private Integer age;

    @NotNull(message = "{birthDate.notNull}")
    @Past(message = "{birthDate.past}")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthDate;

    @NotNull(message = "{deptId.notNull}")
    private Integer deptId;

    @NotBlank(message = "{address.notBlank}")
    @Size(max = 10, message = "{address.size}")
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }

    public Integer getDeptId() {
        return deptId;
    }

    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

UpdateForm.java

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

A.6 Controller

ファイル 役割 作る章
EmployeeListController.java 一覧・削除 第8章
EmployeeRegisterController.java 新規登録 第10章
EmployeeUpdateController.java 変更 第12章

EmployeeListController.java

src/main/java/com/example/employeeapp/controller/EmployeeListController.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.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

import com.example.employeeapp.entity.Employee;
import com.example.employeeapp.repository.EmployeeRepository;

@Controller
public class EmployeeListController {

    @Autowired
    private EmployeeRepository employeeRepository;

    /** 社員一覧画面を表示
     * 社員IDの順に社員を取り出して表示
     * @param model 表示する社員情報
     * @return 社員一覧画面 */
    @GetMapping("/employees")
    public String list(Model model) {
        List<Employee> employees = employeeRepository.findAllByOrderByEmpIdAsc();
        model.addAttribute("employees", employees);
        return "employee/list";
    }

    /** 社員を削除して社員一覧画面に戻る
     * @param empId 削除する社員の社員ID
     * @return 社員一覧画面 */
    @PostMapping("/employees/{empId}/delete")
    public String delete(@PathVariable Integer empId) {
        employeeRepository.deleteById(empId);
        return "redirect:/employees";
    }
}

EmployeeRegisterController.java

src/main/java/com/example/employeeapp/controller/EmployeeRegisterController.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.validation.BindingResult;
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.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.RegisterForm;
import com.example.employeeapp.repository.DepartmentRepository;
import com.example.employeeapp.repository.EmployeeRepository;

@Controller
@SessionAttributes("registerForm")
public class EmployeeRegisterController {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

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

    /** 社員登録の入力画面を表示
     * @param model 表示する入力フォームと部署一覧
     * @return 社員登録の入力画面 */
    @GetMapping("/employees/register")
    public String input(Model model) {
        model.addAttribute("registerForm", new RegisterForm());
        List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
        model.addAttribute("departments", departments);
        return "employee/register/input";
    }

    /** 社員登録の確認画面を表示
     * @param registerForm 入力された社員情報
     * @param bindingResult 入力チェックの結果
     * @param model 表示する部署情報
     * @return 正常:社員登録の確認画面
     *         エラー:社員登録の入力画面 */
    @PostMapping("/employees/register/confirm")
    public String confirm(@Validated @ModelAttribute RegisterForm registerForm,
            BindingResult bindingResult, Model model) {
        // 入力チェックでエラーがあれば、エラーメッセージを付けて入力画面に戻す
        if (bindingResult.hasErrors()) {
            List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
            model.addAttribute("departments", departments);
            return "employee/register/input";
        }
        Department department = departmentRepository.findByDeptId(registerForm.getDeptId());
        model.addAttribute("department", department);
        return "employee/register/confirm";
    }

    /** 社員登録の確認画面から入力画面に戻る
     * @param model 表示する部署一覧
     * @return 社員登録の入力画面 */
    @GetMapping("/employees/register/back")
    public String back(Model model) {
        List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
        model.addAttribute("departments", departments);
        return "employee/register/input";
    }

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

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

    /** 社員登録の完了画面を表示
     * @return 社員登録の完了画面 */
    @GetMapping("/employees/register/done")
    public String done() {
        return "employee/register/complete";
    }
}

EmployeeUpdateController.java

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.validation.BindingResult;
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.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 bindingResult 入力チェックの結果
     * @param model 表示する部署情報
     * @return 正常:社員変更の確認画面
     *         エラー:社員変更の入力画面 */
    @PostMapping("/employees/{empId}/edit/confirm")
    public String confirm(@Validated @ModelAttribute UpdateForm updateForm,
            BindingResult bindingResult, Model model) {
        // 入力チェックでエラーがあれば、エラーメッセージを付けて入力画面に戻す
        if (bindingResult.hasErrors()) {
            List<Department> departments = departmentRepository.findAllByOrderByDeptIdAsc();
            model.addAttribute("departments", departments);
            return "employee/update/input";
        }
        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";
    }
}

A.7 テンプレート(画面)

ファイル 役割 作る章
list.html 社員一覧 第8章
input.html 新規登録:入力 第10章
confirm.html 新規登録:確認 第11章
complete.html 新規登録:完了 第11章
input.html 変更:入力 第12章
confirm.html 変更:確認 第12章
complete.html 変更:完了 第12章

list.html

src/main/resources/templates/employee/list.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><a class="button" th:href="@{/employees/register}">新規登録</a></p>

  <table>
    <tr>
      <th>社員ID</th>
      <th>氏名</th>
      <th>性別</th>
      <th>年齢</th>
      <th>生年月日</th>
      <th>部署</th>
      <th>住所</th>
      <th>操作</th>
    </tr>
    <tr th:each="employee : ${employees}">
      <td th:text="${employee.empId}"></td>
      <td th:text="${employee.name}"></td>
      <td th:switch="${employee.gender}">
        <span th:case="1">男</span>
        <span th:case="2">女</span>
      </td>
      <td th:text="${employee.age}"></td>
      <td th:text="${#dates.format(employee.birthDate, 'yyyy/MM/dd')}"></td>
      <td th:text="${employee.department.deptName}"></td>
      <td th:text="${employee.address}"></td>
      <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>
    </tr>
  </table>
</body>
</html>

input.html

src/main/resources/templates/employee/register/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/register/confirm}" th:object="${registerForm}" method="post">
    <table>
      <tr>
        <th>氏名</th>
        <td>
          <input type="text" th:field="*{name}">
          <div class="error" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
        </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>
          <div class="error" th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}"></div>
        </td>
      </tr>
      <tr>
        <th>年齢</th>
        <td>
          <input type="text" th:field="*{age}">
          <div class="error" th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></div>
        </td>
      </tr>
      <tr>
        <th>生年月日</th>
        <td>
          <input type="date" th:field="*{birthDate}">
          <div class="error" th:if="${#fields.hasErrors('birthDate')}" th:errors="*{birthDate}"></div>
        </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>
          <div class="error" th:if="${#fields.hasErrors('deptId')}" th:errors="*{deptId}"></div>
        </td>
      </tr>
      <tr>
        <th>住所(都道府県)</th>
        <td>
          <input type="text" th:field="*{address}">
          <div class="error" th:if="${#fields.hasErrors('address')}" th:errors="*{address}"></div>
        </td>
      </tr>
    </table>
    <button type="submit">確認</button>
  </form>

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

confirm.html

src/main/resources/templates/employee/register/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="${registerForm}">
    <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/register/back}">戻る</a>
  <form class="inline" th:action="@{/employees/register/complete}" method="post">
    <button type="submit">登録</button>
  </form>
</body>
</html>

complete.html

src/main/resources/templates/employee/register/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>

input.html

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}">
          <div class="error" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
        </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>
          <div class="error" th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}"></div>
        </td>
      </tr>
      <tr>
        <th>年齢</th>
        <td>
          <input type="text" th:field="*{age}">
          <div class="error" th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></div>
        </td>
      </tr>
      <tr>
        <th>生年月日</th>
        <td>
          <input type="date" th:field="*{birthDate}">
          <div class="error" th:if="${#fields.hasErrors('birthDate')}" th:errors="*{birthDate}"></div>
        </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>
          <div class="error" th:if="${#fields.hasErrors('deptId')}" th:errors="*{deptId}"></div>
        </td>
      </tr>
      <tr>
        <th>住所(都道府県)</th>
        <td>
          <input type="text" th:field="*{address}">
          <div class="error" th:if="${#fields.hasErrors('address')}" th:errors="*{address}"></div>
        </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>

A.8 CSS

ファイル 役割 作る章
app.css 画面の見た目 第8章

app.css

src/main/resources/static/css/app.css
body { /* ページ全体 */
    margin: 24px;
    color: #222;
}

h1 { /* 見出し */
    font-size: 24px;
}

table { /* 表 */
    border-collapse: collapse;
    margin-bottom: 16px;
}

th, td { /* 表のマス */
    border: 1px solid #999;
    padding: 6px 12px;
    text-align: left;
}

th { /* 表の見出しのマス */
    background-color: #eee;
}

a.button { /* リンクをボタンの見た目にする(新規登録・変更・戻るボタン用) */
    display: inline-block;
    padding: 1px 6px;
    border: 1px solid #767676;
    border-radius: 3px;
    background-color: #efefef;
    color: #000;
    font-size: 13px;
    text-decoration: none;
}

form.inline { /* フォームを横に並べる(削除ボタン用) */
    display: inline;
}

.error { /* 入力チェックのエラーメッセージ */
    color: #c00;
    font-size: 14px;
}

A.9 その他(練習・起動用)

ファイル 役割 作る章
EmployeeAppApplication.java アプリを起動する main メソッド(Eclipse が作成) 第4章
HelloController.java Hello World(練習用) 第4章
GreetingMessage.java DI の練習用 第5章
hello.html Hello World の画面(練習用) 第4章
RepositoryCheckTest.java Repository の動作確認テスト 第7章

EmployeeAppApplication.java

src/main/java/com/example/employeeapp/EmployeeAppApplication.java
package com.example.employeeapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmployeeAppApplication {

	public static void main(String[] args) {
		SpringApplication.run(EmployeeAppApplication.class, args);
	}

}

HelloController.java

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

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 com.example.employeeapp.component.GreetingMessage;

@Controller
public class HelloController {

    @Autowired
    private GreetingMessage greetingMessage;

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("message", greetingMessage.getMessage());
        return "hello";
    }
}

GreetingMessage.java

src/main/java/com/example/employeeapp/component/GreetingMessage.java
package com.example.employeeapp.component;

import org.springframework.stereotype.Component;

@Component
public class GreetingMessage {

    public String getMessage() {
        return "DI で受け取ったメッセージです";
    }
}

hello.html

src/main/resources/templates/hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Hello</title>
</head>
<body>
  <h1>はじめての Spring Boot</h1>
  <p th:text="${message}">ここにメッセージが入ります</p>
</body>
</html>

RepositoryCheckTest.java

src/test/java/com/example/employeeapp/RepositoryCheckTest.java
package com.example.employeeapp;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

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

@SpringBootTest
class RepositoryCheckTest {

    @Autowired
    private EmployeeRepository employeeRepository;

    @Autowired
    private DepartmentRepository departmentRepository;

    /** 全社員を取得できること */
    @Test
    void findAllEmployees() {
        List<Employee> list = employeeRepository.findAllByOrderByEmpIdAsc();
        for (Employee employee : list) {
            System.out.println(employee.getEmpId() + " " + employee.getName() + " "
                    + employee.getDepartment().getDeptName());
        }
        assertEquals(3, list.size());
    }

    /** 社員IDを指定して社員を取得できること */
    @Test
    void findEmployee() {
        Employee employee = employeeRepository.findByEmpId(1);
        assertEquals("雷禅 海皇", employee.getName());
    }

    /** 全部署を取得できること */
    @Test
    void findAllDepartments() {
        List<Department> list = departmentRepository.findAllByOrderByDeptIdAsc();
        assertEquals(3, list.size());
    }

}

前へ

第15章 まとめと次のステップ

次へ

付録B よくあるエラーと対処法

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?