提出用課題 RINE

SpringBootを使ったWebサイトを作成する。

プロジェクト名: RINE
Spring Boot Version: 2.2.1

hsqldb.bat

cd data
java -classpath ../lib/hsqldb.jar org.hsqldb.server.Server ^
--database.0 db/shindan --dbname.0 shindan ^
--database.1 db/bookshelf --dbname.1 bookshelf ^
--database.2 db/rine --dbname.2 rine

application.properties

spring.datasource.url=jdbc:hsqldb:hsql://localhost/rine
spring.datasource.username=SA
spring.datasource.password=
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
spring.jpa.hibernate.ddl-auto=update

作成するページとURL

ページ名 URL 内容
トップページ http://localhost:8080/ ともだちリストを表示し、ともだち追加ができる
ともだち http://localhost:8080/friend/ 選択したともだちに送信した投稿の一覧を表示する。新規に投稿する。
ともだち編集ページ http://localhost:8080/friend/edit/{id} ともだちの名前と画像を変更できる
ともだちブロックページ http://localhost:8080/friend/block/{id} ともだちをブロックする

作成するエンティティ

Friend
・id : long
・name : String
・image : String
・blocked : boolean
・posts : List
Post
・id : Long
・time : java.util.Date
・content : String
・friend : Friend

トップページ

テンプレート: index.html

  • サイトタイトル
  • 学籍番号と名前
  • ともだちリスト
  • ともだち追加フォーム

ともだちごとのページ

テンプレート: talk.html

  • ともだちの名前
  • ともだちのアイコン
  • 修正ページへのリンク
  • ブロックorブロック解除のリンク
  • 送信したメッセージのリスト
  • メッセージ送信フォーム

ともだち修正ページ

テンプレート: editfriend.html

  • アイコン表示
  • 修正フォームで名前と画像URL

ともだちブロックページ

テンプレート: blockfriend.html

  • アイコン表示
  • ブロック or ブロック解除 ボタン

配点

  1. トップページに指定の内容を表示: 10点
  2. トップページをCSSでデザイン変更: 10点
  3. ともだち追加できる: 10点
  4. ともだち追加で画像URLを入力するとプレビューを表示する: 10点
  5. トップページからともだちページに行ける: 10点
  6. ともだちページに指定の内容を表示: 10点
  7. ともだちページからメッセージを送信できる: 10点
  8. ともだちページをCSSでデザイン変更: 10点
  9. ともだち修正ページを表示できる: 10点
  10. ともだち修正で名前と画像を修正できる: 10点
  11. ともだち修正で画像URLを入力するとプレビューを表示する: 10点
  12. ともだち修正ページをCSSでデザイン変更: 10点
  13. ともだちページからブロックページに行ける: 10点
  14. ともだちをブロック/ブロック解除できる: 10点
  15. ともだちブロックページをCSSでデザイン変更: 10点

Bookshelfアプリケーションを参考にする

Bookshelfに複数のBookを収納できる。
  ↓(ほぼ同じ内容)
Friendに複数のPostを送信できる。

BookshelfをFriendに置きかえ、BookをPostに置きかえればよい。

違う点は、BookshelfではBookにimageがあったが、RINEではFriendにimageがある。

課題の提出方法

pom.xml で以下の場所を修正する。(11を8にする)

	<properties>
		<java.version>8</java.version>
	</properties>

プロジェクト RINE を右クリックして、[実行]-[maven install]を選択する。

RINE-0.0.1-SNAPSHOT.jar ファイルができる。

動作確認

コマンドプロンプトを起動する。
以下のコマンドを実行すると、Spring Bootが起動する。
http://localhost:8080/
にアクセスして、動作することを確認する。

Z:\>cd Z:\Java\202010\RINE\target  ←自分のPCの環境に合わせる
Z:\Java\202010\RINE\target>java -jar RINE-0.0.1-SNAPSHOT.jar

提出先

「提出\システム開発実習\1917XXX」(←XXXは学籍番号に合わせる)フォルダを作成し、そこに RINE-0.0.1-SNAPSHOT.jarをコピーする。

提出期限:12月23日(水)

JavaScriptで動きをつけてみる

本の編集画面で画像URLを入力したときに、画像のプレビューを表示する。

src/main/resources にある static フォルダに js フォルダを新規作成する。
そのフォルダに、jQueryファイルを貼り付ける。

まず、開発者ツールのJavaScriptコンソールでやりたい動きを確認する。
うまく動いたら、その内容を script タグ内に記述する。

imgタグに表紙という意味でid=coverを割り当てておく。

editbook.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本の編集</title>
<script src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(function() {
	$('[name=image]').on('keyup', function() {
		$('#cover').prop('src', $(this).val());
	})
});
</script>
</head>
<body>

<a href="/">トップ</a>

<h1>本の編集</h1>

<img id="cover" th:src="${book.image}" width="200" />

<form action="/book/edit" method="post">
	<input type="hidden" name="id" th:value="${book.id}" />
	<div>タイトル: <input type="text" name="title" th:value="${book.title}" /></div>
	<div>著者: <input type="text" name="author" th:value="${book.author}" /></div>
	<div>画像URL: <input type="text" name="image" th:value="${book.image}" /></div>
	<div><input id="submit" type="submit" value="修正" /></div>
</form>


</body>
</html>

本のリストの画面も同じような動きになるように修正してみる。

books.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本のリスト</title>
<script src="/js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(function() {
	$('[name=image]').on('keyup', function() {
		$('#cover').prop('src', $(this).val());
	})
});
</script>
</head>
<body>

<h1>Book list</h1>

<a href="/">トップ</a>

