12月9日

SpringMVCのHibernate Validationで入力チェックしようとすると、次のエラーが表示される。

The request sent by the client was syntactically incorrect.

    @RequestMapping(value = "/input", method = RequestMethod.POST)
    public String form1(@Valid @ModelAttribute Hoge hoge, Model model, Errors result) {
        if (result.hasErrors()) {
            model.addAttribute("message", "エラーですよ");
            return "input";
        }
 	// 正常処理
        return "input";
    }

java – Syntactically incorrect request sent upon submitting form with invalid data in Spring MVC (which uses hibernate Validator) – Stack Overflow

You have to modify the order of your arguments. Put the BindingResult result parameter always directly after the parameter with the @Valid annotation.

BindingResultパラメータは、@Validアノテーションをつけた引数の直後に書かないといけないらしい。

12月5日

リクエストパラメータをリダイレクト先に渡す方法
Modelに設定しておけば、リダイレクト先で参照できる。

    @RequestMapping(value = "/foo", method = RequestMethod.GET)
    public String from(@RequestParam(value = "id") Integer id, Model model) {
        model.addAttribute("id", id);
        return "redirect:/baa";
    }

    @RequestMapping(value = "/baa", method = RequestMethod.GET)
    public String to(@Param(value = "id") Integer id, Model model) {
       model.addAttribute("id", id);
       return "baa";
    }

10月31日

spring-securityによるユーザー認証

ライブラリの追加
pom.xmlに以下を追加する。

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-core</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.0.1.RELEASE</version>
</dependency>

設定ファイルを作成する

src/main/resources/spring フォルダに、spring-security.xml を作成する。
まずはXMLで指定したユーザーでログインできるようにする。
ユーザー名: user
パスワード: user

データベースに登録したユーザー認証できるように、あとで修正する。

アクセス許可の設定は、http要素(ハイライト部分)で設定する。

spring-security.xml

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd">
 
    <http pattern="/" security="none"/>
    <http pattern="/index.jsp" security="none"/>
    <http auto-config="true" >
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <form-login />
        <logout />
    </http>
 
    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="admin" authorities="ROLE_ADMIN" />
                <user name="user" password="user" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>

すべてのページにアクセスする前にアクセス権限のチェックを実行させるため、web.xml の最後に、フィルターを追加する。

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
 
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

データベースを使用した認証

ユーザーとユーザの権限を管理するためのテーブルを作成する。

CREATE TABLE users(
 user_id bigint NOT NULL identity,
 user_name varchar(50) NOT NULL,
 password varchar(50) NOT NULL,
 enabled tinyint NOT NULL,
 role varchar(100) NOT NULL
);

テーブルにテスト用のユーザーを登録する。

INSERT INTO users ( user_name , password , enabled, role)
   VALUES ( 'taro', 'abcd' , 1, 'ROLE_USER' );

spring-security.xml を、データベースを使った認証をするように書き換える。

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-4.0.xsd">
 
    <http pattern="/" security="none"/>
    <http pattern="/index.jsp" security="none"/>
    <http auto-config="true" >
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <form-login />
        <logout />
    </http>
 
    <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <beans:property name="driverClassName" value="org.hsqldb.jdbc.JDBCDriver" />
        <beans:property name="url" value="jdbc:hsqldb:hsql://localhost/mydatabase" />
        <beans:property name="username" value="sa" />
        <beans:property name="password" value="" />
    </beans:bean>
 
    <!-- ユーザとROLEを定義 -->
    <authentication-manager>
        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"
                 users-by-username-query="SELECT user_name, password, enabled FROM users WHERE user_name = ?"
                 authorities-by-username-query="SELECT user_name, role FROM users WHERE user_name = ?" />
        </authentication-provider>
    </authentication-manager>
</beans:beans>

ログインしているユーザー情報の取得

ログインしたユーザー情報を取得するには、コントローラの引数に Principal を追加する。
追加した Princical から getName()メソッドでユーザー名を取得できる。

@RequestMapping(value = "/home", method = RequestMethod.GET)
public String mydata(Model model, Principal p) {
    model.addAttribute("name", p.getName());
    model.addAttribute("title", "Home");
    model.addAttribute("message", "ホーム画面ですよ。");
    return "home";
}

