11月12日

課題をたくさん完了させよう!

課題をなかなか完了できない場合は、課題を細かく分割する。

XxControllerを作成する。
XxControllerにURL=/hoge の GET リクエストを実装する。
hoge.jsp を作成する。
XxControllerのURL=/hoge の応答で、hoge.jsp を表示する。
XxControllerのURL=/hoge の GET リクエストで message データを設定する。
hoge.jsp で message データを表示する。
hoge.jsp に form を追加する。
XxControllerにURL /hoge の POST リクエストを実装する。
XxControllerのURL=/hoge の POST リクエストの応答で resp.jsp を表示する。

9月18日

エンティティの連携

MyDataController に POST メソッドのためのメソッドを追加する。

MyDataController.java

	@RequestMapping(value = "/msg", method = RequestMethod.POST)
	public String msgform(@Valid @ModelAttribute MsgData msgdata, Errors result, Model model) {
		System.out.println("msgform: " + msgdata.getMydata());
		if (result.hasErrors()) {
			model.addAttribute("title", "MsgData [Error]");
			model.addAttribute("message", "値が間違っています");
			return "msgdata";
		}
		MsgDataDao<MsgData> dao = new MsgDataDaoImpl();
		dao.add(msgdata);
		return "redirect:/msg";
	}

この変更でクエリ送信はできるようになるが、エラーが表示される。
java.lang.String を jp.abc.MyData に変換できないというエラー。

この対策のため、テキストp.357からのコードを追加する。
まず新規で MyDataPropertyEditor クラスを作成する。

MyDataPropertyEditor.java

package jp.abc;

import java.beans.PropertyEditorSupport;

public class MyDataPropertyEditor extends PropertyEditorSupport {
	public String getAsText() {
		MyData value = (MyData)getValue();
		System.out.println("getAsText: " + value);
		if (value == null) {
			return "";
		} else {
			return "" + value.getId();
		}
	}

	public void setAsText(String value) {
		MyDataDao<MyData> dao = new MyDataDaoImpl();
		MyData mydata = dao.findById(Long.parseLong(value));
		System.out.println("setAsText: " + mydata);
		setValue(mydata);
	}
}

Controller で、作成した PropertyEditor を利用できるように設定する。
MyDataController の先頭に以下のメソッドを追加する。

MyDataController.java

	@InitBinder
	protected void initBinder(HttpServletRequest req,
			ServletRequestDataBinder binder) throws Exception {
		binder.registerCustomEditor(MyData.class, new MyDataPropertyEditor());
	}

pom.xml

<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>
  <groupId>org.springframework.samples.service.service</groupId>
  <artifactId>MyData</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

    <properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Web -->
		<jsp.version>2.2</jsp.version>
		<jstl.version>1.2</jstl.version>
		<servlet.version>2.5</servlet.version>


		<!-- Spring -->
		<spring-framework.version>3.2.3.RELEASE</spring-framework.version>

		<!-- Hibernate / JPA -->
		<hibernate.version>4.2.1.Final</hibernate.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

	</properties>

	<dependencies>

		<!-- Spring MVC -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Other Web dependencies -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${servlet.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>${jsp.version}</version>
			<scope>provided</scope>
		</dependency>

		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>


		<!-- Test Artifacts -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring-framework.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.1</version>
		</dependency>

		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.2</version>
		</dependency>

		<dependency>
			<groupId>javax.transaction</groupId>
			<artifactId>jta</artifactId>
			<version>1.1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>3.2.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.2.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.3.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
				<exclusion>
					<groupId>xml-apis</groupId>
					<artifactId>xml-apis</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.0.1.Final</version>
		</dependency>

	</dependencies>
</project>

9月14日

Criteria API による検索

Criteria API は3つのクラスを組み合わせて利用する。

  • CriteriaBuilder
  • CriteriaQUery
  • Root

テキストでは MyDataDaoImpl を書き換えているが、ここでは新しく実装クラスを作成する。
クラス名を MyDataDaoCriteria とし、Criteria API による実装であることを、クラス名で示す。

新規クラスを作成するときにインタフェース MyDataDao を追加する。総称型 T は、MyData に変更する。
実装されていないメソッドの追加をして、メソッドを生成してもらう。

MyDataDaoCriteria.java

package jp.abc;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

public class MyDataDaoCriteria implements MyDataDao<MyData> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

	public List<MyData> getAll() {
		EntityManager manager = factory.createEntityManager();
		List<MyData> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<MyData> query = builder.createQuery(MyData.class);
		Root<MyData> root = query.from(MyData.class);
		query.select(root);
		list = manager.createQuery(query).getResultList();
		return list;
	}

	public MyData findById(long id) {
		// TODO 自動生成されたメソッド・スタブ
		return null;
	}

	public MyData findByName(String name) {
		// TODO 自動生成されたメソッド・スタブ
		return null;
	}

	public List<MyData> find(String s) {
		// TODO 自動生成されたメソッド・スタブ
		return null;
	}

	public void add(MyData data) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public void update(MyData data) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public void delete(MyData data) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public void delete(long id) {
		// TODO 自動生成されたメソッド・スタブ

	}

}