<table>
	<tr>
		<th>ID</th><th>画像</th><th>タイトル</th><th>著者</th><th>本棚</th>
	</tr>
	<tr th:each="book : ${list}">
		<td th:text="${book.id}"></td>
		<td><img th:src="${book.image}" height="50" /></td>
		<td><a th:href="@{'/book/' + ${book.id}}" th:text="${book.title}"></a></td>
		<td th:text="${book.author}"></td>
		<td th:if="${book.bookshelf != null}" th:text="${book.bookshelf.name}"></td>
		<td th:if="${book.bookshelf == null}" th:text="本棚に入れてません"></td>
	</tr>
</table>

<hr />

<div>新しい本</div>
<img id="cover" src="" width="200" />
<form action="/addbook" method="post">
	<div>タイトル: <input type="text" name="title" /></div>
	<div>著者: <input type="text" name="author" /></div>
	<div>画像URL: <input type="text" name="image" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

今のままでは、画像URLを入力しても保存していないので、サーバー側に画像URLを保存する処理を追加する。

	@RequestMapping(value="/addbook", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("title") String title,
			@RequestParam("author") String author,
			@RequestParam("image") String image) {
		Book book = new Book();
		book.setTitle(title);
		book.setAuthor(author);
		book.setImage(image);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/books");
	}

Bookshelfアプリを作成する (Delete)

book.html テンプレートに削除確認画面へのリンクを追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>

<a href="/">トップ</a>


<table>
	<tr><td><img th:src="${book.image}" width="100" /></td></tr>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
		<td><a th:href="@{'/book/edit/' + ${book.id}}" th:text="修正"></a></td>
		<td><a th:href="@{'/book/delete/' + ${book.id}}" th:text="削除"></a></td>
	</tr>
</table>

<hr />

<div>どの本棚に入れますか?</div>
<form action="/book" method="post">
	<input type="hidden" name="id" th:value="${book.id}" />
	<div>
		<select name="bookshelfId">
			<option value="0">本棚に入れない</option>
			<option th:each="bs : ${bookshelfList}" th:value="${bs.id}" th:selected="${bs.id == book.id}" th:text="${bs.name}"></option>
		</select>
	</div>
	<div><input id="submit" type="submit" value="本棚に入れる" /></div>
</form>


</body>
</html>

コントローラに削除確認画面に対応するURLマッピングを追加する。

	@RequestMapping(value = "/book/delete/{id}", method = RequestMethod.GET)
	public ModelAndView deleteConfirm(ModelAndView mav,
			@PathVariable long id) {
		mav.setViewName("deletebook");
		Optional<Book> data = repository.findById(id);
		mav.addObject("book", data.get());
		return mav;
	}

削除確認画面になる deletebook.html テンプレートを追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を削除する</title>
</head>
<body>

<h1>本の削除確認</h1>

<a href="/">トップ</a>


<table>
	<tr><td><img th:src="${book.image}" width="100" /></td></tr>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
	</tr>
</table>
<form action="/book/delete" method="post">
	<input type="hidden" name="id" th:value="${book.id}" />
	<div><input id="submit" type="submit" value="削除する" /></div>
</form>


</body>
</html>

POSTメソッドで本の削除を実行する。

	@RequestMapping(value = "/book/delete", method = RequestMethod.POST)
	public ModelAndView delete(ModelAndView mav,
			@RequestParam("id") long id) {
		repository.deleteById(id);
		return new ModelAndView("redirect:/books");
	}

Bookshelfアプリを作成する (Update)

CRUDのうち、CreateとReadは動いているので、次はUpdateを作ってみる。

BookContoller.java

	@RequestMapping(value = "/book/edit/{id}", method = RequestMethod.GET)
	public ModelAndView edit(ModelAndView mav,
			@PathVariable long id) {
		mav.setViewName("editbook");
		Optional<Book> data = repository.findById(id);
		mav.addObject("book", data.get());
		List<Bookshelf> list = bookshelfRepository.findAll();
		mav.addObject("bookshelfList", list);
		return mav;
	}

editbookテンプレートを作成する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本の編集</title>
</head>
<body>

<a href="/">トップ</a>

<h1>本の編集</h1>

<form action="/book/edit" method="post">
	<div>タイトル: <input type="text" name="title" th:value="${book.title}" /></div>
	<div>著者: <input type="text" name="author" th:value="${book.author}" /></div>
	<div><input id="submit" type="submit" value="修正" /></div>
</form>


</body>
</html>

サーバーを再起動して /book/edit/{id} にアクセスすると、本の編集画面を表示する。

POSTメソッドのリクエストを受け取るところは作ってないので「修正」ボタンを押すとエラーになる。

コントローラでPOSTメソッドを受け取れるようにする。

	@RequestMapping(value = "/book/edit", method = RequestMethod.POST)
	public ModelAndView edit(ModelAndView mav,
			@RequestParam("id") long id,
			@RequestParam("title") String title,
			@RequestParam("author") String author) {
		Optional<Book> data = repository.findById(id);
		Book book = data.get();
		book.setTitle(title);
		book.setAuthor(author);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/book/" + id);
	}

再起動して「修正」ボタンを押すとエラー表示になる。
エラーメッセージを読むと、リクエストパラメータにidが存在しないと言っているので、入力フォームに追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本の編集</title>
</head>
<body>

<a href="/">トップ</a>

<h1>本の編集</h1>

<form action="/book/edit" method="post">
	<input type="hidden" name="id" th:value="${book.id}" />
	<div>タイトル: <input type="text" name="title" th:value="${book.title}" /></div>
	<div>著者: <input type="text" name="author" th:value="${book.author}" /></div>
	<div><input id="submit" type="submit" value="修正" /></div>
</form>


</body>
</html>

book.html に修正画面へのリンクを追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>

<a href="/">トップ</a>