9月26日

ツイートをDBに保存する
ツイートをDBに保存し、保存したツイートを表示できるようにする。

TweetDao.java

package jp.abc;

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

public interface TweetDao<T> extends Serializable {
	public List<T> getAll();
	public void add(T t);
}

TweetDaoImpl.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.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

public class TweetDaoImpl implements TweetDao<Tweet> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

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

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

}

コントローラで URL:timeline でツイート一覧を表示するコードを実装する。
ユーザーからツイートを受信したら、POSTメソッドでツイートをDBに保存する。

HomeController.java

 
	@RequestMapping(value = "/timeline", method = RequestMethod.GET)
	public String timeline(Model model) {
		model.addAttribute("title", "タイムライン");
		model.addAttribute("message", "ツイート一覧");
		UserDao<User> dao = new UserDaoImpl();
		List<User> list = dao.getAll();
		model.addAttribute("list", list);
		model.addAttribute("tweetModel", new TweetModel());
		TweetDao<Tweet> tdao = new TweetDaoImpl();
		List<Tweet> tweets = tdao.getAll();
		model.addAttribute("tweets", tweets);
		return "timeline";
	}

	@RequestMapping(value = "/timeline", method = RequestMethod.POST)
	public String tweet(@Valid @ModelAttribute TweetModel tm, Errors result, Model model) {
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください");
			return "timeline";
		}
		model.addAttribute("title", "タイムライン");
		model.addAttribute("message", "ツイート一覧");
		UserDao<User> dao = new UserDaoImpl();
		User u = dao.findById(tm.getUserId());
		Tweet t = new Tweet();
		t.setUser(u);
		t.setDate(new Date());
		t.setContent(tm.getText());
		TweetDao<Tweet> tdao = new TweetDaoImpl();
		tdao.add(t);
		return "redirect:/timeline";
	}

JSPでツイートの一覧を表示する。
ツイートした人のメールアドレスと日時、ツイート内容を表示する。

		<hr />
		<h2>${message}</h2>
				<c:if test="${tweets != null}">
			<table>
				<tr><th>ユーザー</th><th>日時</th><th>ツイート</th></tr>
				<c:forEach var="t" items="${tweets}" varStatus="status">
					<tr><td>${t.user.mail}</td><td>${t.date}</td><td>${t.content}</td></tr>
				</c:forEach>
			</table>
		</c:if>
	</body>

9月23日

チーム開発で作るものを決める

  • 2ちゃんねる
  • Twitter
  • LINE
  • SNS
  • Wiki
  • EC(ネットショップ)

提出用課題の作成

前回はユーザー登録できるところまで作ったので、ユーザーを指定してツイートする機能を追加する。
ログインは設定が必要なので、ここではログインなしで画面上でユーザーを指定してツイートさせる。

ツイートを表示する画面を作成する
まだツイートに対応するクラスを作ってないので、ツイートするユーザーを選択できるUIを作成する。
UIは、ラジオボタンまたはリストを使い、ツイートする内容はテキストエリアを使う。

timeline.jsp

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

<html>
	<head>
		<meta charset="utf-8">
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
		<title>${title}</title>
		<link rel="stylesheet" type="text/css" href="css/home.css">
	</head>
	<body>
		<h1>${title}</h1>
		<c:if test="${list != null}">
			<form:form modelAttribute="tweetModel">
			<table>
			<tr>
				<td>
				<form:select path="userId">
					<form:option value="-1" label="--- Select ---" />
					<form:options items="${list}" itemLabel="mail" itemValue="id"/>
				</form:select>
				</td>
			</tr>
			<tr>
				<td><form:textarea cols="40" rows="5" path="text"/></td>
			</tr>
			<tr>
				<td><input type="submit"></td>
			</tr>
			</table>
			</form:form>
		</c:if>
		<hr />
		<h2>${message}</h2>
	</body>
</html>

フォームモデルを作成する
ユーザーを指定してツイートするためにフォームモデルを追加する。

TweetModel.java

package jp.abc;

public class TweetModel {
	private long userId;
	private String text;
	public long getUserId() {
		return userId;
	}
	public void setUserId(long userId) {
		this.userId = userId;
	}
	public String getText() {
		return text;
	}
	public void setText(String text) {
		this.text = text;
	}
}

