8月26日

BookshelfとBookを連携する

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

8月22日

Bookshelfを登録できるようにする

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

8月19日

Spring Bootの復習

簡単なWebアプリを作成してみる。

プロジェクト名: bookshelf
パッケージ名: jp.abc

プロジェクトを作成すると、pom.xml内でのSpringBootのバージョンが2.1.7になっているが、そのままではビルドエラーになるので、これを2.1.4に変更する。

BookshelfエンティティとBookエンティティを作成する。

Bookshelfのプロパティは name のみ。

Bookのプロパティは title と author の二つ。

作成するページ

  • トップページ http://localhost:8080/
  • Bookshelfページ http://localhost:8080/bookshelf
  • Bookページ http://localhost:8080/book

ビルドでエラーが出る人は、サーバーの 
 授業資料・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;
	}

}

前期提出課題

プロジェクト

プロジェクト名: se3NN (NN は学籍番号の下2桁)
パッケージ名: jp.abc

MyBootApp と同様のWebアプリケーションを作成する。
MyDataのかわりに、Userクラスを作成し、MsgDataクラスの代わりに Tweetクラスを作成する。

作成するページとURL

トップページ: http://localhost:8080/
ユーザー一覧&ユーザー登録: http://localhost:8080/user/
ユーザー編集ページ: http://localhost:8080/edit/{id}
ツイート一覧&ツイート投稿: http://localhost:8080/tweet/

エンティティ

User
・id – Long
・name – String
・profile – String
・tweets – List<Tweet>
Tweet
・id – Long
・time – java.util.Date
・content – String
・user – User

作成するHTMLテンプレート

  • index.html : トップページ
  • user.html : ユーザー一覧&ユーザー登録ページ
  • edit.html : ユーザー編集ページ
  • tweet.html : ツイート一覧&ツイート投稿ページ

トップページの内容

  • titleとh1タグに番号と名前を書くこと
  • ユーザー一覧&ユーザー登録ページへのリンクを作成
  • ツイート一覧&ツイート投稿ページへのリンクを作成

ユーザー一覧&ユーザー登録ページの内容

  • titleとh1タグに番号と名前を書くこと
  • h1のすぐ下にトップページへのリンクを作成
  • 入力フォームで名前(name)と自己紹介(profile)を入力できる
  • 自己紹介はtextareaにすること
  • 名前は1文字以上30文字以下
  • 自己紹介は1文字以上200文字以下
  • 送信ボタンを表示
  • 入力フォームの下に、登録済みユーザー一覧を表示する
  • ユーザー一覧にはID、名前、編集ページへのリンクを表示する

ユーザー編集ページの内容

  • titleとh1タグに番号と名前を書くこと
  • h1のすぐ下にトップページへのリンクを作成
  • 入力フォームに編集前の名前と自己紹介を表示
  • 入力フォームで名前と自己紹介を入力できる
  • 送信ボタンを表示

ツイート一覧&投稿ページの内容

  • titleとh1タグに番号と名前を書くこと
  • h1のすぐ下にトップページへのリンクを作成
  • 入力フォームで投稿内容(content)を入力できる
  • 投稿は1文字以上140文字以下
  • 投稿欄はtextareaにすること
  • 送信ボタンを表示
  • 日時はフォームに用意せず、コントローラが現在時刻を設定する
  • 入力フォームの下に、投稿済みツイート一覧を表示する
  • 投稿一覧にはID、名前、日時、投稿内容を表示する

application.properties

spring.datasource.url=jdbc:hsqldb:hsql://localhost/se3NN  ←NNは学籍番号の下2桁
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

hsqldb.bat
データベースインスタンスを追加する。
インスタンス名は se3NN (NNは学籍番号の下2桁)

cd (hsqldbの置き場所)\lib
java -cp hsqldb.jar org.hsqldb.Server --database.0 db/mydata --dbname.0 mydata --database.1 db/se3NN --dbname.1 se3NN

課題提出方法

プロジェクトを作成したフォルダを、エクスプローラで開く。

フォルダ「se3NN」(NNは学籍番号の下2桁)を右クリックし、[送る]-[圧縮(zip 形式)フォルダー]を選択する。
se3NN.zip ファイルができるので、そのファイルを提出する。

提出先

サーバーの以下のフォルダに提出。

\\stfs\ABKC\課題提出用\澤田\2019_SE3\課題提出

配点