<table>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
		<td><a th:href="@{'/book/edit/' + ${book.id}}" th:text="修正"></a></td>
	</tr>
</table>
:

本の画像を表示できるようにする。

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Book {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String title;

	@Column
	@NotEmpty
	private String author;

	@Column
	private String image;

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

	public String getImage() {
		return image;
	}

	public void setImage(String image) {
		this.image = image;
	}

}

コントローラで画像URLも保存するように修正する。

	@RequestMapping(value = "/book/edit", method = RequestMethod.POST)
	public ModelAndView edit(ModelAndView mav,
			@RequestParam("id") long id,
			@RequestParam("title") String title,
			@RequestParam("author") String author,
			@RequestParam("image") String image) {
		Optional<Book> data = repository.findById(id);
		Book book = data.get();
		book.setTitle(title);
		book.setAuthor(author);
		book.setImage(image);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/book/" + id);
	}

editbook.html テンプレート内で画像を表示する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本の編集</title>
</head>
<body>

<a href="/">トップ</a>

<h1>本の編集</h1>

<img th:src="${book.image}" width="200" />

<form action="/book/edit" method="post">
	<input type="hidden" name="id" th:value="${book.id}" />
	<div>タイトル: <input type="text" name="title" th:value="${book.title}" /></div>
	<div>著者: <input type="text" name="author" th:value="${book.author}" /></div>
	<div>画像URL: <input type="text" name="image" th:value="${book.image}" /></div>
	<div><input id="submit" type="submit" value="修正" /></div>
</form>


</body>
</html>

book.html テンプレートにも画像を追加。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>

<a href="/">トップ</a>


<table>
	<tr><td><img th:src="${book.image}" width="100" /></td></tr>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
		<td><a th:href="@{'/book/edit/' + ${book.id}}" th:text="修正"></a></td>
	</tr>
</table>
:

books.html テンプレートにも画像を追加。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本のリスト</title>
</head>
<body>

<h1>Book list</h1>

<a href="/">トップ</a>

<table>
	<tr>
		<th>ID</th><th>画像</th><th>タイトル</th><th>著者</th><th>本棚</th>
	</tr>
	<tr th:each="book : ${list}">
		<td th:text="${book.id}"></td>
		<td><img th:src="${book.image}" height="50" /></td>
		<td><a th:href="@{'/book/' + ${book.id}}" th:text="${book.title}"></a></td>
		<td th:text="${book.author}"></td>
		<td th:if="${book.bookshelf != null}" th:text="${book.bookshelf.name}"></td>
		<td th:if="${book.bookshelf == null}" th:text="本棚に入れてません"></td>
	</tr>
</table>

Bookshelfアプリを作成する

新規プロジェクトを作成する。
プロジェクトの種類は、Springスターター・プロジェクトを選択する。

プロジェクト名を「bookshelf」にして次へをクリック。

依存関係は、以下の項目を選択する。

  • HyperSQL Database
  • Spring Data JPA
  • Spring Web
  • Thymeleaf

「完了」でbookshelfプロジェクトができる。

pom.xml を開いて、8行目のバージョンを2.2.1に修正する。

		<version>2.2.1.RELEASE</version>

[実行]-[Spring Boot アプリケーション]を選択するとサーバーが起動する。

http://localhost:8080 にアクセスすると、Whitelabel Error Page が表示される。

IndexControler.java を作成する。

package jp.kpc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

	@RequestMapping("/")
	public String index() {
		return "index";
	}

}

example プロジェクトの index.html をコピーして、src/main/resources の templates に貼り付ける。

SpringBootアプリケーションを再起動すると、「最初のページ」が表示される。

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.hibernate.ddl-auto=update

hsqldb.bat に bookshelf インスタンスを追加する。

cd data
java -classpath ../lib/hsqldb.jar org.hsqldb.server.Server ^
--database.0 db/shindan --dbname.0 shindan ^
--database.1 db/bookshelf --dbname.1 bookshelf

index.html に、新しい本棚を追加するためのフォームを用意する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚</title>
</head>
<body>

<h1>Bookshelf list</h1>

<hr />

<div>新しい本棚</div>
<form action="/addshelf" method="post">
	<div>なまえ</div>
	<div><input type="text" name="name" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

IndexController.java に /addshelf に対する POST を受け取る用意をする。POSTを受け取ったあとは、URL=”/” にリダイレクトする。

package jp.kpc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/addshelf", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name) {
		return new ModelAndView("redirect:/");
	}
}

本棚に対応する Bookshelf クラスを作成する。

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Bookshelf {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

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

}

新規インターフェースを作成する。
BookshelfRepository.java

package jp.kpc;

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

public interface BookshelfRepository extends JpaRepository<Bookshelf, Long> {

}

IndexController.java で、リクエストパラメータで渡された名前の本棚を保存する処理を追加する。

package jp.kpc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {

	@Autowired
	private BookshelfRepository repository;

	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/addshelf", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name) {
		Bookshelf bookshelf = new Bookshelf();
		bookshelf.setName(name);
		repository.saveAndFlush(bookshelf);
		return new ModelAndView("redirect:/");
	}
}

トップページにアクセスがあったときに、本棚リストをテンプレートに渡す。

IndexController.java

@Controller
public class IndexController {

	@Autowired
	private BookshelfRepository repository;

	@RequestMapping("/")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("index");
		List<Bookshelf> list = repository.findAll();
		mav.addObject("list", list);
		return mav;
	}

テンプレートで本棚のリストを表示する。

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚</title>
</head>
<body>

<h1>Bookshelf list</h1>
<table>
	<tr th:each="bs : ${list}">
		<td th:text="${bs.name}"></td>
	</tr>
</table>

<hr />

<div>新しい本棚</div>
<form action="/addshelf" method="post">
	<div>なまえ</div>
	<div><input type="text" name="name" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

