この章は総まとめです。これまでに学んだことを使って、Spring入門で作る社員管理アプリの元になるクラスを作ります。部署と社員の Entity、社員を保存して一覧・検索・登録・変更・削除を行う Repository、それらを使う Main の4つです。Department と Employee は、Spring入門の Entity と同じ作りにします。
この章で学ぶこと
- Spring入門と同じ作りの Entity クラス(
Department・Employee) -
Listで社員を保存するEmployeeRepositoryクラス - 一覧・検索・登録・変更・削除の処理を Java だけで書く
- Spring入門へ進む準備
14.1 この章で作るもの
パッケージは、Spring入門と同じように役割ごとに分けます。章の最後には、Main を実行するとコンソールに次のように表示されるようになります。
■ 社員一覧
1 雷禅 海皇 男 26歳 営業部 大阪府
2 院照 心愛 女 27歳 総務部 京都府
3 林檎 星飛 男 24歳 開発部 東京都
■ 社員ID 3 を削除
1 雷禅 海皇 男 26歳 営業部 大阪府
2 院照 心愛 女 27歳 総務部 京都府
■ 田中 美咲 を登録
1 雷禅 海皇 男 26歳 営業部 大阪府
2 院照 心愛 女 27歳 総務部 京都府
4 田中 美咲 女 24歳 開発部 愛知県
■ 社員ID 1 を変更
1 雷禅 海皇 男 27歳 営業部 神奈川県
2 院照 心愛 女 27歳 総務部 京都府
4 田中 美咲 女 24歳 開発部 愛知県
■ 社員ID 99 を検索
見つかりません
| ファイル | 作業 | 内容 | Spring入門での対応 |
|---|---|---|---|
ch14/entity/Department.java |
新規 | 部署を表す Entity |
entity/Department.java(第6章) |
ch14/entity/Employee.java |
新規 | 社員を表す Entity |
entity/Employee.java(第6章) |
ch14/repository/EmployeeRepository.java |
新規 | 社員を保存し、一覧・検索・登録・変更・削除を行う |
repository/EmployeeRepository.java(第7章。Spring入門ではインターフェースで、中身は Spring が用意する) |
ch14/Main.java |
新規 | これらを使って社員管理の操作を行う | Controller(第8〜12章) |
14.2 Entity:Department と Employee
Entity(エンティティ)は、データ1件を表すクラスです。Spring入門では、データベースの表の1行に対応します。「private のフィールド + getter と setter」だけの、第9章で学んだ形です。コンストラクタは書きません(第8章 8.5)。
-
srcを右クリックし、新規 → パッケージ で、ch14.entity・ch14.repository・ch14の3つのパッケージを順に作ります。 -
ch14.entityパッケージを右クリックし、新規 → クラス で、名前Department、mainの チェックを入れずに クラスを作り、緑の行を入力します(getter と setter は ソース → getter および setter の生成 で作れます)。ch14/entity/Department.javapackage ch14.entity; /** 部署を表す */ public class Department { private Integer deptId; 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; } } -
同じく
ch14.entityパッケージに、名前Employeeのクラスを作り、緑の行を入力します。Dateは補完でjava.utilのものを選びます。ch14/entity/Employee.javapackage ch14.entity; import java.util.Date; /** 社員を表す */ public class Employee { private Integer empId; private String name; private Integer gender; private Integer age; private Date birthDate; private Department department; 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; } } -
2つのファイルを保存します。
Employee の department フィールドの型は、自分で作った Department クラスです。社員は「部署IDの数」ではなく「部署のオブジェクト」を持ちます。employee.getDepartment().getDeptName() と続けて呼べば(第13章 13.5)、社員の部署名を取り出せます。性別は、1 が男、2 が女の数で持ちます(Spring入門と同じ)。
14.3 Repository:EmployeeRepository
Repository(リポジトリ)は、データを保存して、取り出したり消したりするクラスです。Spring入門ではデータベースに保存しますが、ここでは List に保存します。メソッドの名前は、Spring入門の EmployeeRepository と同じにします。
-
ch14.repositoryパッケージを右クリックし、新規 → クラス で、名前EmployeeRepository、mainの チェックを入れずに クラスを作り、緑の行を入力します。Employee・List・ArrayListは補完で入力し、importを自動で入れます。ch14/repository/EmployeeRepository.javapackage ch14.repository; import java.util.ArrayList; import java.util.List; import ch14.entity.Employee; /** 社員を保存し、一覧・検索・登録・変更・削除を行う */ public class EmployeeRepository { /** 保存した社員。登録した順(=社員IDの順)に並ぶ */ private List<Employee> employees = new ArrayList<>(); /** 次に登録する社員に付ける社員ID */ private int nextEmpId = 1; /** 社員一覧画面に出すために、全社員を社員IDの順に返す * @return 社員の一覧 */ public List<Employee> findAllByOrderByEmpIdAsc() { return employees; } /** 変更や削除の前に、社員IDで社員を探す * @param empId 探す社員ID * @return 見つかった社員。見つからなければ null */ public Employee findByEmpId(Integer empId) { for (Employee employee : employees) { if (employee.getEmpId().equals(empId)) { return employee; } } return null; } /** 社員を保存する。社員IDが無ければ新規登録として採番して追加し、あれば変更として何もしない(同じオブジェクトを保存しているため) * @param employee 保存する社員 */ public void save(Employee employee) { if (employee.getEmpId() == null) { employee.setEmpId(nextEmpId); nextEmpId++; employees.add(employee); } } /** 社員IDの社員を削除する。見つからなければ何もしない * @param empId 削除する社員ID */ public void deleteById(Integer empId) { Employee employee = findByEmpId(empId); if (employee != null) { employees.remove(employee); } } } -
保存します。
14.4 Main:一覧・削除・登録・変更・検索
Spring入門で第8章から第12章にかけて作る操作(一覧・削除・登録・変更)を、同じ順番で行います。
-
ch14パッケージを右クリックし、新規 → クラス で、名前Main、mainに チェックを入れて クラスを作り、緑の行を入力します。Department・Employee・EmployeeRepository・Listは補完で入力します。ch14/Main.javapackage ch14; import java.util.List; import ch14.entity.Department; import ch14.entity.Employee; import ch14.repository.EmployeeRepository; /** 社員管理の操作(一覧・削除・登録・変更・検索)を順に行う */ public class Main { public static void main(String[] args) { EmployeeRepository employeeRepository = new EmployeeRepository(); Department sales = createDepartment(1, "営業部"); Department general = createDepartment(2, "総務部"); Department dev = createDepartment(3, "開発部"); // サンプルデータの3人を登録する employeeRepository.save(createEmployee("雷禅 海皇", 1, 26, sales, "大阪府")); employeeRepository.save(createEmployee("院照 心愛", 2, 27, general, "京都府")); employeeRepository.save(createEmployee("林檎 星飛", 1, 24, dev, "東京都")); System.out.println("■ 社員一覧"); printList(employeeRepository.findAllByOrderByEmpIdAsc()); // 削除 employeeRepository.deleteById(3); System.out.println("■ 社員ID 3 を削除"); printList(employeeRepository.findAllByOrderByEmpIdAsc()); // 新規登録 employeeRepository.save(createEmployee("田中 美咲", 2, 24, dev, "愛知県")); System.out.println("■ 田中 美咲 を登録"); printList(employeeRepository.findAllByOrderByEmpIdAsc()); // 変更 Employee employee = employeeRepository.findByEmpId(1); employee.setAge(27); employee.setAddress("神奈川県"); employeeRepository.save(employee); System.out.println("■ 社員ID 1 を変更"); printList(employeeRepository.findAllByOrderByEmpIdAsc()); // 存在しない社員IDの検索 System.out.println("■ 社員ID 99 を検索"); Employee notFound = employeeRepository.findByEmpId(99); if (notFound == null) { System.out.println("見つかりません"); } } /** サンプルデータを作るために、部署IDと部署名を決めて部署を作る * @param deptId 部署ID * @param deptName 部署名 * @return 作った部署 */ static Department createDepartment(Integer deptId, String deptName) { Department department = new Department(); department.setDeptId(deptId); department.setDeptName(deptName); return department; } /** 登録する社員を作る。社員IDは save で採番されるので入れない * @param name 名前 * @param gender 性別(1:男、2:女) * @param age 年齢 * @param department 部署 * @param address 住所 * @return 作った社員 */ static Employee createEmployee(String name, Integer gender, Integer age, Department department, String address) { Employee employee = new Employee(); employee.setName(name); employee.setGender(gender); employee.setAge(age); employee.setDepartment(department); employee.setAddress(address); return employee; } /** 社員一覧として、社員を1人1行で表示する * @param employees 表示する社員の一覧 */ static void printList(List<Employee> employees) { for (Employee employee : employees) { String gender; switch (employee.getGender()) { case 1: gender = "男"; break; case 2: gender = "女"; break; default: gender = "不明"; break; } System.out.println(employee.getEmpId() + " " + employee.getName() + " " + gender + " " + employee.getAge() + "歳 " + employee.getDepartment().getDeptName() + " " + employee.getAddress()); } } } -
保存して、
Main.javaを実行します。次のように表示されれば完成です(14.1 と同じ表示です)。コンソール■ 社員一覧 1 雷禅 海皇 男 26歳 営業部 大阪府 2 院照 心愛 女 27歳 総務部 京都府 3 林檎 星飛 男 24歳 開発部 東京都 ■ 社員ID 3 を削除 1 雷禅 海皇 男 26歳 営業部 大阪府 2 院照 心愛 女 27歳 総務部 京都府 ■ 田中 美咲 を登録 1 雷禅 海皇 男 26歳 営業部 大阪府 2 院照 心愛 女 27歳 総務部 京都府 4 田中 美咲 女 24歳 開発部 愛知県 ■ 社員ID 1 を変更 1 雷禅 海皇 男 27歳 営業部 神奈川県 2 院照 心愛 女 27歳 総務部 京都府 4 田中 美咲 女 24歳 開発部 愛知県 ■ 社員ID 99 を検索 見つかりません
14.5 解説
使った文法と、学んだ章
| コード | 使っていること | 章 |
|---|---|---|
private Integer empId; と getter・setter |
カプセル化、Integer(新規登録の前は null) |
第9章・第11章 |
private Department department; |
自分で作ったクラスを型にしたフィールド。参照を持つ | 第8章 |
package ch14.entity; と import ch14.entity.Employee;
|
役割ごとのパッケージと import
|
第9章 |
List<Employee> employees = new ArrayList<>(); |
リストとジェネリクス | 第11章 |
for (Employee employee : employees) |
拡張 for 文 | 第6章 |
employee.getEmpId().equals(empId) |
Integer はクラス(ラッパークラス)なので、== ではなく equals で比べる |
第5章・第8章・第11章 |
return null; と if (notFound == null)
|
見つからなかったことを null で返し、使う側で調べる |
第5章・第8章 |
if (employee.getEmpId() == null) |
社員IDの有無で、新規登録か変更かを分ける | 第5章 |
nextEmpId++ |
採番。次の社員IDを1つ進める | 第4章 |
employees.remove(employee) |
リストから要素を取り除くメソッド。この章で初めて使う | ― |
switch (employee.getGender()) |
性別の数を文字に変える | 第5章 |
static Department createDepartment(…) |
同じ処理をまとめた static なメソッド。引数と戻り値 |
第7章 |
employee.getDepartment().getDeptName() |
メソッドを続けて呼ぶ | 第13章 |
/** … @param … @return … */ |
Javadoc コメント。何のためのメソッドかを書く | 第7章 |
【知識メモ】等値比較(==)と等価比較(equals)
値が同じかどうかを比べる方法は、型によって使い分けます。
| 比べるもの | 使うもの | 比べ方 |
|---|---|---|
基本型(int・double・boolean など) |
== |
値そのものが同じか |
参照型(String・Integer などのクラスの型) |
equals() |
中身(値)が同じか |
== で比べることを 等値比較、equals() で比べることを 等価比較 といいます。
-
等値比較(
==):2つの値がまったく同じか。基本型では値そのものを比べます。参照型では参照を比べるので、「同じオブジェクトを指しているか」を比べることになります(第8章 8.6)。 -
等価比較(
equals()):2つのオブジェクトの中身が同じか。別々のオブジェクトでも、中身が同じならtrueになります。
参照型を == で比べると、どうなるかを見てみましょう。この章の employee.getEmpId().equals(empId) で比べている Integer を例にします。
int a = 1000;
int b = 1000;
System.out.println("a == b:" + (a == b));
Integer x = 1000;
Integer y = 1000;
System.out.println("x == y:" + (x == y));
System.out.println("x.equals(y):" + x.equals(y));
a == b:true
x == y:false
x.equals(y):true
int の a と b は基本型なので、== で値の 1000 どうしが比べられ、true になります。一方、Integer の x と y は、どちらも中身は 1000 ですが、別々のオブジェクトです。== は同じオブジェクトかを比べるので false になります。中身を比べる equals() なら、正しく true になります。
なお、Integer では、-128 から 127 までの小さな数なら、== で比べても true になります。Java が、よく使う小さな数のオブジェクトを使い回すためです。そのため、試しに小さな数で確かめると、== でも正しく動いているように見えてしまいます。参照型は、型や値の大きさにかかわらず equals() で比べてください。
また、equals() で中身が比べられるのは、String や Integer のように、クラスの中で equals() が中身を比べるように書かれている(オーバーライドされている)場合です。この章の Employee のように自分で作ったクラスは、equals() を書かなければ、== と同じく「同じオブジェクトか」を比べます。
変更の処理で save が何もしない理由
findByEmpId(1) が返す Employee は、リストに入っているオブジェクトそのものです(第8章の参照)。setAge(27) でそのオブジェクトを書き換えた時点で、リストの中の社員も変わっています。そのため、save は社員IDがある社員については何もしなくても、一覧に変更が表れます。
Spring入門では、データベースに保存するので、setAge(27) だけでは変わりません。employeeRepository.save(employee) を呼んで初めてデータベースが更新されます。「変更したら save を呼ぶ」という形を、この章でも同じにしてあります。
Spring入門では EmployeeRepository の中身を書かない
この章では findByEmpId や save の中身を自分で書きましたが、Spring入門では EmployeeRepository を インターフェース として定義し(第13章 13.6)、中身は Spring が用意します。メソッドの名前(findAllByOrderByEmpIdAsc、findByEmpId)から、Spring が「社員IDの順に全件取り出す」「社員IDで探す」という処理を自動で作ります。この章で中身を書いてみたことで、Spring が裏で行っていることの見当が付くはずです。
14.6 うまくいかないときは
| 症状 | 考えられる原因と対処 |
|---|---|
| 「Employee を型に解決できません」など、クラス名のエラー |
import が無いか、パッケージの名前が違います。クラス名の末尾で Ctrl + Space を押して import を入れ、ch14.entity・ch14.repository にクラスがあるか確かめてください。 |
NullPointerException が printList で出る |
createEmployee で setGender か setDepartment を呼んでいません(gender が null だと switch で、department が null だと getDeptName() で起きます)。 |
NullPointerException が employee.setAge(27) で出る |
findByEmpId(1) が null を返しています。社員ID 1 の社員が登録されているか、findByEmpId の equals の比較が正しいかを確かめてください。 |
社員IDが null と表示される |
save で setEmpId を呼んでいません。 |
| 登録した社員が一覧に出ない |
createEmployee で社員IDを入れてしまい、save が「変更」と判断して追加していません。社員IDは save だけで入れます。 |
| 田中 美咲 の社員IDが 3 になる | 削除のときに nextEmpId を戻しています。採番は進めるだけにします。 |
| 「ローカル変数 gender が初期化されていない可能性があります」 |
switch の default が無く、どの case にも当てはまらないときに gender に値が入りません。default を書いてください。 |
ほかのエラーは、付録A「よくあるコンパイルエラー」・付録A「よくある実行時のエラー」 も参考にしてください。
14.7 Spring入門へ進む準備
この教材で学んだことと、Spring入門で学ぶことの対応は次のとおりです。
| この教材 | Spring入門 |
|---|---|
Department・Employee(第14章) |
同じクラスに @Entity・@Id などのアノテーションを付けて、データベースの表と対応させる(第6章) |
EmployeeRepository クラスの中身(第14章) |
インターフェースの宣言だけを書き、中身は Spring に任せる(第7章) |
Main の一覧・削除・登録・変更・検索(第14章) |
Controller のメソッドとして書き、画面から操作する(第8〜12章) |
if による年齢の確認(第5章・第12章) |
@Min・@Max などのアノテーションによる入力チェック(第13〜14章) |
new でオブジェクトを作る(第8章) |
Repository などは new せず、@Autowired で Spring に入れてもらう(第5章) |
Validatable v1 = e1;(第10章) |
EmployeeRepository employeeRepository; のように、インターフェースの型でフィールドを持つ |
Spring入門では、Java の文法そのものは新しく出てきません。新しく学ぶのは、Spring と JPA のアノテーションの意味、画面のテンプレート、データベースとのつながりです。Spring入門 に進んでください。Java の書き方を思い出したくなったら、この教材の該当する章と 付録B「文法早見表と用語集」 に戻ってきてください。
14.8 まとめと確認問題
まとめ
- Entity は「
privateのフィールド + getter と setter」だけのクラス。社員は部署のオブジェクトをフィールドに持つ - Repository は、データを保存して取り出すクラス。
Listに保存し、findByEmpIdは見つからなければnullを返す - 新規登録と変更は、社員IDが
nullかどうかで分ける。変更は、取り出したオブジェクトを setter で書き換えてsaveする - 役割ごとにパッケージを分け(
entity・repository)、importでつなぐ - Spring入門では、Java の文法は新しく出てこない。Entity とメソッド名はそのまま使う
確認問題
問1 EmployeeRepository の findByEmpId が、存在しない社員IDを受け取ったときに返す値は何ですか。使う側は、それをどう調べますか。
解答を見る
null を返します。使う側は if (employee == null) で調べてから使います。
問2 employee.getEmpId().equals(empId) を employee.getEmpId() == empId と書いてはいけないのはなぜですか。
解答を見る
Integer はクラスなので、== は「同じオブジェクトか」を比べてしまい、同じ値でも false になることがあるためです。値を比べるには equals を使います。
問3 save は、社員IDが null のときと、そうでないときで、それぞれ何をしますか。
解答を見る
null のときは新規登録として、社員IDを採番してリストに追加します。そうでないときは変更として、何もしません(取り出したオブジェクトを書き換えた時点で、リストの中身も変わっているため)。
問4 社員の部署名を表示するコードを、employee から書いてください。
解答を見る
System.out.println(employee.getDepartment().getDeptName());
問5 この章の EmployeeRepository と、Spring入門の EmployeeRepository の違いは何ですか。
解答を見る
この章ではクラスとしてメソッドの中身を自分で書き、List に保存します。Spring入門ではインターフェースとしてメソッドの宣言だけを書き、中身は Spring が用意して、データベースに保存します。