コントローラの変更
新しいURLとJSPに対応するためにコントローラを変更する。

	@RequestMapping(value = "/timeline", method = RequestMethod.GET)
	public String timeline(Model model) {
		model.addAttribute("title", "タイムライン");
		model.addAttribute("message", "ツイート一覧");
		UserDao<User> dao = new UserDaoImpl();
		List<User> list = dao.getAll();
		model.addAttribute("list", list);
		model.addAttribute("tweetModel", new TweetModel());
		return "timeline";
	}

以下のURLにアクセスすると、ユーザーを選択するリストと、ツイート内容を入力するテキストエリアが表示される。

http://localhost:8080/00name/timeline

Tweetクラスの作成
いつ、誰が、何をツイートしたかを保存すればよい。

Tweet.java

package jp.abc;

import java.util.Date;

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 org.hibernate.validator.constraints.NotEmpty;

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

	@Column(nullable = false)
	private Date date;

	@Column(nullable = false)
	@NotEmpty
	private String content;

	@ManyToOne
	private User user;

	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public Date getDate() {
		return date;
	}
	public void setDate(Date date) {
		this.date = date;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
}

9月12日

成績判定用のWebアプリ作成

UserとTweetの2つのエンティティを持つアプリを作成する。

プロジェクト名: 00name
00 : 番号
name : 名前をアルファベットで

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

[maven]-[プロジェクトの更新] と [実行]-[maven install]を実行する。
Tomcatサーバーにプロジェクトを追加してサーバーを起動する。

http://localhost:8080/00name にアクセスして「click to enter」が表示されればOK。

mvc-config.xmlの修正
以下の1行を修正する。

<?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>

pom.xml にライブラリを追加する。(MyShopからコピー)

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</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>
		<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.4</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>

	</dependencies>
</project>

pom.xml を修正したので、[maven]-[プロジェクトの更新] と [実行]-[maven install]を実行する。

ホーム画面を作成する。

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

<html>
	<head>
		<meta charset="utf-8">
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
		<title>${title}</title>
		<link rel="stylesheet" type="text/css" href="css/home.css">
	</head>
	<body>
		<h1>${title}</h1>
		<h2>${message}</h2>
		<table>
		<form:form modelAttribute="user">
			<form:errors path="*" element="div" />
			<tr>
				<td>
					<form:label path="mail">メール</form:label>
				</td>
				<td>
					<form:input path="mail" size="20" />
				</td>
			</tr>
			<tr>
				<td>
					<form:label path="password">パスワード</form:label>
				</td>
				<td>
					<form:password path="password" size="20" />
				</td>
			</tr>
			<tr><td><input type="submit"></td></tr>
		</form:form>
		</table>
		<hr />
		<c:if test="${list != null}">
			<table>
				<tr><th>ID</th><th>メール</th></tr>
				<c:forEach var="u" items="${list}" varStatus="status">
					<tr><td>${u.id}</td><td>${u.mail}</td></tr>
				</c:forEach>
			</table>
		</c:if>
	</body>
</html>

Userエンティティを作成する。

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.Size;

import org.hibernate.validator.constraints.NotEmpty;

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

	@Column(nullable = false)
	@NotEmpty
	@Email
	private String mail;

	@Column(nullable = false)
	@NotEmpty
	@Size(min = 8, max = 64)
	private String password;

	@Column(nullable = false)
	private String role;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getMail() {
		return mail;
	}

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

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getRole() {
		return role;
	}

	public void setRole(String role) {
		this.role = role;
	}
}

HomeController.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 HomeController {

	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String home(Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "ユーザー");
		model.addAttribute("list", null);
		model.addAttribute("user", new User());
		return "home";
	}

}

http://localhost:8080/00name/home にアクセスして入力フォームが表示されればOK。

データベースの設定
ユーザーをデータベースに格納できるようにする。

HSQLDB のデータベースインスタンスを追加する。

C:\pleiades45\hsqldb-2.3.4\hsqldb\lib\server.properties を編集する。
新しいデータベース名を twi にする。

server.database.0=file:db/mydatabase
server.dbname.0=mydatabase
server.database.1=file:db/myshop
server.dbname.1=myshop
server.database.2=file:db/twi
server.dbname.2=twi