接続先データベースや、プロジェクト名、番号・名前の間違いは、他人のものをコピーしたと判断し0点。
コピペもOKだけど最低限ができてなければ論外。
Webサイトを作って納品してもサイト名や会社名が間違ってたら、まず次の仕事はない。

  1. トップページ : 10点
    • titleとh1に番号と名前
    • ユーザーページへのリンク
    • 投稿ページへのリンク
  2. ユーザー一覧&ユーザー登録(GET) : 10点
    • titleとh1に番号と名前
    • 入力フォーム
    • 送信ボタン
    • ユーザー一覧
    • トップページへのリンク
  3. ユーザー一覧&ユーザー登録(POST) : 10点
    • 名前のエラーチェック
    • 自己紹介のエラーチェック
    • エラーメッセージ
    • データベースに保存
  4. ユーザー編集(GET) : 10点
    • titleとh1に番号と名前
    • 入力フォーム
    • 送信ボタン
    • トップページへのリンク
  5. ユーザー編集(POST) : 10点
    • 名前のエラーチェック
    • 自己紹介のエラーチェック
    • エラーメッセージ
    • データベースに保存
  6. ツイート一覧&投稿(GET) : 10点
    • titleとh1に番号と名前
    • 入力フォーム
    • 送信ボタン
    • ツイート一覧
    • トップページへのリンク
  7. ツイート一覧&投稿(POST) : 10点
    • 投稿内容のエラーチェック
    • 現在時刻を自動設定
    • エラーメッセージ
    • データベースに保存
  8. ツイート一覧で投稿者名が表示される(エンティティの連携) : 10点
  9. エラーメッセージの日本語化(全部できていれば) : 10点
  10. ツイート投稿でSELECT/OPTIONでユーザーを選択できる : 10点
  11. ツイート一覧でIDと名前の下に投稿が表示される : 10点
  12. ツイート一覧で複数行投稿がきちんと表示される : 10点
  13. トップページに授業の感想・意見を書く : 1〜20点

※1〜8はプログラムで採点しています。なので、1〜8の部分点はありません。
※14の授業の感想・意見は参考になった度合いで加点します。
※配点の総合計は100点を超えてますが、最大は100点です。

提出期限

7月19日(金)17:00

6月28日

エンティティの連携

MyData に、複数の MsgData を関連付ける。

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 MsgData {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	@NotNull
	private Long id;
	
	@Column
	private String title;
	
	@Column
	@NotEmpty
	private String message;
	
	@ManyToOne
	private MyData mydata;

	public MsgData() {
		super();
		mydata = new MyData();
	}

	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 getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public MyData getMydata() {
		return mydata;
	}

	public void setMydata(MyData mydata) {
		this.mydata = mydata;
	}
	
}

MyData にもエンティティの連携に関連するコードを追加する。

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.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;


@Entity
public class MyData {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	@NotNull
	private long id;

	@Column(length = 50, nullable = false)
	@NotEmpty
	private String name;

	@Column(length = 200, nullable = true)
	@Email
	private String mail;

	@Column(nullable = true)
	@Min(value=0)
	@Max(value=200)
	private Integer age;

	@Column(nullable = true)
	private String memo;

	@OneToMany
	private List<MsgData> msgdatas;

	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 getMail() {
		return mail;
	}

	public void setMail(String mail) {
		this.mail = mail;
	}

	public Integer getAge() {
		return age;
	}

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

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	public List<MsgData> getMsgdatas() {
		return msgdatas;
	}

	public void setMsgdatas(List<MsgData> msgdatas) {
		this.msgdatas = msgdatas;
	}

}

MsgDataを永続化するためのリポジトリを用意する。

package jp.abc.repositories;

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

import jp.abc.MyData;

public interface MsgDataRepository extends JpaRepository<MyData, Long> {

}

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

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${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;
}
tr {
  margin: 5x;
}
th {
  padding: 5px;
  color: white;
  background: darkgray;
}
td {
  padding: 5px;
  color: black;
  background: #f0f0f0;
}
</style>
</head>
<body>
<h1 th:text="${title}">MyMsg page</h1>
<p th:text="${msg}"></p>
<form method="post" action="/msg" 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}" />
  	</tr>
  	<tr>
  	  <td><label for="message">メッセージ</label></td>
  	  <td><textarea name="message" th:text="*{message}"></textarea></td>
  	</tr>
  	<tr>
  	  <td><label for="mydata">MYDATA_ID</label></td>
  	  <td><input type="text" name="mydata" /></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.mydata.name}"></td>
    <td th:text="${obj.title}"></td>
  </tr>
