1.プロジェクトを作成
① ブラウザを開き、(https://start.spring.io/) を入力し、開く
② 下記を入力し、Dependenciesで下記を選択し、『GENERATE』ボタンを押す
- Project: Maven
- Language: Java
- SpringBoot: 3.1.0
- Group: com.example
- Artifact: contacts
- Name : contacts
- Description: Demo project for Spring Boot
- Package name: com.example.contacts
Dependencies
- Spring Web
- Spring Boot DevTools
- Thymeleaf
2.トップページの作成
①src > main > java > com > example > contacts直下にcontrollersフォルダを作成し、PersonController.javaファイルを作成する。
package com.example.contacts.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org. springframework. web. bind. annotation. RequestParam;
@Controller
public class PersonController {
@GetMapping("/")
public String index(){
return "person/index";
}
@PostMapping("/create")
public String create(@RequestParam String name, Model model){
model.addAttribute("name", name);
return "person/create";
}
}
@PostMapping
HTTPリクエストのPOSTメソッドを受け付ける
@PostMapping("/create") はPOSTメソッドで「/create」のURLをリクエストされた場合の処理を記述することになる
3.トップページのテンプレートを作成
src > main > resources> templatesフォルダの中にpersonフォルダを作成し、index.htmlファイルを作成しましょう。
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
</head>
<body>
//フォームの送信先URLを @{URLパス} の形で指定
<form th:action="@{/create}" method="post">
<label>名前</label>
<input type="text" name="name">
<button>送信</button>
</form>
</body>
</html>
4.フォームの値を受け取って表示させるテンプレートの作成
personフォルダの中にcraete.htmlファイルを作成する。
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
</head>
<body>
名前: <span th:text="${name}"></span><br>
<a href="/">戻る</a>
</body>
</html>
5.Modelファイルの作成
src > main > java > com > example > contactsフォルダ直下にmodelsフォルダを作成し、その中にPerson.javaファイルを作成する。
Personモデルには「名前・年齢・メールアドレス」の3つの属性を持たせる。
※ 属性とはプロパティやフィールドなどと呼ばれる変数のことで、オブジェクト固有の値になる
※ 名前はname、年齢はage、メールアドレスはemailという名前にする。
package com.example.contacts.models;
public class Person {
private String name;
private Integer age;
private String email;
public String getName(){
return this.name;
}
public Integer getAge(){
return this.age;
}
public String getEmail(){
return this.email;
}
public void setName(String name){
this.name = name;
}
public void setAge(Integer age){
this.age = age;
}
public void setEmail(String email){
this.email = email;
}
}
6.PersonControllerの修正
package com.example.contacts.controllers;
import org.springframework.stereotype.Controller;-
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import com.example.contacts.models.Person;
@Controller
public class PersonController {
@GetMapping("/")
public String index() {
public String index(@ModelAttribute Person person){
return "person/index";
}
@PostMapping("/create")
public String create(@RequestParam String name, Model model) {
public String create(@ModelAttribute Person person){
model.addAttribute("name", name);
return "person/create";
}
}
@ModelAttributeアノテーション
モデル属性に関連付け(バインド)を行う
7.テンプレートファイルの修正
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
</head>
<body>
<form th:action="@{/create}" th:object="${person}" method="post"></form>
<label>名前</label>
<input type="number" th:field="*{age}"><br>
<label>メールアドレス</label>
<input type="email" th:field="*{email}"><br>
<button>送信</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
</head>
<body>
名前: [[${person.name}]]<br>
年齢: [[${person.age}]]<br>
メールアドレス: [[${person.email}]]<br>
<a href="/">戻る</a>
</body>
</html>
8.Lombokの追加
依存関係にLombok(ロンボック)を追加する。
※ Lombokはセッターやゲッターメソッドを自動で生成してくれる便利なライブラリ
①VSCodeの拡張機能の検索窓に「Lombok」を入力し、Lombok Annotations Support for VS Code を検索してインストールする。
②pom.xml を開き右クリックから「Add Starters...」を選び、依存関係にLombokを追加する。
③画面右下に「Adding: [Lombok].Proceed?」が表示したら「Proceed」を選択する。
④画面右下に「Adding: [Lombok].Proceed?」が表示したら「Proceed」を選択する。
⑤pom.xmlファイルを保存する。
package com.example.contacts.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Person {
private String name;
private Integer age;
private String email;
}
9.Validationの追加
Validation(検証)ライブラリを依存関係に追加する。
①pom.xmlを開き、右クリックメニューからAdd Startersを選択する。
②右下に「Adding: [Validation]. Proceed?」と表示されましたら「Proceed」を選択し、pom.xmlを保存しましたら追加完了。
10.Modelクラスの修正
package com.example.contacts.models;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Person {
@NotBlank
@Size(max = 120)
private String name;
@NotNull
@Min(0)
@Max(120)
private Integer age;
@NotBlank
@Email
@Size(max = 254)
private String email;
}
@NotBlankアノテーションは文字列の入力必須のバリデーションで、
@NotNUllアノテーションは入力した値がnullでないかを検証する。
@Sizeアノテーションは文字列の長さが指定の文字数になっているかを検証する。引数には最小文字数や最大文字数を指定することが出来る。
//以下は6文字以上50文字以下の指定
@Size(min=6, max=50)
11.Personコントローラの修正
package com.example.contacts.controllers;
import org.springframework.stereotype.Controller;
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 com.example.contacts.models.Person;
@Controller
public class PersonController {
@GetMapping("/")
public String index(@ModelAttribute Person person) {
return "person/index";
}
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result) {
//バリデーションエラーがある場合はindex.htmlを表示
if (result.hasErrors()){
return "person/index";
}
return "person/create";
}
}
12.Viewファイルの修正
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
<style>
.red{
color: red;
}
.invalid{
border:2px solid red;
}
</style>
</head>
<body>
<form th:action="@{/create}" th:object="${person}" method="post">
<label>名前</label>
<input type="text" th:field="*{name}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{name}"></div>
<label>年齢</label>
<input type="number" th:field="*{age}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{age}"></div>
<label>メールアドレス</label>
<input type="email" th:field="*{email}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{email}"></div>
<button>送信</button>
</form>
</body>
</html>
13.データベースの準備
-
Spring Data JPA
Spring Data JPA はJava標準のデータベースにアクセスできるライブラリで、Spring Data JPAを使うことによって簡単にデータベースにアクセスすることができる。
Spring Data JPA はHibernateというORM(Object Relational Mapping)を内包していて、Javaのモデルクラスとデータベースのテーブルを結びつけてデータベースの処理の記述をスマートにしてくれる。 -
H2データベース
H2データベースは開発用の簡易SQLデータベースで、本番環境ではより堅牢なSQLに切り替える必要があるが、利用が簡単なので開発環境ではこちらのデータベースを使用する。
①pom.xmlファイルを開き、右クリックからAdd Starters...を選択し、
依存関係にH2 Database と Spring Data JPA を追加する。
②続行(Proceed)するかを尋ねられたら「Proceed」を選択し、pom.xmlファイルを保存する。
14.Personモデルの修正
package com.example.contacts.models;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@NotBlank
@Size(max = 120)
private String name;
@NotNull
@Min(0)
@Max(120)
private Integer age;
@NotBlank
@Email
@Size(max = 254)
private String email;
}
15.リポジトリの作成
src > main > java > com > example > contacts直下にrepositoryフォルダを作成し、その中にPersonRepository.javaファイルを作成する。
package com.example.contacts.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.contacts.models.Person;
public interface PersonRepository extends JpaRepository<Person, Long> {
}
16.Personコントローラの修正
package com.example.contacts.controllers;
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 com.example.contacts.models.Person;
import com.example.contacts.repository.PersonRepository;
@Controller
public class PersonController {
//Personクラスのフィールドをfinalにする。
private final PersonRepository repository;
public PersonController(PersonRepository repository){
this.repository = repository;
}
@GetMapping("/")
public String index(@ModelAttribute Person person, Model model){
//一覧用データの用意
model.addAttribute("people", repository.findAll());
return "person/index";
}
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result, Model model){
// バリデーションエラーがある場合はindex.htmlを表示
if (result.hasErrors()) {
model.addAttribute("people", repository.findAll());
return "person/index";
}
repository.saveAndFlush(person);
return "redirect:/";
}
}
17.一覧画面の追加
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Person</title>
<style>
.red {
color: red;
}
.invalid {
border: 2px solid red;
}
</style>
</head>
<body>
<h2>登録</h2>
<form th:action="@{/create}" th:object="${person}" method="post">
<label>名前</label>
<input type="text" th:field="*{name}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{name}"></div>
<label>年齢</label>
<input type="number" th:field="*{age}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{age}"></div>
<label>メールアドレス</label>
<input type="email" th:field="*{email}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{email}"></div>
<button>送信</button>
</form>
<h2>一覧</h2>
<table>
<thead>
<tr>
<td>ID</td>
<td>name</td>
<td>age</td>
<td>email</td>
</tr>
</thead>
<tr th:each="person : ${people}">
<td>[[${person.id}]]</td>
<td>[[${person.name}]]</td>
<td>[[${person.age}]]</td>
<td>[[${person.email}]]</td>
</tr>
</table>
</body>
</html>
18.初期データを用意する
H2データベースは初期設定ではサーバを停止するとデータが消えるので、初期データを用意して動作確認しやすいようにする。
次のコードは2つのPersonインスタンスを作成してデータベースに保存する初期化処理。
@PostConstructアノテーションつけたdataInitメソッドを用意する。
※ @PostConstructはコントローラのインスタンスが作成された後に呼び出されるメソッドにつけるアノテーション。データの初期化という意味で「dataInit」というメソッド名にしているがこちらのアノテーションがついているのであればメソッド名は任意のもので良い。
SprongBootのコントローラは一度インスタンスが作成されると、以降はそのインスタンスが保持される。そのためこちらの初期データを用意するメソッド(dataInit)も一度だけ行われる。
package com.example.contacts.controllers;
import javax.annotation.PostConstruct;
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 com.example.contacts.models.Person;
import com.example.contacts.repository.PersonRepository;
@Controller
public class PersonController {
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/")
public String index(@ModelAttribute Person person, Model model) {
// 一覧用データの用意
model.addAttribute("people", repository.findAll());
return "person/index";
}
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result, Model model) {
// バリデーションエラーがある場合はindex.htmlを表示
if (result.hasErrors()) {
model.addAttribute("people", repository.findAll());
return "person/index";
}
repository.saveAndFlush(person);
return "redirect:/";
}
// 初期データの投入
@PostConstruct
public void dataInit() {
Person suzuki = new Person();
suzuki.setName("鈴木");
suzuki.setAge(23);
suzuki.setEmail("suzuki@email.com");
repository.saveAndFlush(suzuki);
Person sato = new Person();
sato.setName("佐藤");
sato.setAge(20);
sato.setEmail("sato@email.com");
repository.saveAndFlush(sato);
}
}
19.データの削除
package com.example.contacts.controllers;
import javax.annotation.PostConstruct;
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 com.example.contacts.models.Person;
import com.example.contacts.repository.PersonRepository;
@Controller
public class PersonController {
// 中略
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result, Model model) {
// 中略
}
@GetMapping("/delete/{id}") // 初期データの投入
public String remove(@PathVariable long id){
repository.deleteById(id);
return "redirect:/";
}
// 初期データの投入
@PostConstruct
public void dataInit() {
// 中略
}
}
データを削除するremoveメソッドを追加。@PathVariableはパスに含まれるパラメータ{id}を変数に格納するアノテーション。
repository.deleteByIdは JPA Repositoryによって提供されたidでデータを削除する機能で、URL中のidを取得し、削除する。
20.index.htmlに削除ボタンを追加する
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Person</title>
<style>
.red {
color: red;
}
.invalid {
border: 2px solid red;
}
</style>
</head>
<body>
<h2>登録</h2>
<form th:action="@{/create}" th:object="${person}" method="post">
<label>名前</label>
<input type="text" th:field="*{name}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{name}"></div>
<label>年齢</label>
<input type="number" th:field="*{age}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{age}"></div>
<label>メールアドレス</label>
<input type="email" th:field="*{email}" th:errorclass="invalid" /><br />
<div class="red" th:errors="*{email}"></div>
<button>送信</button>
</form>
<h2>一覧</h2>
<table>
<thead>
<tr>
<td>ID</td>
<td>name</td>
<td>age</td>
<td>email</td>
</tr>
</thead>
<tr th:each="person : ${people}">
<td>[[${person.id}]]</td>
<td>[[${person.name}]]</td>
<td>[[${person.age}]]</td>
<td>[[${person.email}]]</td>
<td><a th:href="@{/delete/{id}(id=${person.id})}">削除</a></td>
</tr>
</table>
</body>
</html>
21.編集画面の準備
package com.example.contacts.controllers;
import javax.annotation.PostConstruct;
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 com.example.contacts.models.Person;
import com.example.contacts.repository.PersonRepository;
@Controller
public class PersonController {
private final PersonRepository repository;
public PersonController(PersonRepository repository) {
this.repository = repository;
}
@GetMapping("/")
public String index(@ModelAttribute Person person, Model model) {
// 一覧用データの用意
model.addAttribute("people", repository.findAll());
return "person/index";
}
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result, Model model) {
// バリデーションエラーがある場合はindex.htmlを表示
if (result.hasErrors()) {
model.addAttribute("people", repository.findAll());
return "person/index";
}
repository.saveAndFlush(person);
return "redirect:/";
}
@GetMapping("/delete/{id}")
public String remove(@PathVariable long id) {
repository.deleteById(id);
return "redirect:/";
}
@GetMapping("/edit/{id}")
public String edit(@PathVariable long id, Model model) {
model.addAttribute("person", repository.findById(id)); // 一覧用データの用意
return "person/edit";
}
// 初期データの投入
@PostConstruct
public void dataInit() {
Person suzuki = new Person();
suzuki.setName("鈴木");
suzuki.setAge(23);
suzuki.setEmail("suzuki@email.com");
repository.saveAndFlush(suzuki);
Person sato = new Person();
sato.setName("佐藤");
sato.setAge(20);
sato.setEmail("sato@email.com");
repository.saveAndFlush(sato);
}
}
repository.findById(id)はidでデータを検索する機能で、データはperson変数に代入される。
src > main > resources > templates > personフォルダ直下にedit.htmlファイルを作成する。
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>編集画面</title>
<style>
.red{
color: red;
}
.invalid{
border:2px solid red;
}
</style>
</head>
<body>
<h2>編集</h2>
<form th:action="@{/update/{id}(id=${id})}" th:object="${person}" method="post">
<label>名前</label>
<input type="text" th:field="*{name}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{name}"></div>
<label>年齢</label>
<input type="number" th:field="*{age}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{age}"></div>
<label>メールアドレス</label>
<input type="email" th:field="*{email}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{email}"></div>
<button>送信</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Person</title>
<style>
.red{
color: red;
}
.invalid{
border:2px solid red;
}
</style>
</head>
<body>
<h2>登録</h2>
<form th:action="@{/create}" th:object="${person}" method="post">
<label>名前</label>
<input type="text" th:field="*{name}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{name}"></div>
<label>年齢</label>
<input type="number" th:field="*{age}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{age}"></div>
<label>メールアドレス</label>
<input type="email" th:field="*{email}" th:errorclass="invalid"><br>
<div class="red" th:errors="*{email}"></div>
<button>送信</button>
</form>
<h2>一覧</h2>
<table>
<thead>
<tr>
<td>ID</td>
<td>name</td>
<td>age</td>
<td>email</td>
</tr>
</thead>
<tr th:each="person : ${people}">
<td>[[${person.id}]]</td>
<td>[[${person.name}]]</td>
<td>[[${person.age}]]</td>
<td>[[${person.email}]]</td>
<td><a th:href="@{/edit/{id}(id=${person.id})}">編集</a></td>
<td><a th:href="@{/delete/{id}(id=${person.id})}">削除</a></td>
</tr>
</table>
</body>
</html>
22.更新処理の追加
Personコントローラに更新処理updateメソッドを追加する。
package com.example.contacts.controllers;
import javax.annotation.PostConstruct;
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 com.example.contacts.models.Person;
import com.example.contacts.repository.PersonRepository;
@Controller
public class PersonController {
//Personクラスのフィールドをfinalにする。
private final PersonRepository repository;
public PersonController(PersonRepository repository){
this.repository = repository;
}
@GetMapping("/")
public String index(@ModelAttribute Person person, Model model){
model.addAttribute("people", repository.findAll());
return "person/index";
}
@PostMapping("/create")
public String create(@Validated @ModelAttribute Person person, BindingResult result, Model model){
//バリデーションエラーがある場合はindex.htmlを表示
if (result.hasErrors()){
model.addAttribute("people", repository.findAll()); //一覧用データの用意
return "person/index";
}
repository.save(person);
return "redirect:/";
}
@GetMapping("/delete/{id}")
public String remove(@PathVariable long id){
repository.deleteById(id);
return "redirect:/";
}
@GetMapping("/edit/{id}")
public String edit(@PathVariable long id, Model model){
model.addAttribute("person", repository.findById(id)); //一覧用データの用意
return "person/edit";
}
@PostMapping("/update/{id}")
public String update(@PathVariable long id, @Validated @ModelAttribute Person person, BindingResult result){
if (result.hasErrors()){
return "person/edit";
}
repository.save(person);
return "redirect:/";
}
//初期データの投入
@PostConstruct
public void dataInit(){
Person suzuki = new Person();
suzuki.setName("鈴木");
suzuki.setAge(23);
suzuki.setEmail("suzuki@email.com");
repository.saveAndFlush(suzuki);
Person sato = new Person();
sato.setName("佐藤");
sato.setAge(20);
sato.setEmail("sato@email.com");
repository.saveAndFlush(sato);
}
}