HSQLDBを再起動し、コマンドプロンプトに以下の1行が表示されていればOK。

Database [index=0, id=0, db=file:db/twi, alias=twi] opened successfully in xxx ms.

Webアプリからデータベースにアクセスする設定を追加する

Javaリソースの src/main/resources の下の spring フォルダに、database.properties ファイルを作成する。
以下の内容を記述する。

database.driverClassName=org.hsqldb.jdbc.JDBCDriver
database.url=jdbc:hsqldb:hsql://localhost/twi
database.username=sa
database.password=

Javaリソースの src/main/resources に META-INF フォルダを作成し、persistence.xml ファイルを作成する。
以下の内容を記述する。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="update" />
      <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbc.JDBCDriver" />
      <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:hsql://localhost/twi" />
    </properties>
  </persistence-unit>
</persistence>

DAOを作成する

UserDao.java

package jp.abc;

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

public interface UserDao <T> extends Serializable {
	public List<T> getAll();
	public void add(T o);
}

UserDaoImpl.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.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

public class UserDaoImpl implements UserDao<User> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

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

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

}

HomeControllerを変更する。

package jp.abc;

import java.util.List;

import javax.validation.Valid;

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

@Controller
public class HomeController {

	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String home(Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "ユーザー");
		model.addAttribute("list", null);
		model.addAttribute("user", new User());
		UserDao<User> dao = new UserDaoImpl();
		List<User> list = dao.getAll();
		model.addAttribute("list", list);
		return "home";
	}

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String form(@Valid @ModelAttribute User user, Errors result, Model model) {
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください");
			return "/home";
		}
		user.setRole("user");
		UserDao<User> dao = new UserDaoImpl();
		dao.add(user);
		List<User> list = dao.getAll();
		model.addAttribute("list", list);
		return "redirect:/home";
	}
}

9月9日

ItemとPurchaseを関連付ける

Item.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.Min;

import org.hibernate.validator.constraints.NotEmpty;

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

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

	@Column
	@Min(0)
	private int price;

	@Column(nullable = true)
	private String desc;

	@OneToMany(cascade = CascadeType.MERGE)
	private List<Purchase> purchases;

	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 int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getDesc() {
		return desc;
	}
	public void setDesc(String desc) {
		this.desc = desc;
	}
	public List<Purchase> getPurchases() {
		return purchases;
	}
	public void setPurchases(List<Purchase> purchases) {
		this.purchases = purchases;
	}
}

Purchase.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;

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

	@ManyToOne
	private Item item;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public Item getItem() {
		return item;
	}

	public void setItem(Item item) {
		this.item = item;
	}
}

KartDaoImpl.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.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

public class KartDaoImpl implements KartDao<Kart> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

	public List<Kart> getAll() {
		return null;
	}

	public Kart findById(long id) {
		EntityManager manager = factory.createEntityManager();
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<Kart> query = builder.createQuery(Kart.class);
		Root<Kart> root = query.from(Kart.class);
		query.select(root).where(builder.equal(root.get("id"), id));
		Kart kart = manager.createQuery(query).getSingleResult();
		manager.close();
		return kart;
	}

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

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

Purchase を DB に保存するために、DAO を作成する。

PurchaseDao.java

package jp.abc;

import java.io.Serializable;

public interface PurchaseDao <T> extends Serializable {
	public void add(T o);
}

DAO の実装を作成する。

PurchaseDaoImpl.java

package jp.abc;

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

public class PurchaseDaoImpl implements PurchaseDao<Purchase> {
	private static EntityManagerFactory factory =
			Persistence.createEntityManagerFactory("persistenceUnit");

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

}

ItemContoller.java

package jp.abc;

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

import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.bind.annotation.RequestParam;

@Controller
public class ItemController {

	@RequestMapping(value = "/items", method = RequestMethod.GET)
	public String items(Model model) {
		Item item = new Item();
		model.addAttribute("title", "Sample");
		model.addAttribute("message", "MyShopのサンプル");
		ItemDao<Item> dao = new ItemDaoImpl();
		List<Item> list = dao.getAll();
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		return "items";
	}