Bookクラスを作成する。

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Book {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String title;

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


}

BookController.java

package jp.kpc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class BookController {
	@RequestMapping("/books")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("books");
		return mav;
	}

}

books.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本のリスト</title>
</head>
<body>

<h1>Book list</h1>
<table>
	<tr th:each="book : ${list}">
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
	</tr>
</table>

<hr />

<div>新しい本</div>
<form action="/addbook" method="post">
	<div>タイトル: <input type="text" name="title" /></div>
	<div>著者: <input type="text" name="author" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

BookRepository.java

package jp.kpc;

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

public interface BookRepository extends JpaRepository<Book, Long> {

}

リポジトリを用意したので、それをBookControllerで使う。

package jp.kpc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class BookController {
	@Autowired
	private BookRepository repository;

	@RequestMapping("/books")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("books");
		List<Book> list = repository.findAll();
		mav.addObject("list", list);
		return mav;
	}

	@RequestMapping(value="/addbook", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("title") String title,
			@RequestParam("author") String author) {
		Book book = new Book();
		book.setTitle(title);
		book.setAuthor(author);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/books");
	}
}

本と本棚の関連付けを作る。

Bookshelf.java

package jp.kpc;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Bookshelf {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

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

}

Book.java

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Book {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String title;

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

本を入れる本棚を指定するための画面を作る。
そのために、/book/{id} (idはBookのid)というURLを用意する。

package jp.kpc;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class BookController {
	@Autowired
	private BookRepository repository;

	@RequestMapping("/books")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("books");
		List<Book> list = repository.findAll();
		mav.addObject("list", list);
		return mav;
	}

	@RequestMapping(value="/addbook", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("title") String title,
			@RequestParam("author") String author) {
		Book book = new Book();
		book.setTitle(title);
		book.setAuthor(author);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/books");
	}


	@RequestMapping(value = "/book/{id}", method = RequestMethod.GET)
	public ModelAndView book(ModelAndView mav,
			@PathVariable long id) {
		mav.setViewName("book");
		Optional<Book> data = repository.findById(id);
		mav.addObject("book", data.get());
		return mav;
	}

}

book.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>
<table>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
	</tr>
</table>

<hr />

<div>どの本棚に入れますか?</div>
<form action="/book" method="post">
	<input type="hidden" name="bookId" th:value="${book.id}" />
	<div>本棚ID: <input type="text" name="bookshelfId" /></div>
	<div><input id="submit" type="submit" value="本棚に入れる" /></div>
</form>


</body>
</html>

本と本棚の関連付けを保存する。

BookController.java

	@Autowired
	private BookshelfRepository bookshelfRepository;
	@RequestMapping(value="/book", method=RequestMethod.POST)
	public ModelAndView save(ModelAndView mav,
			@RequestParam("bookId") long bookId,
			@RequestParam("bookshelfId") long bookshelfId) {
		Optional<Book> data = repository.findById(bookId);
		Book book = data.get();
		Optional<Bookshelf> bsData = bookshelfRepository.findById(bookshelfId);
		Bookshelf bookshelf = bsData.get();
		book.setBookshelf(bookshelf);
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/books");
	}

books.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本のリスト</title>
</head>
<body>

<h1>Book list</h1>
<table>
	<tr>
		<th>ID</th><th>タイトル</th><th>著者</th><th>本棚</th>
	</tr>
	<tr th:each="book : ${list}">
		<td th:text="${book.id}"></td>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
		<td th:if="${book.bookshelf != null}" th:text="${book.bookshelf.name}"></td>
		<td th:if="${book.bookshelf == null}" th:text="本棚に入れてません"></td>
	</tr>
</table>

<hr />

<div>新しい本</div>
<form action="/addbook" method="post">
	<div>タイトル: <input type="text" name="title" /></div>
	<div>著者: <input type="text" name="author" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

本のタイトルをクリックしたら、/book/{id} に飛べるようにする。

<h1>Book list</h1>
<table>
	<tr>
		<th>ID</th><th>タイトル</th><th>著者</th><th>本棚</th>
	</tr>
	<tr th:each="book : ${list}">
		<td th:text="${book.id}"></td>
		<td><a th:href="@{'/book/' + ${book.id}}" th:text="${book.title}"></a></td>
		<td th:text="${book.author}"></td>
		<td th:if="${book.bookshelf != null}" th:text="${book.bookshelf.name}"></td>
		<td th:if="${book.bookshelf == null}" th:text="本棚に入れてません"></td>
	</tr>
</table>

本棚に入っている本のリストを表示する画面を作る。

/bookshelf/{id} で本棚内の本のリストを表示する。

bookshelf.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚に入っている本のリスト</title>
</head>
<body>

<h1>Bookshelf - 本棚に入っている本のリスト</h1>

<h3 th:text="${bookshelf.name}"></h3>

<ul th:each="book : ${bookshelf.books}">
	<li th:text="${book.title + ' - ' + book.author}"></li>
</ul>


</body>
</html>

Bookshelf.java

@Entity
public class Bookshelf {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String name;

	@OneToMany(mappedBy = "bookshelf")
	private List<Book> books;

IndexController.java