MyDataControllerの変更

MyDataDao に、MyDataDaoCriteria のインスタンスを設定する。

	@RequestMapping(value = "/mydata", method = RequestMethod.GET)
	public String mydata(Model model) {
		model.addAttribute("title", "MyData");
		model.addAttribute("message", "データを入力してください");
		MyData mydata = new MyData();
		model.addAttribute("myData", mydata);
		MyDataDao<MyData> dao = new MyDataDaoCriteria();
		List<MyData> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "mydata";
	}

クエリの実装

MyDataController の find() メソッドで Criteria API を使用する。

MyDataController.java

	@RequestMapping(value = "/find", method = RequestMethod.GET)
	public String find(Model model) {
		model.addAttribute("title", "MyData");
		model.addAttribute("message", "検索のサンプルです");
		MyDataDao<MyData> dao = new MyDataDaoCriteria();
		List<MyData> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "find";
	}

	@RequestMapping(value = "/find", method = RequestMethod.POST)
	public String search(@RequestParam(value = "fstr")String s, Model model) {
		model.addAttribute("title", "MyData");
		model.addAttribute("message", "「" + s + "」の検索結果");
		MyDataDao<MyData> dao = new MyDataDaoCriteria();
		List<MyData> list = dao.find(s);
		model.addAttribute("datalist", list);
		return "find";
	}

MyDataDaoCriteria の find() を実装する。

MyDataDaoCriteria.java

	public List<MyData> find(String s) {
		EntityManager manager = factory.createEntityManager();
		List<MyData> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<MyData> query = builder.createQuery(MyData.class);
		Root<MyData> root = query.from(MyData.class);
		List<Predicate> preds = new ArrayList<Predicate>();
		if (s != null) {
			preds.add(builder.like(root.<String>get("name"), "%" + s + "%"));
		}
		query.where(builder.and(preds.toArray(new Predicate[0])));
		query.select(root);
		list = manager.createQuery(query).getResultList();
		return list;
	}

さらに、id と mail でも検索できるようにする。

MyDataDaoCriteria.java

	public List<MyData> find(String s) {
		EntityManager manager = factory.createEntityManager();
		List<MyData> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<MyData> query = builder.createQuery(MyData.class);
		Root<MyData> root = query.from(MyData.class);
		List<Predicate> preds = new ArrayList<Predicate>();
		if (s != null) {
			try {
				long id = Long.parseLong(s);
				preds.add(builder.equal(root.get("id"), id));
			} catch (NumberFormatException e) {}
			preds.add(builder.like(root.<String>get("name"), "%" + s + "%"));
			preds.add(builder.like(root.<String>get("mail"), s + "@%"));
		}
		query.where(builder.or(preds.toArray(new Predicate[0])));
		query.select(root);
		list = manager.createQuery(query).getResultList();
		return list;
	}

エンティティの連携

1対多のオブジェクト構造を持つエンティティに対して、永続化の実装を追加する。
ここでは、MsgData クラスを追加し、MyData が複数の MsgData 要素を保持できるようにする。

まず MsgData クラスを作成する。

MsgData.java

package jp.abc;

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

import org.hibernate.validator.constraints.NotEmpty;

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

	private String title;

	@NotEmpty
	private String message;

	@ManyToOne
	private MyData 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 が複数の MsgData を保持できるようにする。

MyData.java

package jp.abc;

import java.util.List;

import javax.persistence.CascadeType;
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.Size;

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

	@Size(min = 1)
	@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;

	@OneToMany(cascade = CascadeType.ALL)
	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;
	}
}

MsgDataDao の用意

MsgDataDao.java

package jp.abc;

import java.util.List;

public interface MsgDataDao<T> {
	public List<T> getAll();
	public T findById(long id);
	public void add(T data);
	public void update(T data);
	public void delete(T data);
	public void delete(long id);
}

MsgDataDaoの実装クラスを作成する。

MsgDataDaoImpl.java

package jp.abc;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class MsgDataDaoImpl implements MsgDataDao<MsgData> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

	public List<MsgData> getAll() {
		EntityManager manager = factory.createEntityManager();
		Query query = manager.createQuery("from MsgData");
		List<MsgData> list = query.getResultList();
		manager.close();
		return list;
	}

	public MsgData findById(long id) {
		EntityManager manager = factory.createEntityManager();
		return (MsgData)manager.createQuery("from MsgData where id = " + id).getSingleResult();
	}

	public void add(MsgData data) {
		EntityManager manager = factory.createEntityManager();
		EntityTransaction tx = manager.getTransaction();
		tx.begin();
		manager.persist(data);
		tx.commit();
		manager.close();
	}

	public void update(MsgData data) {
		EntityManager manager = factory.createEntityManager();
		EntityTransaction tx = manager.getTransaction();
		tx.begin();
		manager.merge(data);
		tx.commit();
		manager.close();
	}

	public void delete(MsgData data) {
		EntityManager manager = factory.createEntityManager();
		EntityTransaction tx = manager.getTransaction();
		tx.begin();
		MsgData entity = manager.merge(data);
		manager.remove(entity);
		tx.commit();
		manager.close();
	}

	public void delete(long id) {
		delete(findById(id));
	}

}