	@RequestMapping(value = "items", method = RequestMethod.POST)
	public String form(@Valid @ModelAttribute Item item, Errors result, Model model) {
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください");
			return "items";
		}
		ItemDao<Item> dao = new ItemDaoImpl();
		dao.add(item);
		return "redirect:/items";
	}

	@RequestMapping(value = "/update", method = RequestMethod.GET)
	public String edit(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "Sample");
		model.addAttribute("message", "更新のページ");
		ItemDao<Item> dao = new ItemDaoImpl();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", dao.getAll());
		return "items";
	}

	@RequestMapping(value = "/update", method = RequestMethod.POST)
	public String update(@RequestParam(value = "id")int id,
			@Valid @ModelAttribute Item item,
			Errors result, Model model) {
		ItemDao<Item> dao = new ItemDaoImpl();
		dao.update(item);
		return "redirect:/items";
	}

	@RequestMapping(value = "/delete", method = RequestMethod.GET)
	public String delete(@RequestParam(value = "id")int id, Model model) {
		ItemDao<Item> dao = new ItemDaoImpl();
		dao.delete(id);
		return "redirect:/items";
	}

	@RequestMapping(value = "/home", method = RequestMethod.GET)
	public String home(Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		List<Item> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "home";
	}

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		KartDao<Kart> kdao = new KartDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart = new Kart();
		List<Purchase> plist = kart.getPurchases();
		if (plist == null) plist = new ArrayList<Purchase>();
		Purchase p = new Purchase();
		p.setItem(item);
		plist.add(p);
		kart.setPurchases(plist);
		kdao.add(kart);
		model.addAttribute("kart", kart);
		return "/home";
	}
}

ItemControllerでPurchaseをDBに格納する処理を追加する。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		KartDao<Kart> kdao = new KartDaoImpl();
		PurchaseDao<Purchase> pdao = new PurchaseDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart = new Kart();
		List<Purchase> plist = kart.getPurchases();
		if (plist == null) plist = new ArrayList<Purchase>();
		Purchase p = new Purchase();
		p.setItem(item);
		pdao.add(p);
		plist.add(p);
		kart.setPurchases(plist);
		kdao.add(kart);
		model.addAttribute("kart", kart);
		return "/home";
	}

Purchaseを格納する処理を追加して実行すると、JSPのエラーが発生する。
${kart.items} でエラーとなっているので、Kart – Purchase – Item の関連に書き換える。

		<c:if test="${kart != null}">
			カートID: ${kart.id}
			<table>
				<tr><th>商品名</th><th>価格</th></tr>
				<c:forEach var="obj" items="${kart.purchases}" varStatus="status">
					<tr><td>${obj.item.name}</td><td>${obj.item.price}</td></tr>
				</c:forEach>
			</table>
		</c:if>
	</body>

「org.hibernate.LazyInitializationException: could not initialize proxy – no Session」が発生する。
Kartを取得したときに、同時にKartのPurchaseのリストも取得するように@OneToManyアノテーションにfetch引数を追加する。

Kart.java

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

	@OneToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
	@Column(nullable = true)
	private List<Purchase> purchases;

サーバー側でカートを取得できるように、カートのIDをhidden属性でフォームに追加する。

		<table border="1">
		<tr>
			<th class="id">ID</th>
			<th class="name">商品名</th>
			<th class="price">価格</th>
			<th class="desc">説明</th>
			<th >&nbsp;</th>
		</tr>
		<c:if test="${datalist != null}">
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<form:form modelAttribute="item">
					<input type="hidden" name="id" value="${obj.id}" />
					<c:if test="${kart != null}">
						<input type="hidden" name="kid" value="${kart.id}" />
					</c:if>
					<form:errors path="*" element="div" />
					<tr>
						<td><c:out value="${obj.id}" /></td>
						<td><c:out value="${obj.name}" /></td>
						<td class="price"><c:out value="${obj.price}" /></td>
						<td><c:out value="${obj.desc}" /></td>
						<td><input type="submit" value="カートに入れる" /></td>
					</tr>
				</form:form>
			</c:forEach>
		</c:if>
		</table>