	@RequestMapping(value = "/bookshelf/{id}", method = RequestMethod.GET)
	public ModelAndView bookshelf(ModelAndView mav,
			@PathVariable long id) {
		mav.setViewName("bookshelf");
		Optional<Bookshelf> data = repository.findById(id);
		mav.addObject("bookshelf", data.get());
		return mav;
	}

index.htmlのテーブルに列を追加して、本棚のIDを表示する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚</title>
</head>
<body>

<h1>Bookshelf list</h1>
<table>
	<tr th:each="bs : ${list}">
		<td th:text="${bs.id}"></td>
		<td th:text="${bs.name}"></td>
	</tr>
</table>

<hr />

<div>新しい本棚</div>
<form action="/addshelf" method="post">
	<div>なまえ</div>
	<div><input type="text" name="name" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>


</body>
</html>

本棚の名前をクリックしたら、本棚の内容を表示するページに移動できるようにする。
本のリストページ(/books)へのリンクも追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚</title>
</head>
<body>

<h1>Bookshelf list</h1>
<table>
	<tr th:each="bs : ${list}">
		<td th:text="${bs.id}"></td>
		<td><a th:href="@{'/bookshelf/' + ${bs.id}}" th:text="${bs.name}"></a></td>
	</tr>
</table>

<hr />

<div>新しい本棚</div>
<form action="/addshelf" method="post">
	<div>なまえ</div>
	<div><input type="text" name="name" /></div>
	<div><input id="submit" type="submit" value="+追加" /></div>
</form>

<a href="/books">本のリスト</a>


</body>
</html>

books.html、book.html、bookshelf.html に、トップページへのリンクを追加する。

books.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本のリスト</title>
</head>
<body>

<h1>Book list</h1>

<a href="/">トップ</a>

<table>
:

book.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>

<a href="/">トップ</a>

<table>
:

bookshelf.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚に入っている本のリスト</title>
</head>
<body>

<h1>Bookshelf - 本棚に入っている本のリスト</h1>

<a href="/">トップ</a>

<h3 th:text="${bookshelf.name}"></h3>
:

本棚ページ内に表示した本のリストで、タイトルをクリックすると本のページに移動できるようにする。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Bookshelf - 本棚に入っている本のリスト</title>
</head>
<body>

<h1>Bookshelf - 本棚に入っている本のリスト</h1>

<a href="/">トップ</a>

<h3 th:text="${bookshelf.name}"></h3>

<ul th:each="book : ${bookshelf.books}">
	<li>
		<a th:href="@{'/book/' + ${book.id}}" th:text="${book.title}"></a>
		<span th:text="${' - ' + book.author}"></span>
	</li>
</ul>


</body>
</html>

book.html で、本棚IDを入力するのではなく、select で選択できるようにする。

BookController.java で、本棚のリストをテンプレートに渡すようにする。

	@RequestMapping(value = "/book/{id}", method = RequestMethod.GET)
	public ModelAndView book(ModelAndView mav,
			@PathVariable long id) {
		mav.setViewName("book");
		Optional<Book> data = repository.findById(id);
		mav.addObject("book", data.get());
		List<Bookshelf> list = bookshelfRepository.findAll();
		mav.addObject("bookshelfList", list);
		return mav;
	}

book.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Book - 本を入れる本棚を指定する</title>
</head>
<body>

<h1>Book</h1>

<a href="/">トップ</a>

<table>
	<tr>
		<td th:text="${book.title}"></td>
		<td th:text="${book.author}"></td>
	</tr>
</table>

<hr />

<div>どの本棚に入れますか?</div>
<form action="/book" method="post">
	<input type="hidden" name="bookId" th:value="${book.id}" />
	<div>
		<select name="bookshelfId">
			<option th:each="bs : ${bookshelfList}" th:value="${bs.id}" th:selected="${bs.id == book.id}" th:text="${bs.name}"></option>
		</select>
	</div>
	<div><input id="submit" type="submit" value="本棚に入れる" /></div>
</form>


</body>
</html>

本棚に入れない選択肢を追加する。

<div>どの本棚に入れますか?</div>
<form action="/book" method="post">
	<input type="hidden" name="bookId" th:value="${book.id}" />
	<div>
		<select name="bookshelfId">
			<option value="0">本棚に入れない</option>
			<option th:each="bs : ${bookshelfList}" th:value="${bs.id}" th:selected="${bs.id == book.id}" th:text="${bs.name}"></option>
		</select>
	</div>
	<div><input id="submit" type="submit" value="本棚に入れる" /></div>
</form>

コントローラでは、本棚検索して見つからないときは null を設定する。

	@RequestMapping(value="/book", method=RequestMethod.POST)
	public ModelAndView save(ModelAndView mav,
			@RequestParam("bookId") long bookId,
			@RequestParam("bookshelfId") long bookshelfId) {
		Optional<Book> data = repository.findById(bookId);
		Book book = data.get();
		Optional<Bookshelf> bsData = bookshelfRepository.findById(bookshelfId);
		if(bsData.isPresent()) {
			Bookshelf bookshelf = bsData.get();
			book.setBookshelf(bookshelf);
		} else {
			book.setBookshelf(null);
		}
		repository.saveAndFlush(book);
		return new ModelAndView("redirect:/books");
	}

Spring Bootを使ってWebアプリを作成する その3

Webアプリケーションを起動するたびにデータベースが初期化されてしまう設定になっていたので、それを修正する。

application.properties

spring.datasource.url=jdbc:hsqldb:hsql://localhost/shindan
spring.datasource.username=SA
spring.datasource.password=
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
spring.jpa.hibernate.ddl-auto=update

IndexController.java

package jp.kpc;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {
	@Autowired
	private ShindanRepository repository;

	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/form", method=RequestMethod.GET)
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		List<Shindan> list = repository.findAll();
		mav.addObject("list", list);
		return mav;
	}

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name,
			@RequestParam("eval") String eval) {
		int percent = (name + eval).hashCode() % 101;
		percent = Math.abs(percent);

		Shindan shindan = new Shindan();
		shindan.setName(name);
		shindan.setEval(eval);
		shindan.setPercent(percent);
		repository.saveAndFlush(shindan);
		String result = name + " さんの" + eval + "度は" + percent + "%です!!!";
		mav.addObject("result", result);

		List<Shindan> list = repository.findAll();
		mav.addObject("list", list);
		mav.setViewName("form");
		return mav;
	}

}