</table>
</body>
</html>

新しくコントローラを作成する。
まずはGETメソッドに対応する部分だけを作成。

MsgDataController.java

package jp.abc;

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.servlet.ModelAndView;

import jp.abc.repositories.MsgDataRepository;

@Controller
public class MsgDataController {

	@Autowired
	private MsgDataRepository repository;

	@RequestMapping(value = "/msg", method=RequestMethod.GET)
	public ModelAndView msg(ModelAndView mav) {
		mav.setViewName("showMsgData");
		mav.addObject("title", "Sample");
		mav.addObject("msg", "MsgDataのサンプルです。");
		MsgData msgdata = new MsgData();
		mav.addObject("formModel", msgdata);
		List<MsgData> list = repository.findAll();
		mav.addObject("datalist", list);
		return mav;
	}
}

コントローラにPOSTメソッドを追加する。

package jp.abc;

import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
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;

import jp.abc.repositories.MsgDataRepository;

@Controller
public class MsgDataController {

	@Autowired
	private MsgDataRepository repository;

	@RequestMapping(value = "/msg", method=RequestMethod.GET)
	public ModelAndView msg(ModelAndView mav) {
		mav.setViewName("showMsgData");
		mav.addObject("title", "Sample");
		mav.addObject("msg", "MsgDataのサンプルです。");
		MsgData msgdata = new MsgData();
		mav.addObject("formModel", msgdata);
		List<MsgData> list = repository.findAll();
		mav.addObject("datalist", list);
		return mav;
	}

	@RequestMapping(value = "/msg", method=RequestMethod.POST)
	public ModelAndView msgform(@Valid @ModelAttribute MsgData msgdata,
			Errors result,
			ModelAndView mav) {
		if (result.hasErrors()) {
			mav.setViewName("showMsgData");
			mav.addObject("title", "Sample [ERROR]");
			mav.addObject("msg", "値を再チェックしてください。");
			return mav;
		}
		repository.saveAndFlush(msgdata);
		return new ModelAndView("redirect:/msg");
	}
}

HTMLテンプレートの以下の部分でエラーが出てるので変更しておく。

  <input type="hidden" name="id" th:value="*{id}" />

↓↓↓↓↓↓

  <input type="hidden" name="id" value="0" />

6月21日

各入力フィールドにえらーを表示

エラーメッセージをそれぞれの入力フィールドの近くに表示するように、HTMLテンプレートを変更する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</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;
}
.err {
  color: red;
}
</style>
</head>
<body>

<h1>MyData page</h1>
<p th:text="${msg}"></p>
<form method="post" action="/mydata" 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><label for="age">年齢</label></td>
      <td>
        <input type="text" name="age" th:value="*{age}"
      			th:errorclass="err"/>
      	<div th:if="${#fields.hasErrors('age')}" th:errors="*{age}"
      		th:errorclass="err"></div>
      </td>
    </tr>
    <tr>
      <td><label for="mail">メール</label></td>
      <td>
        <input type="text" name="mail" th:value="*{mail}"
        	th:errorclass="err"/>
        <div th:if="${#fields.hasErrors('mail')}" th:errors="*{mail}"
        	th:errorclass="err"></div>
      </td>
    </tr>
    <tr>
      <td><label for="memo">メモ</label></td>
      <td><textarea name="memo" th:value="*{memo}" rows="5" cols="20"></textarea></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.name}"></td>
    <td><a th:href="@{'/edit/' + ${obj.id}}">編集</a></td>
    <td><a th:href="@{'/delete/' + ${obj.id}}">削除</a></td>
  </tr>
</table>


<pre th:text="${data}"></pre>


</body>
</html>

エラーメッセージを日本語で表示する

バリデーション条件を指定するアノテーションにmessageパラメータを追加することで、メッセージをカスタマイズできる。

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.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;


@Entity
public class MyData {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	@NotNull
	private long id;

	@Column(length = 50, nullable = false)
	@NotEmpty(message="空白は不可")
	private String name;

	@Column(length = 200, nullable = true)
	@Email(message="メールアドレスのみ")
	private String mail;

	@Column(nullable = true)
	@Min(value=0, message="0以上")
	@Max(value=200, message="200以下")
	private Integer age;

	@Column(nullable = true)
	private String memo;

	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 getMail() {
		return mail;
	}

	public void setMail(String mail) {
		this.mail = mail;
	}