ビューテンプレートの作成

src/main/webapp/WEB-INF/view の中に、JSPファイル msgdata.jsp を新規作成する。

msgdata.jsp

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${title}</title>
</head>
<body>
	<h1>${title}</h1>
	<p>${message}</p>
	<table>
	<form:form modelAttribute="msgData">
		<form:errors path="*" />
		<form:hidden path="id"/>
		<tr>
			<td><form:label path="title">タイトル</form:label></td>
			<td><form:input path="title" size="20" /></td>
		</tr>
		<tr>
			<td><form:label path="message">メッセージ</form:label></td>
			<td><form:textarea path="message" cols="20" rows="5" /></td>
		</tr>
		<tr>
			<td><form:label path="mydata">MYDATA_ID</form:label></td>
			<td><form:input path="mydata" size="20" /></td>
		</tr>
		<tr><td></td><td><input type="submit"></td></tr>
	</form:form>
	</table>
	<hr />
	<c:if test="${datalist != null}">
		<table border="1">
			<tr><th>ID</th><th>投稿者</th></tr>
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<tr>
					<td>${obj.id}</td>
					<td>${obj.mydata.name}</td>
					<td>${obj.title}</td>
					<td>${obj.message}</td>
				</tr>
			</c:forEach>
		</table>
	</c:if>
</body>
</html>

MyDataContoller に、新しいマッピングを追加する。

MyDataController.java

	@RequestMapping(value = "/msg", method = RequestMethod.GET)
	public String msg(Model model) {
		model.addAttribute("title", "MsgData");
		model.addAttribute("message", "MsgDataのサンプル");
		MsgData msgdata = new MsgData();
		model.addAttribute("msgData", msgdata);
		MsgDataDao<MsgData> dao = new MsgDataDaoImpl();
		List<MsgData> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "msgdata";
	}

8月31日

JPAによるデータベースの利用

5章のために、新規プロジェクトを作成する。

プロジェクト名: MyData

いつもの手順でプロジェクトを生成し、http://localhost:8080/MyData/ にアクセスする。
click to enter が表示されればOK。

pom.xmlの変更

pom.xml に以下の内容を追加する。

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.1</version>
		</dependency>

		<dependency>
			<groupId>org.hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>2.3.2</version>
		</dependency>

		<dependency>
			<groupId>javax.transaction</groupId>
			<artifactId>jta</artifactId>
			<version>1.1</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>3.2.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>3.2.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>1.3.4.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
				<exclusion>
					<groupId>xml-apis</groupId>
					<artifactId>xml-apis</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

pom.xml を保存したらエラーが出るかも。
エラーが出たらダイアログで「OK」してダイアログを閉じる。

プロジェクトを右クリックして、
[maven]-[プロジェクトの更新]
[実行]-[maven install]
を実行する。

134行目の ${spring.version} でエラーが出る場合は、${spring-framework.version} に変更する。

database.propertiesの作成

Javaリソースの下の src/main/resources の下の spring フォルダを右クリックし[新規]-[ファイル]を選択する。
ファイル名に database.properties を指定して、テキスト p.270 のリスト5-4の内容を入力して保存する。

database.properties

database.driverClassName=org.hsqldb.jdbc.JDBCDriver
database.url=jdbc:hsqldb:mem:mydata
database.username=sa
database.password=

persistence.xmlの作成

Javaリソースの下の src/main/resources を右クリックし[新規]-[フォルダ]を選択する。
フォルダ名に META-INF と入力して完了。

作成した META-INF フォルダを右クリックして[新規]-[XMLファイル]を選択する。(XMLファイルがなければ「その他」から探す)
ファイル名に persistence.xml と入力して完了。

テキスト p.272 の内容を入力する。

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="2.0"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
		http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
	<persistence-unit name="persistenceUnit"
					  transaction-type="RESOURCE_LOCAL">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<properties>
			<property name="hibernate.dialect"
					  value="org.hibernate.dialect.HSQLDialect" />
			<property name="hibernate.hbm2ddl.auto" value="create"/>
			<property name="javax.persistence.jdbc.driver"
					  value="org.hsqldb.jdbc.JDBCDriver"/>
			<property name="javax.persistence.jdbc.url"
					  value="jdbc:hsqldb:mem:mydata"/>
		</properties>
	</persistence-unit>
</persistence>

application-config.xml の変更