form.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
</head>
<body>

<h1>診断ページ</h1>

<p th:text="${result}"></p>

<form action="/form" method="post">
	おなまえ: <input type="text" name="name" />
	<br />
	診断: <input type="text" name="eval" />
	<input type="submit" value="診断" />
</form>

<hr />

<h3>これまでの診断結果</h3>
<table>
	<tr th:each="s : ${list}">
		<td th:text="${s.name} + ' さんの ' + ${s.eval} + '度は ' + ${s.percent} + '%です!!!'"></td>
	</tr>
</table>

</body>
</html>

CSSを使ってみる。

SpringBootの場合は、CSSファイルをsrc/main/resources の下の static 内に置く。

styles.css

@charset "UTF-8";

header {
	text-align: center;
	background: lightblue;
}

footer {
	text-align: center;
	background: lightblue;
}

form.html にヘッダーとフッターを追加する。
head要素内でCSSファイルを読み込む。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>

<header>
この部分はヘッダーですよ
</header>

<h1>診断ページ</h1>

<p th:text="${result}"></p>

<form action="/form" method="post">
	おなまえ: <input type="text" name="name" />
	<br />
	診断: <input type="text" name="eval" />
	<input type="submit" value="診断" />
</form>

<hr />

<h3>これまでの診断結果</h3>
<table>
	<tr th:each="s : ${list}">
		<td th:text="${s.name} + ' さんの ' + ${s.eval} + '度は ' + ${s.percent} + '%です!!!'"></td>
	</tr>
</table>


<footer>
この部分はフッターですよ
</footer>

</body>
</html>

入力フォーム部分などを中央寄せにする。

form.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>

<header>
この部分はヘッダーですよ
</header>

<h1>診断ページ</h1>

<p th:text="${result}"></p>

<form action="/form" method="post">
	<div>おなまえ</div>
	<div><input type="text" name="name" /></div>
	<div>なに度を診断しますか?</div>
	<div><input type="text" name="eval" /></div>
	<div><input id="submit" type="submit" value="診断" /></div>
</form>

<hr />

<h3>これまでの診断結果</h3>
<table>
	<tr th:each="s : ${list}">
		<td th:text="${s.name} + ' さんの ' + ${s.eval} + '度は ' + ${s.percent} + '%です!!!'"></td>
	</tr>
</table>


<footer>
この部分はフッターですよ
</footer>

</body>
</html>

styles.css

@charset "UTF-8";

header {
	text-align: center;
	background: #ffc107;
	color: white;
}

h1 {
	text-align: center;
	color: darkblue;
}

form {
	text-align: center;
}

footer {
	text-align: center;
	background: #ffc107;
	color: white;
}

#submit {
	background: lightgray;
	border: outset;
}

Spring Bootを使ってWebアプリを作成する その2

IndexControllerを作成する。
src/main/java の中にある jp.kpc を右クリックし[新規]-[クラス]を選択する。
名前に「IndexController」と入力し「完了」をクリック。

SpringBoot に対して、このクラスがコントローラであることを教えるために、@Controller アノテーションを追加する。
public class の前の行で「@Cont」を入力してCtrl+スペースを入力すると候補がリストアップされる。
org.springframework.stereotype のControllerを選択する。

package jp.kpc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
	@RequestMapping("/")
	public String index() {
		return "index";
	}
}

SpringBootアプリケーションを起動して、 http://localhost:8080/ にアクセスすると、エラーになるが、先ほどと違うエラーになっていることがわかる。

エラーメッセージを読むと、indexテンプレートが見つからないという意味なので、次はテンプレートを作成する。

src/main/resources の下にある templates を右クリックして[新規]-[その他]を選択する。
「HTMLファイル」を選択し「次へ」をクリック。
ファイル名を index.html にして「次へ」をクリック。
「新規HTMLファイル(5)」を選択して「完了」をクリック。

もうひとつURLのパスを割り当てて、そちらでも index.html を表示してみる。

package jp.kpc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {
	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping("/form")
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("index");
		return mav;
	}
}

form.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
</head>
<body>

<h1>診断ページ</h1>

<form action="/form" method="post">
	おなまえ: <input type="text" name="name" />
	<br />
	診断: <input type="text" name="eval" />
	<input type="submit" value="診断" />
</form>


</body>
</html>

IndexController の form メソッドで指定しているテンプレートを form に変更する。

	@RequestMapping("/form")
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

form メソッドは、GETメソッドだけ受け付けるように指定する。

	@RequestMapping(value="/form", method=RequestMethod.GET)
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

このように書き換えて再起動すると、http://localhost:8080/form へのアクセスは今まで通りに正常に動作するが、「診断」をクリックするとエラーになる。

form メソッドでGETメソッドだけを受け付けるようにしたために、POSTメソッドを受け取る場所がなくなった。

POSTを受け取るメソッドを用意する。

package jp.kpc;

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("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/form", method=RequestMethod.GET)
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

}

POSTメソッドでリクエストパラメータを受け取って、結果を渡してみる。

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name,
			@RequestParam("eval") String eval) {
		String result = name + " さんの" + eval + "度は?";
		mav.addObject("result", result);
		mav.setViewName("form");
		return mav;
	}

form.html に結果を表示するタグを追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
</head>
<body>

<h1>診断ページ</h1>

<p th:text="${result}"></p>

<form action="/form" method="post">
	おなまえ: <input type="text" name="name" />
	<br />
	診断: <input type="text" name="eval" />
	<input type="submit" value="診断" />
</form>


</body>
</html>