サーバー側でカートのIDをリクエストパラメータの引数で取得する。
カートIDは存在しない場合もあるので、required = false にする。
その場合は、プリミティブ型ではなく、Long型のオブジェクトで受け取る。(存在しない場合はnull)
カートIDがある場合は KartDao でクエリを実行して取得する。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id,
					   @RequestParam(value = "kid", required = false)Long kid, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		KartDao<Kart> kdao = new KartDaoImpl();
		PurchaseDao<Purchase> pdao = new PurchaseDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart;
		if (kid == null) {
			kart = new Kart();
		} else {
			kart = kdao.findById(kid);
		}
		List<Purchase> plist = kart.getPurchases();
		if (plist == null) plist = new ArrayList<Purchase>();
		Purchase p = new Purchase();
		p.setItem(item);
		pdao.add(p);
		plist.add(p);
		kart.setPurchases(plist);
		kdao.add(kart);
		model.addAttribute("kart", kart);
		return "/home";
	}

この状態で実行すると、2個目の商品を追加したときに「javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist」が発生する。

2回目はカートが存在しているので、add() ではなく update() する。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id,
					   @RequestParam(value = "kid", required = false)Long kid, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		KartDao<Kart> kdao = new KartDaoImpl();
		PurchaseDao<Purchase> pdao = new PurchaseDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart;
		if (kid == null) {
			kart = new Kart();
		} else {
			kart = kdao.findById(kid);
		}
		List<Purchase> plist = kart.getPurchases();
		if (plist == null) plist = new ArrayList<Purchase>();
		Purchase p = new Purchase();
		p.setItem(item);
		pdao.add(p);
		plist.add(p);
		kart.setPurchases(plist);
		if (kid == null) {
			kdao.add(kart);
		} else {
			kdao.update(kart);
		}
		model.addAttribute("kart", kart);
		return "/home";
	}

9月5日

カートに商品を保存する

Kart – Item の関連は間違い。

購入する商品に対応するクラスとして、新しく Purchase エンティティを用意する。

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 Purchase {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	private long id;

	@Column(nullable = false)
	private Item item;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public Item getItem() {
		return item;
	}

	public void setItem(Item item) {
		this.item = item;
	}
}

Kartに入れるのは購入する商品にする。

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;

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

	@OneToMany(cascade = CascadeType.MERGE)
	@Column(nullable = true)
	private List<Purchase> purchases;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public List<Purchase> getPurchases() {
		return purchases;
	}

	public void setPurchases(List<Purchase> purchases) {
		this.purchases = purchases;
	}
}

ItemからKartを削除する。

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.Min;

import org.hibernate.validator.constraints.NotEmpty;

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

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

	@Column
	@Min(0)
	private int price;

	@Column(nullable = true)
	private String desc;

	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 int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public String getDesc() {
		return desc;
	}
	public void setDesc(String desc) {
		this.desc = desc;
	}
}

コントローラの修正。
KartにはPurchaseを追加するようにして、PurchaseにはItemを設定する。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		KartDao<Kart> kdao = new KartDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart = new Kart();
		List<Purchase> plist = kart.getPurchases();
		if (plist == null) plist = new ArrayList<Purchase>();
		Purchase p = new Purchase();
		p.setItem(item);
		plist.add(p);
		kart.setPurchases(plist);
		kdao.add(kart);
		model.addAttribute("kart", kart);
		return "/home";
	}

9月2日

MyShop の 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>MyShop</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>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>
		<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.4</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>

	</dependencies>
</project>

商品をカートに入れる処理を実装する

現状では、「カートに入れる」ボタンを押すとエラーになる。
これは、POSTメソッドに対応するマッピングが用意されていないから。
なので、POSTメソッドに対応するマッピングをコントローラに追加する。

ItemController.java

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		List<Item> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "/home";
	}

JSP側はひとつのformに複数のsubmitがあるので、複数のformに分割する。

home.jsp

	<body>
		<h1>${title}</h1>
		<h2>${message}</h2>
		<table border="1">
		<tr>
			<th class="name">商品名</th>
			<th class="price">価格</th>
			<th class="desc">説明</th>
			<th >&nbsp;</th>
		</tr>
		<c:if test="${datalist != null}">
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<form:form modelAttribute="item">
				<form:errors path="*" element="div" />
					<tr>
						<td><c:out value="${obj.name}" /></td>
						<td class="price"><c:out value="${obj.price}" /></td>
						<td><c:out value="${obj.desc}" /></td>
						<td><input type="submit" value="カートに入れる" /></td>
					</tr>
				</form:form>
			</c:forEach>
		</c:if>
		</table>
		<hr />
	</body>