Javaリソースの下の src/main/resources の下の spring フォルダにある application-config.xml を変更する。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/data/jpa
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
 
    <context:property-placeholder location="classpath:spring/database.properties"/>
 
    <bean class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" id="dataSource">
        <property name="driverClassName" value="$database.driverClassName}" />
        <property name="url" value="${database.url}" />
        <property name="username" value="${database.username}" />
        <property name="password" value="${database.password}" />
        <property name="testOnBorrow" value="true" />
        <property name="testOnReturn" value="true" />
        <property name="testWhileIdle" value="true" />
        <property name="timeBetweenEvictionRunsMillis" value="1800000" />
        <property name="numTestsPerEvictionRun" value="3" />
        <property name="minEvictableIdleTimeMillis" value="1800000" />
    </bean>
</beans>

mvc-config.xmlの変更

mvc-config.xml

    <context:component-scan base-package="jp.abc"/>

MyDataクラスの作成

MyDataエンティティクラスを作成する。

Javaリソースの下の src/main/java を右クリックして、[新規]-[クラス]を選択する。
パッケージ名: jp.abc
クラス名: MyData

MyData.java

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

Data Access Object の作成

インタフェースを作成する。

MyDataDao.java

package jp.abc;

import java.io.Serializable;
import java.util.List;

public interface MyDataDao <T> extends Serializable {
	public List<T> getAll();
	public void add(MyData mydata);
}

実装クラスを作成する。
MyDataDaoImpl.java

package jp.abc;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class MyDataImpl implements MyDataDao<MyData> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

	public List<MyData> getAll() {
		EntityManager manager = factory.createEntityManager();
		Query query = manager.createQuery("from MyData");
		List<MyData> list = query.getResultList();
		manager.close();
		return list;
	}

	public void add(MyData mydata) {
		EntityManager manager = factory.createEntityManager();
		EntityTransaction tx = manager.getTransaction();
		tx.begin();
		manager.persist(mydata);
		tx.commit();
		manager.close();
	}

}

8月24日

復習として新規にWebアプリを作成する