	public Integer getAge() {
		return age;
	}

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

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

}

プロパティーファイルを用意してメッセージをまとめて扱う

バリデーション用アノテーションに message 引数を追加してメッセージをカスタマイズする方法とは別に、プロパティーファイルを使う方法がある。

resources フォルダにValidationMessages.properties ファイルを作成する。
作成したファイルを右クリックして[次で開く]-[Limyプロパティーエディター]を選択する。

プロパティーファイルの内容はバリデーション用アノテーションのクラス名に続けて「.message = 」を追加して、右辺にカスタマイズしたメッセージを記述する。
MinとMaxは、アノテーションにvalue引数で渡した値を {value} で受け取って表示できる。

javax.validation.constraints.NotEmpty.message = 空白は不可です。
javax.validation.constraints.Email.message = メールアドレスではありません。
javax.validation.constraints.Min.message = {value}より大きくしてください。
javax.validation.constraints.Max.message = {value}より小さくしてください。

HSQLDBサーバーを起動する

HSQLDBをダウンロードする。
検索して公式サイトからhsqldb-2.4.1.zip をダウンロードする。
ZIPファイルを解凍したフォルダを、C:\pleiades\ に置く。

デスクトップに起動用BATファイル hsqldb.bat を作成する。

cd C:\pleiades\hsqldb-2.4.1\hsqldb\lib
java -cp hsqldb.jar org.hsqldb.Server --database.0 db/mydata --dbname.0 mydata

Macの人は、「書類」フォルダにHSQLDBを解凍する。
書類フォルダなどにテキストファイル hsqldb.sh を作成する。

#!/bin/sh
cd ~/Documents/hsqldb/lib
java -cp hsqldb.jar org.hsqldb.Server --database.0 db/mydata --dbname.0 mydata

hsqldb.sh を作成後、ターミナルで以下のコマンドを実行する。

$ cd ~/Documents
# chmod +x ./hsqldb.sh

hsqldb.bat をダブルクリックすると、HSQLDBサーバーが起動する。
コマンドプロンプトの画面を閉じるとHSQLDBサーバーが終了するので閉じないこと。
誤って閉じないように最小化しておく。

application.properties ファイルに以下の内容を記述する。

spring.datasource.url=jdbc:hsqldb:hsql://localhost/mydata
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

6月14日

モデルとデータベース

データベースのテーブルに対応するPOJOクラスとしてMyDataを定義する。
MyDataクラスには、エンティティであることを示すために @Entity アノテーションをつける。

package jp.abc;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class MyData {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	private long id;

	@Column(length = 50, nullable = false)
	private String name;

	@Column(length = 200, nullable = true)
	private String mail;

	@Column(nullable = true)
	private Integer age;

	@Column(nullable = true)
	private String memo;

	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 getMail() {
		return mail;
	}

	public void setMail(String mail) {
		this.mail = mail;
	}

	public Integer getAge() {
		return age;
	}

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

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

}

MyData エンティティをデータベースに格納するためのインタフェースを定義する。
JpaRepositoryを継承して定義する。
テキストでは新たに repositories パッケージを作ってその中に作っているので、それにならう。

package jp.abc.repositories;

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

import jp.abc.MyData;

@Repository
public interface MyDataRepository extends JpaRepository<MyData, Long> {

}

MyDataを扱うために、コントローラを新しく用意する。

MyDataController

package jp.abc;

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

import jp.abc.repositories.MyDataRepository;

@Controller
public class MyDataController {

	@Autowired
	private MyDataRepository repository;

	@RequestMapping("/mydata")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("mydata");
		mav.addObject("msg", "this is sample content.");
		Iterable<MyData> list = repository.findAll();
		mav.addObject("data", list);
		return mav;
	}
}

新たに mydata.html を作成する。

mydata.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</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;
}
</style>
</head>
<body>

<h1>MyData page</h1>
<pre th:text="${data}"></pre>

</body>
</html>

エンティティのCRUD

フォームでデータを保存するために、HTMLに入力フォームを追加する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</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>MyData page</h1>

<form method="post" action="/mydata" th:object="${formModel}">
  <table>
    <tr>
      <td><label for="name">名前</label></td>
      <td><input type="text" name="name" th:value="*{name}" /></td>
    </tr>
    <tr>
      <td><label for="age">年齢</label></td>
      <td><input type="text" name="age" th:value="*{age}" /></td>
    </tr>
    <tr>
      <td><label for="mail">メール</label></td>
      <td><input type="text" name="mail" th:value="*{mail}" /></td>
    </tr>
    <tr>
      <td><label for="memo">メモ</label></td>
      <td><textarea name="memo" th:value="*{memo}" rows="5" cols="20"></textarea></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>