パーセンテージを計算して結果に追加する。

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name,
			@RequestParam("eval") String eval) {
		int percent = (name + eval).hashCode() % 101;
		percent = Math.abs(percent);
		String result = name + " さんの" + eval + "度は" + percent + "%です!!!";
		mav.addObject("result", result);
		mav.setViewName("form");
		return mav;
	}

過去の診断結果も表示できるようにする。

package jp.kpc;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {
	private List<String> list = new ArrayList<String>();

	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/form", method=RequestMethod.GET)
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name,
			@RequestParam("eval") String eval) {
		int percent = (name + eval).hashCode() % 101;
		percent = Math.abs(percent);
		String result = name + " さんの" + eval + "度は" + percent + "%です!!!";
		list.add(result);
		mav.addObject("result", result);
		mav.addObject("list", list);
		mav.setViewName("form");
		return mav;
	}

}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>診断ページ</title>
</head>
<body>

<h1>診断ページ</h1>

<p th:text="${result}"></p>

<form action="/form" method="post">
	おなまえ: <input type="text" name="name" />
	<br />
	診断: <input type="text" name="eval" />
	<input type="submit" value="診断" />
</form>

<hr />

<h3>これまでの診断結果</h3>

<table>
	<tr th:each="result : ${list}">
		<td th:text="${result}"></td>
	</tr>
</table>

</body>
</html>

配布フォルダにある hsqldb フォルダをコピーしてZドライブの適当な場所に貼り付ける。
hsqldb フォルダ内の hsqldb.bat を編集する。

cd data
java -classpath ../lib/hsqldb.jar org.hsqldb.server.Server --database.0 db/shindan --dbname.0 shindan

データベースとの接続を行えるようにするために、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>2.2.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>jp.kpc</groupId>
	<artifactId>example</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>example</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
		</dependency>
	</dependencies>

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

</project>

src/main/resources にある application.properties ファイルを開き、以下の内容を記述する。

spring.datasource.url=jdbc:hsqldb:hsql://localhost/shindan
spring.datasource.username=SA
spring.datasource.password=
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver

jp.kpc を右クリックして[新規]-[クラス]を選択する。
名前に「Shindan」を入力して「完了」をクリック。

以下の内容を入力する。
Eclipseがアノテーションの補完をしてくれない場合は、pom.xml を保存してない可能性があるので、保存を確認!

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Shindan {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String name;

	@Column
	@NotEmpty
	private String eval;

	@Column
	@NotNull
	private int percent;
}

メニューの[ソース]-[getterおよびsetterの生成]を選択する。
「すべて選択」をクリック。
挿入ポイントは「最後のメンバー」を選択し、「完了」をクリック。

すると、自動的にgetter/setter が追加される。

package jp.kpc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

@Entity
public class Shindan {
	@Id
	@GeneratedValue
	@Column
	@NotNull
	private long id;

	@Column
	@NotEmpty
	private String name;

	@Column
	@NotEmpty
	private String eval;

	@Column
	@NotNull
	private int percent;

	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 String getEval() {
		return eval;
	}

	public void setEval(String eval) {
		this.eval = eval;
	}

	public int getPercent() {
		return percent;
	}

	public void setPercent(int percent) {
		this.percent = percent;
	}
}

jp.kpc を右クリックして[新規]-[インターフェース]を選択する。
名前に「ShindanRepository」を入力。
拡張インターフェース「追加」をクリック。
JapRepository を選択して「OK」をクリック。
エラーが出ている T を Shindan に変更、ID を Long に変更して保存する。

package jp.kpc;

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

public interface ShindanRepository extends JpaRepository<Shindan, Long> {

}

コントローラにShindanをデータベースに保存するコードを追加する。

package jp.kpc;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class IndexController {
	private List<String> list = new ArrayList<String>();
	@Autowired
	private ShindanRepository repository;

	@RequestMapping("/")
	public String index() {
		return "index";
	}

	@RequestMapping(value="/form", method=RequestMethod.GET)
	public ModelAndView form(ModelAndView mav) {
		mav.setViewName("form");
		return mav;
	}

	@RequestMapping(value="/form", method=RequestMethod.POST)
	public ModelAndView formPost(ModelAndView mav,
			@RequestParam("name") String name,
			@RequestParam("eval") String eval) {
		int percent = (name + eval).hashCode() % 101;
		percent = Math.abs(percent);

		Shindan shindan = new Shindan();
		shindan.setName(name);
		shindan.setEval(eval);
		shindan.setPercent(percent);
		repository.saveAndFlush(shindan);

		String result = name + " さんの" + eval + "度は" + percent + "%です!!!";
		list.add(result);
		mav.addObject("result", result);
		mav.addObject("list", list);
		mav.setViewName("form");
		return mav;
	}

}

診断をひとつやってみてから、データベースに接続してみる。
manager.bat をダブルクリックする。
Typeで「HSQL Database Engine Server」を選択する。
URLを「jdbc:hsqldb:hsql://localhost/shindan」にして「OK」をクリック。

Spring Bootを使ってWebアプリを作成する

新規プロジェクトを作成する。

プロジェクトの種類は、Springスターター・プロジェクト
名前: example
グループ: jp.kpc
成果物: example
パッケージ: jp.kpc

[ウィンドウ]-[パースペクティブ]-[パースペクティブを開く]-[その他] で開いたダイアログから「JavaEE」を選択する。

pom.xml を開いて8行目の 2.2.10.RELEASE を 2.2.1.RELEASE に変更して保存する。

配布フォルダの システム開発実習/.m2 にある repository フォルダをコピーして、
Cドライブの「ユーザー」の下にある自分のフォルダ内にある .m2 の repository に上書きする。

その後、Eclipse で example プロジェクトを右クリックして「リフレッシュ」を選択する。