入力フォームを用意(URLは、 http://localhost:8080/Rating/form )

「○○」さんの「○○」度は? [判定]

判定ボタンを押すと、

「○○さんの ○○度は、○○%です。」

と表示する。

プロジェクト名: Rating
テンプレート: Simple Spring Web Maven

プロジェクトを作成したら、以下を実行。

  1. [Maven]-[プロジェクトの更新]
  2. [実行]-[maven install]

文字化け対策

文字化け対策のために、web.xml を編集。
最後の <web-app> タグの手前に以下の内容を追加。
src/main/webapp/WEB-INF/web.xml

	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

パッケージの設定

使用するパッケージ名を mvc-config.xml に設定。
src/main/webapp/WEB-INF/mvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="jp.abc"/>


    <mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	        <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	        <property name="prefix" value="/WEB-INF/view/"/>
	        <property name="suffix" value=".jsp"/>
	</bean>

</beans>

ルートコンテキストの動作確認

サーバータブの Tomcat7 サーバーに Ratingプロジェクトを追加して Tomcat7 サーバーを再起動。
http://localhost:8080/Rating/ にアクセスすれば、click to enter が表示される。

FormModelの作成

javaリソースの src/java/main を右クリックし、クラスを新規作成する。
パッケージ名: jp.abc
クラス名; FormModel

FormModel.java

package jp.abc;

public class FormModel {
	private String name;
	private String title;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
}

ビューテンプレートの作成

form.jsp を以下のように作成する。

<!DOCTYPE html>

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
	<head>
		<meta charset="utf-8">
		<title>なんとか度判定</title>
	</head>
	<body>
		<h1>なんとか度判定</h1>
		<div style="font-size: 24pt;">${result}</div>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
		<form:input path="name"/>さんの<form:input path="title"/>度
		<br />
		<input type="submit" value="判定" />
		</form:form>
	</body>
</html>

コントローラを作成する

買い物メモのコントローラを作成する。
テキストでは MyAppController を再利用しているが、ここでは新しく RatingController を作成する。
※開発する際に、名前は重要!

Javaリソースの src/java/main の下にある jp.abc パッケージを右クリックして [新規]-[クラス] を選択。

名前: RatingController

まずは、GET メソッドに対応する部分だけ入力する。

RatingController.java

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class RatingController {

	@RequestMapping(value = "/form", method = RequestMethod.GET)
	public String form(Model model) {
        model.addAttribute("message", "名前と判定したいなにかを入力してください。");
        FormModel fm = new FormModel();
        model.addAttribute("formModel", fm);
		return "form";
	}

}

http://localhost:8080/Rating/form にアクセスする。
なんとか度判定の画面が表示されればOK。

次に POST メソッドに対応する部分を実装する。

RatingController.java

	@RequestMapping(value = "/form", method = RequestMethod.POST)
	public String rating(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {
            model.addAttribute("message", "名前と判定したいなにかを入力してください。");
       	    String s = formModel.getName() + " さんの "
       			 + formModel.getTitle() + "度は、";
       	    float r = (Math.abs(s.hashCode()) % 1001) / 10;
       	    s = s + r + "% です。";
       	    model.addAttribute("result", s);
       	    FormModel fm = new FormModel();
            model.addAttribute("formModel", fm);
	    return "form";
	}

入力チェックの追加

バリデーション用ライブラリをロードする

パッケージエクスプローラで、Memoプロジェクトの一番下にあるファイル pom.xml を開く。
エディタの下にある「pom.xml」タブを選択する。
ファイルの最後の の手前に以下の内容を追加する。

pom.xml

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.0.1.Final</version>
        </dependency>
    </dependencies>
</project>

保存すると、自動的にプロジェクトがビルドされ、ライブラリをダウンロードしてくれる。

JSPの変更

form.jsp にエラーメッセージを表示するためのタグを追加する。

form.jsp

		<h1>なんとか度判定</h1>
		<div style="font-size: 24pt;">${result}</div>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
		<form:errors path="*" cssStyle="color:red" element="div" />
		<form:input path="name"/>さんの<form:input path="title"/>度
		<br />
		<input type="submit" value="判定" />
		</form:form>

コントローラの修正

入力チェックのための@Validアノテーションと、結果を通知するための BindingResult引数を追加する。

RatingController.java

	@RequestMapping(value = "/form", method = RequestMethod.POST)
	public String rating(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {
        model.addAttribute("message", "名前と判定したいなにかを入力してください。");
        if (result.hasErrors()) {
            model.addAttribute("title", "Sample [ERROR]");
            model.addAttribute("message", "値を再チェックしてください");
        } else {
        	String s = formModel.getName() + " さんの "
        			 + formModel.getTitle() + "度は、";
        	float r = (Math.abs(s.hashCode()) % 1001) / 10;
        	s = s + r + "% です。";
        	model.addAttribute("result", s);
        	FormModel fm = new FormModel();
            model.addAttribute("formModel", fm);
        }
		return "form";
	}

FormModelの変更

FormModelに入力チェック方法を示すアノテーションを追加する。

FormModel.java

	@NotEmpty
	private String name;
	@NotEmpty
	private String title;

エラーメッセージをカスタマイズする

@NotEmpty などのアノテーションに message 引数を指定する。

FormModel.java

	@NotEmpty(message = "名前は必須です")
	private String name;
	@NotEmpty(message = "判定内容は必須です")
	private String title;

7月6日

フォームを使いこなす(続き)

複数のチェックボックスをまとめて作る

前回は複数のチェックボックスをまとめて作成するところまで作ったが、クエリ送信でエラーが発生していた。

エラーメッセージでは items must not be null と表示されていることから、${checkItems} が null であると推測できる。

コントローラで値を設定するように修正する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

チェックされた要素をメッセージに追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

ラベルと値を設定する

チェックボックスのラベルと値を設定するために、クラスを追加する。
javaリソースの下の src/main/java にある jp.abc パッケージを右クリックし、[新規]-[クラス]を選択する。
新規クラスのダイアログで名前に ListDataModel と入力し「完了」をクリック。
テキスト p.214 のコードを記述する。

ListDataModel.java

package jp.abc;

public class ListDataModel {
	private String label;
	private String data;
	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
	public String getData() {
		return data;
	}
	public void setData(String data) {
		this.data = data;
	}
	public ListDataModel(String label, String data) {
		this.label = label;
		this.data = data;
	}
}

JSPの <form:checkboxes> の内部を変更する。

				<tr><td>
					<form:checkboxes items="${checkItems}" path="checks"
					itemLabel="label" itemValue="data" delimiter=" "/>
				</td></tr>

MyAppControllerでは、ListDataModel を格納した List を作るように getList() を修正する。

MyAppController.java

package jp.abc;

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

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

@Controller
public class MyAppController {

	private List<ListDataModel> getList() {
		List<ListDataModel> list = new ArrayList<ListDataModel>();
		list.add(new ListDataModel("まっく", "Mac OS X"));
		list.add(new ListDataModel("うぃんどうず", "Windows"));
		list.add(new ListDataModel("りなくす", "Linux"));
		return list;
	}

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}
}

ラジオボタンの利用

ラジオボタンは、<form:radiobutton> タグを使用する。

ラジオボタンのために、FormModel に radio1 を追加する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
}

JSP にラジオボタンのタグを追加する。

			<form:form modelAttribute="formModel">
				<tr><td>input1:<form:input path="input1"/></td></tr>
				<tr><td>pass1:<form:password path="pass1" showPassword="on"/></td></tr>
				<tr><td>area1:<form:textarea path="area1" cols="40" rows="3"></form:textarea></td></tr>
				<tr><td><form:checkbox path="check1" label="checkbox 1"/></td></tr>
				<tr><td>
					<form:checkboxes items="${checkItems}" path="checks"
					itemLabel="label" itemValue="data" delimiter=" "/>
				</td></tr>
				<tr><td><form:radiobutton path="radio1" name="radio1"
					label="男性" value="male"/>
					<form:radiobutton path="radio1" name="radio1"
					label="女性" value="female"/>
				<tr><td><input type="submit" ></td></tr>
			</form:form>

GETメソッドを処理する helo() メソッドでは、1行だけ追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

POSTメソッドを処理する form() メソッドでも1行だけの追加。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "selected: " + formModel.getRadio1();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

ラジオボタンをまとめて作成する

複数のラジオボタンをまとめて作成するには、 <form:radiobuttons> タグを使用する。

まずは複数のラジオボタン用に、FormModel に radio2 を追加し、getter/setter を用意する。
FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
}