<pre th:text="${data}"></pre>


</body>
</html>

コントローラを変更する。
GETメソッドとPOSTメソッドで別の処理をやりたいので、(Javaクラスの)メソッドを分ける。
そのために、@RequestMapping アノテーションの引数で RequestMethodを指定する。
HTMLテンプレートのフォームから渡されるデータは、@ModelAttribute アノテーションでオブジェクト名を指定する。

package jp.abc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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;

import jp.abc.repositories.MyDataRepository;

@Controller
public class MyDataController {

	@Autowired
	private MyDataRepository repository;

	@RequestMapping(value = "/mydata", method = RequestMethod.GET)
	public ModelAndView index(
			@ModelAttribute("formModel") MyData mydata,
			ModelAndView mav) {
		mav.setViewName("mydata");
		mav.addObject("msg", "this is sample content.");
		Iterable<MyData> list = repository.findAll();
		mav.addObject("datalist", list);
		mav.addObject("data", list);
		return mav;
	}

	@RequestMapping(value = "/mydata", method = RequestMethod.POST)
	public ModelAndView form(
			@ModelAttribute("formModel") MyData mydata,
			ModelAndView mav) {
		repository.saveAndFlush(mydata);
		return new ModelAndView("redirect:/mydata");
	}
}

MyDataを編集するためのHTMLテンプレート edit.html を作成する。
mydata.html とほぼ同じなので、mydata.html をコピーしてファイル名を edit.html にして保存する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</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>MyData edit page</h1>

<form method="post" action="/edit" th:object="${formModel}">
  <input type="hidden" name="id" th:value="*{id}" />
  <table>
    <tr>
      <td><label for="name">名前</label></td>
      <td><input type="text" name="name" th:value="*{name}" /></td>
    </tr>
    <tr>
      <td><label for="age">年齢</label></td>
      <td><input type="text" name="age" th:value="*{age}" /></td>
    </tr>
    <tr>
      <td><label for="mail">メール</label></td>
      <td><input type="text" name="mail" th:value="*{mail}" /></td>
    </tr>
    <tr>
      <td><label for="memo">メモ</label></td>
      <td><textarea name="memo" th:value="*{memo}" rows="5" cols="20"></textarea></td>
    </tr>
    <tr>
      <td></td>
      <td><input type="submit" /></td>
    </tr>
  </table>
</form>


</body>
</html>

コントローラに初期データを登録するメソッドを追加する。
@PostConstruct アノテーションを指定すると、コンストラクタが呼ばれた直後に、このメソッドが呼び出される。

	@PostConstruct
	public void init() {
		MyData d1 = new MyData();
		d1.setName("tuyano");
		d1.setAge(123);
		d1.setMail("syoda@tuyano.com");
		d1.setMemo("this is data!");
		repository.saveAndFlush(d1);
		MyData d2 = new MyData();
		d2.setName("hanako");
		d2.setAge(15);
		d2.setMail("hanako@flower.com");
		d2.setMemo("this is data!");
		repository.saveAndFlush(d2);
		MyData d3 = new MyData();
		d3.setName("sachiko");
		d3.setAge(37);
		d3.setMail("sachiko@happy");
		d3.setMemo("this is data!");
		repository.saveAndFlush(d3);
	}

6月11日

条件分岐

ログインするWebサイトで、ログインしてない人に「ゲスト」と表示したり、ログインした人に「ようこそ〇〇さん!」のように表示するときに条件分岐を使用する。

テキストではp.180からの部分。

IndexController.java の最後にメソッドを追加する。
メソッド名、URL、HTMLテンプレートの名前を number にする。

	@RequestMapping("/number/{id}")
	public ModelAndView number(@PathVariable int id, ModelAndView mav) {
		mav.setViewName("number");
		mav.addObject("id", id);
		mav.addObject("check", id >= 0);
		mav.addObject("trueVal", "POSITIVE!");
		mav.addObject("falseVal", "negative...");
		return mav;
	}

HTMLテンプレートで th:if と th:unless を使用して条件分岐する。

<body>
<h1>条件分岐</h1>
<p th:if="${check}" th:text="${id} + ' is ' + ${trueVal}">message.</p>
<p th:unless="${check}" th:text="${id} + ' is ' + ${falseVal}">message.</p>
</body>