さらに、Eclipse で example プロジェクトを右クリックして[Maven]-[プロジェクトの更新]を選択する。

「マーカー」タブを見ると、Maven構成問題として「不明」というエラーが出ているが、右クリックして「削除」しておく。

プロジェクトを右クリックし[実行]-[SpringBootアプリケーション」を選択する。

「コンソール」タブの一番下に「Started ExampleApplication in x.xxx seconds (JVM running for ..」のメッセージが出ていれば、Webアプリケーションの起動に成功している。

Webアプリケーションが動作している状態で http://localhost:8080/ にアクセスすれば「Whitelabel Error Page」と表示される。

Gitでソースコード管理

hello プロジェクトを右クリックして[チーム]-[プロジェクトの共用]を選択する。

リポジトリー・タイプは「Git」を選択して「次へ」をクリック。

Gitリポジトリーの構成で、リポジトリーの「作成」をクリックして、個人ドライブの適当なフォルダを指定して「完了」をクリック。

こちらのメニューから「フィルター」を選択する。

フィルターのダイアログで「.*リソース」のチェックを外して「OK」をクリック。

.settings などを右クリックして[チーム]-[無視]を選択すると、.gitignore ファイルが作られる。

.gitignore を開いて、以下の内容を記述する。

/.settings/
*.class
*.jar
*.war
*.ear

# Eclipse
.project
.classpath
.settings

helloプロジェクトを右クリックして[チーム]-[コミット] を選択すると、Gitステージングが表示される。
.gitignore と HelloWorld.java をステージされた変更に移動する。
コミットメッセージに「最初のコミット」などのメッセージを記入して「コミット」をクリックする。

FizzBuzzでテスト駆動開発(TDD)の練習をしてみる

hello パッケージを右クリックして新規クラスで FizzBuzz を作成する。

FizzBuzz.java

package hello;

public class FizzBuzz {
	public String say(int i) {
		return "1";
	}
}

プロジェクトを右クリックして[チーム]-[コミット] を選択する。
FizzBuzz.javaをステージされた変更に移動してコミットメッセージを入力して[コミット]をクリック。

FizzBuzz.javaを右クリックし、[新規]-[その他]を選択。
JUnitテスト・ケースを選択。

FizBuzzTest.java

package hello;

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

import org.junit.jupiter.api.Test;

class FizzBuzzTest {

	@Test
	void testSay() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(1);
		assertEquals("1", s);
	}

}

FizzBuzzTestに2を追加する。

package hello;

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

import org.junit.jupiter.api.Test;

class FizzBuzzTest {

	@Test
	void testSay() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(1);
		assertEquals("1", s);
		s = fb.say(2);
		assertEquals("2", s);
	}

}

テストの再実行を行うとエラーになるので、FizzBuzzクラスを修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		return String.valueOf(i);
	}
}

テストの再実行を行うと、テストがグリーンになった!
Gitにコミットする。

さらにテストを追加する。
3回目はFizzと返してほしいので、テストを分けてみる。

package hello;

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

import org.junit.jupiter.api.Test;

class FizzBuzzTest {

	@Test
	void testSay() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(1);
		assertEquals("1", s);
		s = fb.say(2);
		assertEquals("2", s);
	}

	@Test
	void test3の場合はFizzを返す() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(3);
		assertEquals("Fizz", s);
	}
}

FizzBuzz.java を修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		if (i == 3) return "Fizz";
		return String.valueOf(i);
	}
}

テストの再実行を行ったらグリーンになったので、コミットする。

4の場合と5の場合のテストを追加する。

	@Test
	void test4の場合は4を返す() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(4);
		assertEquals("4", s);
	}

	@Test
	void test5の場合はBuzzを返す() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(5);
		assertEquals("Buzz", s);
	}

5の場合がエラーになるのでFizzBuzzを修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		if (i == 3) return "Fizz";
		if (i == 5) return "Buzz";
		return String.valueOf(i);
	}
}

テストの再実行を行ったらグリーンになったので、コミットする。

6の場合のテストを追加する。

	@Test
	void test6の場合はFizzを返す() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(6);
		assertEquals("Fizz", s);
	}

6の場合のテストがエラーになるのでFizzBuzzを修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		if (i % 3 == 0) return "Fizz";
		if (i == 5) return "Buzz";
		return String.valueOf(i);
	}
}

9と10の場合のテストを追加する。

	@Test
	void test9と10のテスト() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(9);
		assertEquals("Fizz", s);
		s = fb.say(10);
		assertEquals("Buzz", s);
	}

10の場合にエラーになるのでFizzBuzzを修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		if (i % 3 == 0) return "Fizz";
		if (i % 5 == 0) return "Buzz";
		return String.valueOf(i);
	}
}

11~14は従来のパターンで対応できているので、15の場合のテストを追加する。

	@Test
	void test15の場合はFizzBuzzを返す() {
		FizzBuzz fb = new FizzBuzz();
		String s = fb.say(15);
		assertEquals("FizzBuzz", s);
	}

FizzBuzzを修正する。

package hello;

public class FizzBuzz {
	public String say(int i) {
		if (i % 15 == 0) return "FizzBuzz";
		if (i % 3 == 0) return "Fizz";
		if (i % 5 == 0) return "Buzz";
		return String.valueOf(i);
	}
}

テストにパスしたのでコミットする。

ここまでで、FizzBuzzのTDDの練習は終わり。

Hello World

開発環境は Eclipse を使用する。

Eclipseは、プロジェクトという単位でプログラムを管理する。

新規Javaプロジェクトを作成する。
名前: hello

新規Javaクラスを作成する。
名前: HelloWorld

package hello;

public class HelloWorld {

	public static void main(String[] args) {
		System.out.println("Hello World!");
	}

}