JSP に、複数のラジオボタンを表示するタグを追加する。

showMessage.jsp

				<tr><td><form:radiobutton path="radio1" name="radio1"
					label="男性" value="male"/>
					<form:radiobutton path="radio1" name="radio1"
					label="女性" value="female"/>
				</td></tr>
				<tr><td><form:radiobuttons path="radio2" name="radio2"
						items="${radiolist}" itemLabel="label" itemValue="data"
						delimiter=" " />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

GETメソッドを処理する helo() メソッドでは、FormModel の値を設定し、radiolist の名前で ListDataModel の List を渡してやる。

MyController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		return "showMessage";
	}

POST メソッドを処理する form() メソッドでは、選択された値を表示する1行の追加と、radiolist の名前で ListDataModel の List を渡してやる1行の追加のみ。

MyAppController.jav

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		return "showMessage";
	}

SELECTによる選択リストの作成

複数項目をリスト表示する <select>タグは、<form:select> タグで作成できる。

まず、FormModel に select 用のメンバーを用意する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;
	private String select1;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
	public String getSelect1() {
		return select1;
	}
	public void setSelect1(String select1) {
		this.select1 = select1;
	}
}

JSP の、radiobuttons と submit の間に SELECT用のタグを追加する。

				<tr><td><form:radiobuttons path="radio2" name="radio2"
						items="${radiolist}" itemLabel="label" itemValue="data"
						delimiter=" " />
				</td></tr>
				<tr><td><form:select path="select1" name="select1"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="false" />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

GETメソッドを処理する helo() メソッドでは、FormModel の値を設定し、optionlist の名前で ListDataModel の List を渡してやる。

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		fm.setSelect1("Windows");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

POST メソッドを処理する form() メソッドでは、選択された値を表示する1行の追加と、optionlist の名前で ListDataModel の List を渡してやる1行の追加のみ。

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		res += "<br />selected: " + formModel.getSelect1();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

リストで複数項目を選択可能にする

<form:select> タグの multiple属性をtrueにすることで、リストの複数項目を選択可能になる。

複数選択したデータを受け取るために、FormModel に select2 を追加し、getter/setterを生成する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;
	private String select1;
	private String[] select2;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
	public String getSelect1() {
		return select1;
	}
	public void setSelect1(String select1) {
		this.select1 = select1;
	}
	public String[] getSelect2() {
		return select2;
	}
	public void setSelect2(String[] select2) {
		this.select2 = select2;
	}
}

JSP の <form:select タグの multiple 属性を true にしたものを追加する。

showMessage.jsp

				<tr><td><form:select path="select1" name="select1"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="false" />
				</td></tr>
				<tr><td><form:select path="select2" name="select2"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="true" />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

コントローラで、GETメソッドを処理する helo() メソッドの変更箇所は1行のみ。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		fm.setSelect1("Windows");
		fm.setSelect2(new String[]{"Linux"});
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

POSTメソッドを処理する form() メソッドでは、選択された複数の項目を表示するコードを追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		res += "<br />selected: " + formModel.getSelect1();
		selected = formModel.getSelect2();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

6月26日

STSのインストール

テキスト「SpringMVC/Roo」のp.21「EclipseへのSTSのネットワーク経由インストール」の手順に従って、EclipseにSTSプラグインをインストールする。

Springプロジェクトの作成

  • [ファイル]-[新規]-[プロジェクト] を選択
  • プロジェクトウィザードで「Springプロジェクト」を選択
  • プロジェクト名は SpringMyApp と入力。
  • 「次へ」をクリック
  • テンプレートで「Simple Spring Web Maven」を選択して「完了」をクリック

DIの仕組み

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

Controllerを作成する

src/main/java を右クリックして[新規]-[クラス]を選択する。
パッケージ: jp.abc
クラス名: MyAppController

MyAppController.java

package jp.abc;

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

@Controller
public class MyAppController {

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("message", "this is sample. ok?");
		return "showMessage";
	}
}

mvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="jp.abc"/>


    <mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	        <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	        <property name="prefix" value="/WEB-INF/view/"/>
	        <property name="suffix" value=".jsp"/>
	</bean>