switchを使って多項分岐する

月の値から季節を表示する。

IndexController.java に month() メソッドを追加する。

	@RequestMapping("/month/{month}")
	public ModelAndView month(@PathVariable int month, ModelAndView mav) {
		mav.setViewName("month");
		int m = Math.abs(month) % 12;
		m = m == 0 ? 12 : m;
		mav.addObject("month", m);
		mav.addObject("check", Math.floor(m / 3));
		return mav;
	}

HTMLテンプレートは、month.html を追加する。

<body>
<h1>多項分岐</h1>

<div th:switch="${check}">
  <p th:case="0" th:text="|${month} - Winter|"></p>
  <p th:case="1" th:text="|${month} - Spring|"></p>
  <p th:case="2" th:text="|${month} - Summer|"></p>
  <p th:case="3" th:text="|${month} - Autumn|"></p>
  <p th:case="4" th:text="|${month} - Winter|"></p>
</div>

</body>

th:eachで繰り返し表示する

IndexController.java に list() メソッドを追加する。

	@RequestMapping("/list/")
	public ModelAndView list(ModelAndView mav) {
		mav.setViewName("list");
		ArrayList<String[]> data = new ArrayList<>();
		data.add(new String[] {"taro", "taro@yamada", "090-999-999"});
		data.add(new String[] {"hanako", "hanako@flower", "080-888-888"});
		data.add(new String[] {"sachiko", "sachiko@happy", "070-777-777"});
		mav.addObject("data", data);
		return mav;
	}

list.html を追加する。

<body>
<h1>繰り返し</h1>

<table>
  <tr>
    <th>NAME</th>
    <th>MAIL</th>
    <th>TEL</th>
  </tr>
  <tr th:each="obj:${data}">
    <td th:text="${obj[0]}"></td>
    <td th:text="${obj[1]}"></td>
    <td th:text="${obj[2]}"></td>
  </tr>
</table>

</body>

JavaScript のインライン処理

Webサイトを作っていると、コントローラからJavaScriptに値を渡したいことがあるので、そのやり方を試してみる。

InddexController.java に tax() メソッドを追加する。

	@RequestMapping("/tax/{tax}")
	public ModelAndView tax(@PathVariable int tax, ModelAndView mav) {
		mav.setViewName("tax");
		mav.addObject("tax", tax);
		return mav;
	}

tax.html を作成する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>消費税計算</title>
<style type="text/css">
h1 {
  font-size: 18pt;
  font-weight: bold;
  color: gray;
}
body {
  font-size: 13pt;
  color: gray;
  margin: 5px 25px;
}
</style>
<script th:inline="javascript">
function action() {
	var val = document.getElementById("text1").value;
	var res = parseInt(val * ((100 + /*[[${tax}]]*/) / 100));
	document.getElementById("msg").innerHTML = "include tax: " + res;
}
</script>
</head>
<body>
<h1>JavaScriptのインライン処理で消費税計算</h1>
<p id="msg"></p>
<input type="text" id="text1" />
<button onclick="action()">click</button>
</body>
</html>

テンプレートフラグメント

part.html を作成する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>part page</title>
<style th:fragment="frag_style">
div.fragment {
  border:solid 3px lightgray;
  padding: 0px 20px;
}
</style>
</head>
<body>

  <h1>Part page</h1>
  <div th:fragment="frag_body">
    <h2>fragment</h2>
    <div class="fragment">
      <p>this is fragment content.</p>
      <p>sample message..</p>
    </div>
  </div>

</body>
</html>

frag.html を作成する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</title>
<style th:include="part :: frag_style"></style>
</head>
<body>

  <h1>Top page</h1>
  <div th:include="part :: frag_body">
    <p>this is original content.</p>
  </div>

</body>
</html>

IndexController.java に frag() メソッドを追加する。

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

frag.html にヘッダーとフッターを別ファイルから読み込むようにしてみる。

<body>

  <div th:include="part :: header">
    <p>this is original content.</p>
  </div>

  <h1>Top page</h1>
  <div th:include="part :: frag_body">
    <p>this is original content.</p>
  </div>
  <p>ここが本文ですよ</p>

  <div th:include="part :: footer">
    <p>this is original content.</p>
  </div>

</body>

part.html に共通で使うヘッダーとフッターを用意する。