submitでidを受け取れるようにする
hidden属性のinput要素をform内に追加して、idをパラメータで渡せるようにする。
ボタンを押された商品名を、商品一覧の下に表示する。

		<tr>
			<th class="id">ID</th>
			<th class="name">商品名</th>
			<th class="price">価格</th>
			<th class="desc">説明</th>
			<th >&nbsp;</th>
		</tr>
		<c:if test="${datalist != null}">
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<form:form modelAttribute="item">
					<input type="hidden" name="id" value="${obj.id}" />
				<form:errors path="*" element="div" />
					<tr>
						<td><c:out value="${obj.id}" /></td>
						<td><c:out value="${obj.name}" /></td>
						<td class="price"><c:out value="${obj.price}" /></td>
						<td><c:out value="${obj.desc}" /></td>
						<td><input type="submit" value="カートに入れる" /></td>
					</tr>
				</form:form>
			</c:forEach>
		</c:if>
		</table>
		<hr />
		<c:if test="${item != null}">
			${item.name} がカートに追加されました。
		</c:if>

コントローラは、パラメータでIDを受け取る。
受け取ったIDでItemを検索して、JSPに渡す。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		return "/home";
	}

Kartエンティティを用意する。

Kart.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;

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

	@OneToMany(cascade = CascadeType.ALL)
	@Column(nullable = true)
	private List<Item> items;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public List<Item> getItems() {
		return items;
	}

	public void setItems(List<Item> items) {
		this.items = items;
	}
}

DAOとDAOの実装も用意する。

KartDao.java

package jp.abc;

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

public interface KartDao<T> extends Serializable {
	public List<T> getAll();
	public T findById(long id);
	public void add(T o);
}

KartDaoImpl.java

package jp.abc;

import java.util.List;

public class KartDaoImpl implements KartDao<Kart> {
	public List<Kart> getAll() {
		return null;
	}

	public Kart findById(long id) {
		return null;
	}

	public void add(Kart o) {
	}

}

コントローラで、カートに入れられた商品をKartに追加して、そのKartをJSPに渡すようにする。

	@RequestMapping(value = "/home", method = RequestMethod.POST)
	public String kart(@RequestParam(value = "id")int id, Model model) {
		model.addAttribute("title", "ホーム");
		model.addAttribute("message", "商品");
		ItemDao<Item> dao = new ItemDaoImpl();
		List<Item> list = dao.getAll();
		Item item = dao.findById(id);
		model.addAttribute("item", item);
		model.addAttribute("datalist", list);
		Kart kart = new Kart();
		list = kart.getItems();
		if (list == null) list = new ArrayList<Item>();
		list.add(item);
		kart.setItems(list);
		model.addAttribute("kart", kart);
		return "/home";
	}

JSP側でカートの中身を表示してみる。

		<c:if test="${item != null}">
			${item.name} がカートに追加されました。
		</c:if>
		<c:if test="${kart != null}">
			<table>
				<tr><th>商品名</th><th>価格</th></tr>
				<c:forEach var="obj" items="${kart.items}" varStatus="status">
					<tr><td>${obj.name}</td><td>${obj.price}</td></tr>
				</c:forEach>
			</table>
		</c:if>

カートをDBに保存するため、KartDaoImpleの実装を追加する。

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

detached entity passed to persist というメッセージで例外が発生したので、Kartのコードを一部修正する。

	@OneToMany(cascade = CascadeType.MERGE)
	@Column(nullable = true)
	private List<Item> items;

カートがDBに保存されるとIDが割り振られるので、画面上に表示してIDを確認する。

		<c:if test="${kart != null}">
			カートID: ${kart.id}
			<table>
				<tr><th>商品名</th><th>価格</th></tr>
				<c:forEach var="obj" items="${kart.items}" varStatus="status">
					<tr><td>${obj.name}</td><td>${obj.price}</td></tr>
				</c:forEach>
			</table>
		</c:if>