</beans>

修正した mvc-config.xml を読み込ませるために、Tomcat7サーバーを再起動する。
再起動後、 http://localhost:8080/SpringMyApp/helo にアクセスすると、「this is sample. ok?」と表示され、showMessage.jsp が表示されたことがわかる。

6月22日

JSPの暗黙オブジェクト

参考サイトを参照。
暗黙オブジェクト

index.jsp に暗黙オブジェクト request から取得できるデータを表示してみる。

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Hello World!</title>
</head>
<body>

<h1>Hello World!</h1>
<p>
日本語の表示を確認
</p>

<h3>式</h3>
<%=1+1 %>
<br />
<%=new java.util.Date() %>
<!-- HTMLコメント -->
<%-- JSPコメント --%>

<h3>JSP宣言</h3>
<%! int count = 10; %>
<%=count %>

<h3>スクリプトレット</h3>
<%
	out.print("scriptlet<br />");
%>

<h3>requestの情報</h3>
authType=<%=request.getAuthType() %><br />
characterEncoding=<%=request.getCharacterEncoding() %><br />
contentLength=<%=request.getContentLength() %><br />
contentType=<%=request.getContentType() %><br />
contextPath=<%=request.getContextPath() %><br />
localAddr=<%=request.getLocalAddr() %><br />
localName=<%=request.getLocalName() %><br />
localPort=<%=request.getLocalPort() %><br />
method=<%=request.getMethod() %><br />
pathInfo=<%=request.getPathInfo() %><br />
pathTranslated=<%=request.getPathTranslated() %><br />
protocol=<%=request.getProtocol() %><br />
queryString=<%=request.getQueryString() %><br />
remoteAddr=<%=request.getRemoteAddr() %><br />
remoteHost=<%=request.getRemoteHost() %><br />
remotePort=<%=request.getRemotePort() %><br />
remoteUser=<%=request.getRemoteUser() %><br />
requestedSessionId=<%=request.getRequestedSessionId() %><br />
requestURI=<%=request.getRequestURI() %><br />
scheme=<%=request.getScheme() %><br />
serverName=<%=request.getServerName() %><br />
serverPort=<%=request.getServerPort() %><br />
servletPath=<%=request.getServletPath() %><br />

</body>
</html>

パラメータ

URL にパラメータを追加すれば、request.getParameter() で値を取得できる。

index.jsp

<h3>パラメータ</h3>
a=<%=request.getParameter("a") %><br />
b=<%=request.getParameter("b") %><br />
c=<%=request.getParameter("c") %><br />

EL式

EL式は、${式} の書式で記述できる。

index.jsp

<h3>EL式</h3>
${100 * 100}<br />
<% request.setAttribute("test", new java.util.Date()); %>
${test}<br />

JSTL

2つのJARファイルを以下の場所からコピーして、WEB-INF/lib の下に貼り付ける。
\\kgakusei1\share\澤田\jstl\lib

c:setタグ

c:set タグで、値を設定できる。
実際には、暗黙オブジェクト request に対して、setAttribute() メソッドを呼んでいる。

jstl.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSTL</title>
</head>
<body>

<h1>JSTL</h1>

<c:set var="data" value="てすと!" />
${data}<br />

</body>
</html>

c:ifタグ

c:if タグで、単一の条件分岐を記述できる。

jstl.jsp

<c:set var="count" value="10" />
<c:if test="${count >= 10}" >
  count=${count}
</c:if>
<c:if test="${count >= 11}" >
  count=${count}
</c:if>

c:chooseタグ

c:choose タグで複数の条件分岐を記述できる。

jstl.jsp

<c:set var="age" value="9" />
<c:choose>
  <c:when test="${age >= 30}">30代以上</c:when>
  <c:when test="${age >= 20}">20代</c:when>
  <c:when test="${age >= 10}">10代</c:when>
  <c:otherwise>10歳以下</c:otherwise>
</c:choose>

c:forEachタグ

c:forEach タグでループを記述できる。
items属性にループの対象となる配列またはコレクションを指定し、var属性に取得した要素を格納する変数名を指定する。

jstl.jsp

<h3>c:forEach</h3>
<%
	List<String> list = new ArrayList<String>();
	list.add("Wii U");
	list.add("PS Vita");
	list.add("Nintendo 3DS");
	request.setAttribute("list",list);
%>

<c:forEach var="s" items="${list}">
  ${s}<br />
</c:forEach>

fmt:formatDateタグ

fmt:formatDateタグで書式を指定して日時を出力できる。

jstl.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>



<h3>fmt:formatDate</h3>
<%
	request.setAttribute("date", new Date());
%>
<fmt:formatDate value="${date}" pattern="yyyy年MM月dd日(E) a KK時mm分ss秒" /><br>

6月15日

Mapを使用したクラス

ゲームのタイトルをキーにして会社名を値として保存するクラスの続き。
ひとつのデータを格納して会社名を取得するテストを追加する。

GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}

このテストにパスするには、Fakeすればよい。

Games.java

import java.util.HashMap;
import java.util.Map;


public class Games {
	private Map<String, String> map = new HashMap<String, String>();

	public void add(String title, String company) {
		map.put(title, company);
	}

	public int getCount() {
		return map.size();
	}

	public String get(String string) {
		return "任天堂";
	}
}

さらにテストを追加する。

	@Test
	public void testGet1() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		String s = g.get("パズドラ");
		assertThat(s, is("ガンホー"));
	}

両方のテストをパスするためには、Mapを使用してきちんと実装するのがよい。

Games.java

	public String get(String key) {
		return map.get(key);
	}

ファイルから読み込む

map.txt を作成して、タイトルと会社名のデータを書き込んでおく。

map.txt

スーパーマリオブラザーズ,任天堂
パズドラ,ガンホー
妖怪ウォッチ,レベルファイブ
ドラゴンクエスト,スクエアエニックス

このファイルのデータを読み込むテストを作成する。

GamesTest.java

	@Test
	public void testLoad() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
	}

Gamesクラスにload()メソッドを追加して、指定されたファイルの内容を読み込み、マップに追加する。
ファイルを読み込む部分は、TextDocumentクラスのload()メソッドが参考になる。

Games.java

	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			while (in.ready()) {
				String s = in.readLine();
				String[] sa = s.split(",");
				map.put(sa[0], sa[1]);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
	}

テキストファイルに出力する機能を追加する

格納している内容をテキストファイルに出力し、出力したファイルを読み込んで行数が一致するかを確認するテストを作成する。

GamesTest.java

	@Test
	public void testSave() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		g.save("map1.txt");
		boolean result = g.load("map1.txt");
		assertThat(result, is(true));
		int n = g.getCount();
		assertThat(n, is(1));
	}

保存の処理内容は、TextDocumentクラスのsave()とほぼ同じ。
タイトルと会社名をカンマで区切って出力すればよい。
ループする部分はテキストp.444が参考になる。

Games.java

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			Set<String> keys = map.keySet();
			for (String key : keys) {
				String v = map.get(key);
				out.println(key + "," + v);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

要素の削除

指定したタイトルを削除する機能を追加する。
まずテストを作成する。

GamesTest.java

	@Test
	public void testDelete() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
		g.delete("パズドラ");
		n = g.getCount();
		assertThat(n, is(3));
		String s = g.get("パズドラ");
		assertNull(s);
	}

テストにパスするようにGames#delete()を実装する。
要素の削除はテキストには掲載されてないが、Eclipseの補完機能で表示される候補から探せば見つかる。

6月12日

ストリームとコレクション

前回、TextDocumentクラスをテスト駆動開発で作成し始めたので、その続きを実施する。

  1. 指定した行の文字列取得
  2. 指定した行を削除(削除した行の文字列を返す)
  3. 指定した文字列を行として末尾に挿入
  4. 指定した場所に指定した文字列を行として挿入
  5. 出力先ファイル名を指定してファイル出力

指定した場所に指定した文字列を行として挿入

TextDocument.java

	public void addLine(int i, String s) {
		lines.add(i, s);
	}

前回は、4まで実施したが、先頭行に挿入するテストしかやっていないので、別のテストを追加して動作確認を実施する。

まずは、途中に挿入するテストを追加してみる。

	@Test
	public void testAdd2() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine(3, "1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
		String s = td.getLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(3);
		assertThat(s, is("1234567890"));
		s = td.getLine(4);
		assertThat(s, is("abcde"));
	}

範囲外の -1 行目を引数で指定した場合は例外が発生する。
例外の発生をテストで書くには、Testアノテーションの引数に発生する例外を記述する。

TextDocumentTest.java

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd3() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(-1, "1234567890");
	}

10行しかないときに11行目に追加しようとしても例外が発生する。
まずは、例外が発生してエラーになるテストを書く。
TextDocumentTest.java

	@Test
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

例外が発生してエラーになることを確認してから、アノテーションに expected 引数を追加する。

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

次はファイルに書き込む機能を追加する。
まずはテストを書く。
ファイルに書けたかどうかは、ファイルを読み込んでテストしてみる。
TextDocumentTest.java

	@Test
	public void testSave() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(0, "1234567890");
		td.save("out.txt");
		td.load("out.txt");
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

TextDocumentに、保存の処理を実装する。

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			for (String s : lines) {
				out.println(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Mapの使用例

ゲームタイトルをキーにして会社名を保存するクラスを作成する。

Games.java

public class Games {
}

テストを作る。
GamesTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;


public class GamesTest {

	@Test
	public void testAdd() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		int n = g.getCount();
		assertThat(n, is(1));
		g.add("パズドラ", "ガンホー");
		n = g.getCount();
		assertThat(n, is(2));
	}

}

テストにパスするように Games を実装したら、さらにテストを追加する。
GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}