<body>

  <div th:fragment="header">
    <p>ここがヘッダーです</p>
    <hr />
  </div>

  <h1>Part page</h1>
  <div th:fragment="frag_body">
    <h2>fragment</h2>
    <div class="fragment">
      <p>this is fragment content.</p>
      <p>sample message..</p>
    </div>
  </div>

  <div th:fragment="footer">
    <hr />
    <p>ここがフッターです</p>
  </div>

</body>

モデルとデータベース

次章のモデルとデータベースに進む準備として、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 http://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.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>jp.abc</groupId>
	<artifactId>MyBootApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>MyBootApp</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</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>

6月7日

Gitにコミットする前に、.gitignoreファイルを編集する。
プロジェクト・エクスプローラーの右上の▽をクリックして「フィルターおよびカスタマイズ」を選択する。
「.*リソース」のチェックを外してOKする。
.gitignoreファイルが見えるようになるので、以下の行を追加する。

HELP.md
/target/
!.mvn/wrapper/maven-wrapper.jar
/.mvn/
mvnw
mvnw.cmd

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
:
:

プロジェクトを右クリックして[チーム]-[コミット]を選択する。
コミットするファイルを選択して、コミットメッセージを入力し、右下の「コミット」をクリック。
Gitリポジトリにコミットされる。

フォワードとリダイレクト

テキストではp.158のリスト3-23に書かれている内容。
ここでは、IndexControllerに追加してみる。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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 IndexController {

	@RequestMapping("/index/{num}")
	public String index(@PathVariable int num, Model model) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		model.addAttribute("msg", "total: " + res);
		return "index";
	}

	@RequestMapping("/mav/{num}")
	public ModelAndView mav(@PathVariable int num, ModelAndView mav) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		mav.addObject("msg", "total: " + res);
		mav.setViewName("index");
		return mav;
	}

	@RequestMapping(value="/form1", method=RequestMethod.GET)
	public ModelAndView form1(ModelAndView mav) {
		mav.setViewName("form1");
		mav.addObject("msg", "お名前を書いて送信してください");
		return mav;
	}

	@RequestMapping(value="/form1", method=RequestMethod.POST)
	public ModelAndView send(@RequestParam("text1")String str,
			@RequestParam("text2")String str2,
			ModelAndView mav) {
		int p = (str + str2).hashCode() % 101;
		p = Math.abs(p);
		mav.addObject("msg", "こんにちは" + str + "さん!");
		mav.addObject("result", str + "さんの"
							+ str2 + "度は、" + p +"%です。");
		mav.addObject("value", str);
		mav.addObject("text2", str2);
		mav.setViewName("form1");
		return mav;
	}

	@RequestMapping("/other")
	public String other() {
		return "redirect:/";
	}

	@RequestMapping("/home")
	public String home() {
		return "forward:/";
	}

}

Thymeleafをマスターする

IndexControllerの最後に以下のメソッドを追加する。

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

index.html に1行追加する。

<h1>helo page</h1>
<p class="msg">this is Thymeleaf sample page</p>
<p class="msg" th:text="${msg}"></p>
<p th:text="${new java.util.Date().toString()}"></p>

ユーティリティオブジェクト

Thymeleafにはユーティリティオブジェクトが用意されているので、使ってみる。
index.html に以下の内容を追加する。

<p th:text="${#dates.format(new java.util.Date(), 'dd/MMM/yyyy HH:mm')}"></p>
<p th:text="${#numbers.formatInteger(1234, 7)}"></p>
<p th:text="${#strings.toUpperCase('Welcome to Spring')}"></p>

パラメータへのアクセス

index.html にさらに追加する。

<p th:text="'from parameter... id=' + ${param.id[0]}"></p>
<p th:text="'name=' + ${param.name[0]}"></p>

URLにパラメータを追加すると、その値が表示される。

http://localhost:8080/date?id=12345&name=taro

選択オブジェクトへの変数式

既存のコードが長くなってきたので、新しくコントローラを作成する。

ObjectController.java

package jp.abc;

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

@Controller
public class ObjectController {
	@RequestMapping("/obj")
	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("obj");
		mav.addObject("msg", "current data");
		DataObject obj = new DataObject(123, "hanako", "hanako@flower");
		mav.addObject("obj", obj);
		return mav;
	}
}

HTMLも新規作成する。

