課題提出予定
前期提出課題にログイン認証機能を追加したものを提出課題とする予定なので、前期提出課題ができていない人は、取り組んでおくこと
本棚の編集と削除
本の編集と削除ができるようになったら、本棚の編集と削除もやってみよう!
Java/TDD/Spring Boot2/Backlog/Gitを使ったWebアプリ開発
前期提出課題にログイン認証機能を追加したものを提出課題とする予定なので、前期提出課題ができていない人は、取り組んでおくこと
本の編集と削除ができるようになったら、本棚の編集と削除もやってみよう!
src/main/java/
└jp.abc
└Bookshelf.java
package jp.abc;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Entity
public class Bookshelf {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
@NotNull
private long id;
@Column(length = 200, nullable = false)
@NotEmpty
private String name;
@OneToMany
private List<Book> books;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
src/main/java/
└jp.abc
└Book.java
package jp.abc;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
@NotNull
private long id;
@Column(length = 200, nullable = false)
@NotEmpty
private String title;
@Column(length = 200, nullable = false)
@NotEmpty
private String author;
@ManyToOne
private Bookshelf bookshelf;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Bookshelf getBookshelf() {
return bookshelf;
}
public void setBookshelf(Bookshelf bookshelf) {
this.bookshelf = bookshelf;
}
}
HTMLテンプレートを修正する。
src/main/resources/
└templates
└book.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book</title>
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
tr {
margin: 5px;
}
th {
padding: 5px;
color: white;
background: darkgray;
}
td {
padding: 5px;
color: black;
background: #f0f0f0;
}
.err {
color: red;
}
</style>
</head>
<body>
<h1>Book</h1>
<p th:text="${msg}"></p>
<form method="post" action="/book" th:object="${formModel}">
<table>
<tr>
<td><label for="title">タイトル</label></td>
<td>
<input type="text" name="title" th:value="*{title}"
th:errorclass="err" />
<div th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td><label for="author">著者</label></td>
<td>
<input type="text" name="author" th:value="*{author}"
th:errorclass="err" />
<div th:if="${#fields.hasErrors('author')}" th:errors="*{author}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td><label for="bookshelf">本棚</label></td>
<td>
<input type="text" name="bookshelf" th:value="*{bookshelf}"
th:errorclass="err"/>
<div th:if="${#fields.hasErrors('bookshelf')}" th:errors="*{bookshelf}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" /></td>
</tr>
</table>
</form>
<hr />
<table>
<tr>
<th>ID</th><th>タイトル</th><th>著者</th><th>本棚</th>
</tr>
<tr th:each="obj : ${datalist}">
<td th:text="${obj.id}"></td>
<td th:text="${obj.title}"></td>
<td th:text="${obj.author}"></td>
<td th:if="${obj.bookshelf != null}" th:text="${obj.bookshelf.name}"></td>
<td th:if="${obj.bookshelf == null}" th:text="本棚なし"></td>
</tr>
</table>
</body>
</html>
HTMLテンプレートを用意する。
src/main/resources/
└templates
└edit.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>本の編集</title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
pre {
border: solid 3px #ddd;
padding: 10px;
}
tr {
margin: 5px;
}
th {
padding: 5px;
color: white;
background: darkgray;
}
td {
padding: 5px;
color: black;
background: #f0f0f0;
}
</style>
</head>
<body>
<h1>本の編集</h1>
<form method="post" action="/edit" th:object="${formModel}">
<input type="hidden" name="id" th:value="*{id}" />
<table>
<tr>
<td><label for="title">タイトル</label></td>
<td><input type="text" name="title" th:value="*{title}" /></td>
</tr>
<tr>
<td><label for="artist">著者</label></td>
<td><input type="text" name="author" th:value="*{author}" /></td>
</tr>
<tr>
<td><label for="bookshelf">本棚</label></td>
<td th:if="*{bookshelf != null}">
<input type="text" name="bookshelf" th:value="*{bookshelf.id}" />
</td>
<td th:if="*{bookshelf == null}">
<input type="text" name="bookshelf" value="" />
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" /></td>
</tr>
</table>
</form>
</body>
</html>
コントローラに編集画面へのマッピングを追加する。
src/main/java/
└jp.abc
└BookController.java
@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public ModelAndView edit(@ModelAttribute Book book,
@PathVariable long id,
ModelAndView mav) {
mav.setViewName("edit");
mav.addObject("title", "edit music");
Optional<Book> data = repository.findById(id);
mav.addObject("formModel", data.get());
return mav;
}
POSTメソッドを受け取れるようにする。
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public ModelAndView form(@ModelAttribute @Validated Book book,
Errors result,
ModelAndView mav) {
if (result.hasErrors()) {
return mav;
}
repository.saveAndFlush(book);
return new ModelAndView("redirect:/book");
}
book.html に編集と削除のリンクを追加する。
src/main/resources/
└templates
└book.html
<hr />
<table>
<tr>
<th>ID</th><th>タイトル</th><th>著者</th><th>本棚</th><th>編集</th><th>削除</th>
</tr>
<tr th:each="obj : ${datalist}">
<td th:text="${obj.id}"></td>
<td th:text="${obj.title}"></td>
<td th:text="${obj.author}"></td>
<td th:if="${obj.bookshelf != null}" th:text="${obj.bookshelf.name}"></td>
<td th:if="${obj.bookshelf == null}" th:text="本棚なし"></td>
<td><a th:href="@{'/edit/' + ${obj.id}}">編集</a></td>
<td><a th:href="@{'/delete/' + ${obj.id}}">削除</a></td>
</tr>
</table>
src/main/resources/
└templates
└delete.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>本の削除</title>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
pre {
border: solid 3px #ddd;
padding: 10px;
}
tr {
margin: 5px;
}
th {
padding: 5px;
color: white;
background: darkgray;
}
td {
padding: 5px;
color: black;
background: #f0f0f0;
}
</style>
</head>
<body>
<h1>本の削除</h1>
<form method="post" action="/delete" th:object="${formModel}">
<input type="hidden" name="id" th:value="*{id}" />
<table>
<tr><td><p th:text="|タイトル : *{title}|"></p></td></tr>
<tr><td><p th:text="|著者 : *{author}|"></p></td></tr>
<tr><td><input type="submit" value="削除" /></td></tr>
</table>
</form>
</body>
</html>
コントローラに削除ページへのマッピングを追加する。
src/main/java/
└jp.abc
└BookController.java
@RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
public ModelAndView delete(@PathVariable int id,
ModelAndView mav) {
mav.setViewName("delete");
mav.addObject("title", "本の削除");
Optional<Book> data = repository.findById((long)id);
mav.addObject("formModel", data.get());
return mav;
}
@RequestMapping(value = "/delete", method = RequestMethod.POST)
public ModelAndView remove(@RequestParam long id,
ModelAndView mav) {
repository.deleteById(id);
return new ModelAndView("redirect:/book");
}
Repositoryを作成する。
src/main/java/
└jp.abc
└BookshelfRepository.java
package jp.abc;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookshelfRepository extends JpaRepository<Bookshelf, Long> {
}
コントローラがPOSTメソッドを受け取れるようにする。
src/main/java/
└jp.abc
└BookshelfController.java
package jp.abc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class BookshelfController {
@Autowired
private BookshelfRepository repository;
@RequestMapping(value = "/bookshelf", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("bookshelf");
mav.addObject("formModel", new Bookshelf());
mav.addObject("datalist", repository.findAll());
return mav;
}
@RequestMapping(value = "/bookshelf", method = RequestMethod.POST)
public ModelAndView post(
@ModelAttribute("formModel") @Validated Bookshelf bookshelf,
Errors errors,
ModelAndView mav) {
if (errors.hasErrors()) {
mav.addObject("msg", "エラーですよ");
return mav;
}
repository.saveAndFlush(bookshelf);
return new ModelAndView("redirect:/bookshelf");
}
}
データベース接続設定を application.properties に記述する。
src/main/resources/
└application.properties
spring.datasource.url=jdbc:hsqldb:hsql://localhost/bookshelf spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect spring.jpa.hibernate.ddl-auto=update
本棚に収納するための本のクラス Book を作成する。
src/main/java/
└jp.abc
└Book.java
package jp.abc;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
@NotNull
private long id;
@Column(length = 200, nullable = false)
@NotEmpty
private String title;
@Column(length = 200, nullable = false)
@NotEmpty
private String author;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Book用のHTMLテンプレートを作成する。
src/main/resources/
└templates
└book.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book</title>
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
tr {
margin: 5px;
}
th {
padding: 5px;
color: white;
background: darkgray;
}
td {
padding: 5px;
color: black;
background: #f0f0f0;
}
.err {
color: red;
}
</style>
</head>
<body>
<h1>Book</h1>
<p th:text="${msg}"></p>
<form method="post" action="/book" th:object="${formModel}">
<table>
<tr>
<td><label for="title">タイトル</label></td>
<td>
<input type="text" name="title" th:value="*{title}"
th:errorclass="err" />
<div th:if="${#fields.hasErrors('title')}" th:errors="*{title}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td><label for="author">著者</label></td>
<td>
<input type="text" name="author" th:value="*{author}"
th:errorclass="err" />
<div th:if="${#fields.hasErrors('author')}" th:errors="*{author}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" /></td>
</tr>
</table>
</form>
<hr />
<table>
<tr>
<th>ID</th><th>タイトル</th><th>著者</th>
</tr>
<tr th:each="obj : ${datalist}">
<td th:text="${obj.id}"></td>
<td th:text="${obj.title}"></td>
<td th:text="${obj.author}"></td>
</tr>
</table>
</body>
</html>
Book用のリポジトリを作成する。
src/main/java/
└jp.abc
└BookRepository.java
package jp.abc;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}
Book用のコントローラを作成する。
src/main/java/
└jp.abc
└BookController.java
package jp.abc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class BookController {
@Autowired
private BookRepository repository;
@RequestMapping(value = "/book", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("book");
mav.addObject("formModel", new Book());
mav.addObject("datalist", repository.findAll());
return mav;
}
@RequestMapping(value = "/book", method = RequestMethod.POST)
public ModelAndView post(
@ModelAttribute("formModel") @Validated Book book,
Errors errors,
ModelAndView mav) {
if (errors.hasErrors()) {
mav.addObject("msg", "エラーですよ");
return mav;
}
repository.saveAndFlush(book);
return new ModelAndView("redirect:/book");
}
}
簡単なWebアプリを作成してみる。
プロジェクト名: bookshelf
パッケージ名: jp.abc
プロジェクトを作成すると、pom.xml内でのSpringBootのバージョンが2.1.7になっているが、そのままではビルドエラーになるので、これを2.1.4に変更する。
BookshelfエンティティとBookエンティティを作成する。
Bookshelfのプロパティは name のみ。
Bookのプロパティは title と author の二つ。
作成するページ
ビルドでエラーが出る人は、サーバーの
授業資料・OC資料\澤田\2019_SpringBOOT\repository
を、
ユーザー\.m2
に上書きしてみる。
pom.xmlの変更
7月2日のページを参照して、pom.xmlを変更する。
ThymeleafとJPAとHSQLDBの依存関係を追加する。
データベースの用意
デスクトップに作成したhsqldb.batを編集して bookshelf インスタンスを用意する。
cd C:\pleiades\hsqldb-2.4.1\hsqldb\lib java -cp hsqldb.jar org.hsqldb.Server --database.0 db/mydata --dbname.0 mydata --database.1 db/bookshelf --dbname.1 bookshelf
トップページを作成する
src/main/resources/
└templates
└index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</title>
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
</style>
</head>
<body>
<h1>top page</h1>
</body>
</html>
トップページを表示するためのコントローラを作成する
src/main/java/
└jp.abc
└IndexController.java
package jp.abc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class IndexController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("index");
return mav;
}
}
Bookshelf(本棚)を作成する
HTMLテンプレートを作成する。
src/main/resources/
└templates
└bookshelf.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf</title>
<style type="text/css">
h1 {
font-size: 18pt;
font-weight: bold;
color: gray;
}
body {
font-size: 13pt;
color: gray;
margin: 5px 25px;
}
tr {
margin: 5px;
}
th {
padding: 5px;
color: white;
background: darkgray;
}
td {
padding: 5px;
color: black;
background: #f0f0f0;
}
.err {
color: red;
}
</style>
</head>
<body>
<h1>Bookshelf</h1>
<p th:text="${msg}"></p>
<form method="post" action="/bookshelf" th:object="${formModel}">
<table>
<tr>
<td><label for="name">名前</label></td>
<td>
<input type="text" name="name" th:value="*{name}"
th:errorclass="err" />
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"
th:errorclass="err"></div>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" /></td>
</tr>
</table>
</form>
<hr />
<table>
<tr>
<th>ID</th><th>名前</th>
</tr>
<tr th:each="obj : ${datalist}">
<td th:text="${obj.id}"></td>
<td th:text="${obj.name}"></td>
</tr>
</table>
</body>
</html>
Bookshelfエンティティを作成する
src/main/java/
└jp.abc
└Bookshelf.java
package jp.abc;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Entity
public class Bookshelf {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
@NotNull
private long id;
@Column(length = 200, nullable = false)
@NotEmpty
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
コントローラを作成する
src/main/java/
└jp.abc
└BookshelfController.java
package jp.abc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class BookshelfController {
@RequestMapping(value = "/bookshelf", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
mav.setViewName("bookshelf");
mav.addObject("formModel", new Bookshelf());
mav.addObject("datalist", null);
return mav;
}
}