obj.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>選択オブジェクト</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: 5x;
}
th {
  padding: 5px;
  color: white;
  background: darkgray;
}
td {
  padding: 5px;
  color: black;
  background: #f0f0f0;
}
</style>
</head>
<body>
<h1>選択オブジェクトの変数式</h1>
<p th:text="${msg}"></p>
<table th:object="${obj}">
  <tr><th>ID</th><td th:text="*{id}"></td></tr>
  <tr><th>NAME</th><td th:text="*{name}"></td></tr>
  <tr><th>MAIL</th><td th:text="*{value}"></td></tr>
</table>

</body>
</html>

リテラル置換

obj.html に以下のコードを追加する。

<div th:object="${obj}">
  <p th:text="|my name is *{name}. mail address is *{value}.|">message.</p>
</div>

HTMLコードの出力

ObjectController に以下のコードを追加する。

	public ModelAndView index(ModelAndView mav) {
		mav.setViewName("obj");
		mav.addObject("msg", "current data");
		DataObject obj = new DataObject(123, "hanako", "hanako@flower");
		mav.addObject("obj", obj);
		mav.addObject("code", "msg1<hr />msg2<br />msg3");
		return mav;
	}

obj.html に以下のコードを追加する。

<p th:text="${code}">message.</p>

条件式

IndexControllerの date() メソッドを以下のように書き換える。

	@RequestMapping("/date")
	public ModelAndView date(
			@RequestParam("id")int id,
			ModelAndView mav) {
		mav.setViewName("index");
		mav.addObject("id", id);
		mav.addObject("check", id % 2 == 0);
		mav.addObject("trueVal", "Even number!");
		mav.addObject("falseVal", "Odd number..");
		return mav;
	}

index.html に以下のコードを追加する。

<p th:text="${id} + ' is ' + (${check} ? ${trueVal} : ${falseVal})"></p>

5月31日

URLの構成

http://hostname:port/contextpath/path?querystring

Spring Boot で Hello World

HeloController.java

package jp.abc;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HeloController {

	@RequestMapping("/")
	public String index() {
		return "Hello Spring Boot World!";
	}
}

@RequestMappingアノテーションでいろいろなURLパスに対応する機能を追加できる。

HeloController.java に以下のメソッドを追加する。
http://localhost:8080/hello

	@RequestMapping("/hello")
	public String hello() {
		return "Hello World! - path = /hello";
	}

パス変数

URLパスに変数を含めることができる。
@PathVariable アノテーションを使用する。

http://localhost:8080/sum/123

	@RequestMapping("/sum/{num}")
	public String sum(@PathVariable int num) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		return "total: " + res;
	}

JSONを出力する

テキストではDataObjectクラスを同一ソース内に書いているが好ましくないので、別のクラスとして新規作成する。

package jp.abc;

public class DataObject {
	private int id;
	private String name;
	private String value;

	public DataObject(int id, String name, String value) {
		this.id = id;
		this.name = name;
		this.value = value;
	}

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

HeloControllerに以下のフィールドとメソッドを追加する。

	String[] names = { "tsuyano", "hanako", "taro",
					   "sachiko", "ichiro" };
	String[] mails = {
			"syoda@tsuyano.com",
			"hanako@flower",
			"taro@yamada",
			"sachiko@happy",
			"ichiro@baseball"
	};

	@RequestMapping("/users/{id}")
	public DataObject users(@PathVariable int id) {
		return new DataObject(id, names[id], mails[id]);
	}

Thymeleafを使ってWebサイトを作る

Thymeleafを使うには、ライブラリを追加する必要がある。
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 http://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.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>jp.abc</groupId>
	<artifactId>MyBootApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>MyBootApp</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>
		</dependency>

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

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

</project>

[Maven]-[プロジェクトの更新]と[実行]-[maven install]を実施する。

テキストではHeloControllerを修正しているが、ここでは新規クラスを作成する。

IndexController.java

package jp.abc;

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

@Controller
public class IndexController {

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

HTMLファイルを作成する。
src/main/resources/templates を右クリックして新規作成で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>helo page</h1>
<p class="msg">this is Thymeleaf sample page</p>
</body>
</html>

テンプレートに値を表示する

<body>

<h1>helo page</h1>
<p class="msg">this is Thymeleaf sample page</p>
<p class="msg" th:text="${msg}"></p>

</body>

コントローラでは引数にModelを追加する。
Modelを通してテンプレートに値を渡す。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

	@RequestMapping("/index/{num}")
	public String index(@PathVariable int num, Model model) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		model.addAttribute("msg", "total: " + res);
		return "index";
	}
}