7月8日

MsgDataDao で、Criteria API での実装を作ってみる。
クラス名は、MsgDataDaoCriteria.javaとする。

MsgDataDaoImplをコピーして、jp.abc パッケージに貼り付ける。
クラス名の入力が求められるので、「MsgDataDaoCriteria」と入力する。

変更する部分は、getAll() メソッドと findById() メソッドの2箇所。

コントローラ側は、以下の部分を変更する。

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

	@RequestMapping(value = "/msg", method = RequestMethod.POST)
	public String msgform(@Valid @ModelAttribute MsgData msgdata,
			Errors result, Model model) {
		System.out.println("msgform: " + msgdata.getMydata());
		model.addAttribute("msgdata", msgdata);
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください。");
			return "msgdata";
		}
		MsgDataDao dao = new MsgDataDaoCriteria();
		dao.add(msgdata);
		return "redirect:/msg";
	}

MsgDataDaoCriteria の、getAll() と findById() は以下のとおり。
MyDataDaoCriteriaからコピーして、MyData を MsgData に書き換えるだけ。

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

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

7月5日

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

前回までに、JSP・Entity・Daoまで作成したので、残りはControllerの実装を追加すること。

MyDataController.javaに以下のメソッドを追加する。

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

	@RequestMapping(value = "/msg", method = RequestMethod.POST)
	public String msgform(@Valid @ModelAttribute MsgData msgdata,
			Errors result, Model model) {
		System.out.println("msgform: " + msgdata.getMydata());
		model.addAttribute("msgdata", msgdata);
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください。");
			return "msgdata";
		}
		MsgDataDaoImpl dao = new MsgDataDaoImpl();
		dao.add(msgdata);
		return "redirect:/msg";
	}

プロパティエディタを作成する。
文字列を数値に変換して、そのIDのMyDataを取得する。
MyDataのIDを文字列化する。

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) {
		MyDataDaoImpl dao = new MyDataDaoImpl();
		MyData mydata = dao.findById(Long.parseLong(value));
		System.out.println("setAsTest: " + mydata);
		setValue(mydata);
	}
}

作成したプロパティエディタをコントローラで利用できるようにするために、コントローラの先頭に以下のメソッドを追加する。

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

	@RequestMapping(value = "/mydata", method = RequestMethod.GET)
	public String helo(@RequestParam(value = "s", required = false)String s, Model model) {

JSPに間違いがあったので修正。

		<c:if test="${datalist != null}">
		<table border="1">
			<tr>
				<th>ID</th>
				<th>名前<a href="?s=d">↓</a>
				<a href="?s=a">↑</a></th>
			</tr>
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<tr>
					<td><c:out value="${obj.id}" /></td>
					<td><c:out value="${obj.mydata.name}" /></td>
					<td><c:out value="${obj.title}" /></td>
					<td><c:out value="${obj.message}" /></td>
				</tr>
			</c:forEach>
		</table>
		</c:if>

HSQLDBの設定を変更

起動用のバッチファイルを修正。

cd C:\pleiades45\hsqldb-2.3.4\hsqldb\lib
java -cp hsqldb.jar org.hsqldb.Server

hsqldb/lib フォルダに server.properties ファイルを作成し、以下の内容を記述。

server.database.0=file:db/mydatabase
server.dbname.0=mydatabase

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/mydatabase" />
    </properties>
  </persistence-unit>
</persistence>

pom.xml の変更
HSQLDB Server のバージョンが2.3.4なので、pom.xml の hsqldb もバージョンをあわせる。

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

pom.xml を変更したので、プロジェクトを右クリックし、[Maven]-[プロジェクトの更新]と、[実行]-[Maven install]を改めて実行しておくこと。

7月1日

orderByによりエンティティのソート
getAll()メソッドで全エンティティを取得する部分で、ソートを追加してみる。

MyDataDaoCriteria.java

	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).orderBy(builder.asc(root.get("name")));
		list = manager.createQuery(query).getResultList();
		return list;
	}

サイトからのリクエストでソートできるようにする。

まずはJSPに「↑」と「↓」ボタンを追加して、UIを用意する。

		<c:if test="${datalist != null}">
		<table border="1">
			<tr>
				<th>ID</th>
				<th>名前<a href="?s=d">↓</a>
				<a href="?s=a">↑</a></th>
			</tr>
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<tr>
					<td><c:out value="${obj.id}" /></td>
					<td><c:out value="${obj.name}" /></td>
				</tr>
			</c:forEach>
		</table>
		</c:if>

ブラウザで http://localhost:8080/SpringMyApp/mydata を再読み込みして、名前の右に「↓↑」が追加されていることを確認する。

コントローラ側でパラメータを受け取れるようにする。
受け取ったパラメータを表示してみる。

MyDataController.java

	@RequestMapping(value = "/mydata", method = RequestMethod.GET)
	public String helo(@RequestParam(value = "s", required = false)String s, Model model) {
		model.addAttribute("title", "Sample");
		model.addAttribute("message", "MyDataのサンプルです。" + s);
		MyData mydata = new MyData();
		model.addAttribute("myData", mydata);
		MyDataDao<MyData> dao = new MyDataDaoCriteria();
		List<MyData> list = dao.getAll();
		model.addAttribute("datalist", list);
		return "mydata";
	}

「↓↑」をクリックすると、「MyDataのサンプルです。」のあとに「a」か「s」が表示されることで、受け取っていることがわかる。

DAOにソート用パラメータを渡せるようにする。
メソッドをひとつ追加する。
※インタフェースを修正するときは、既存メソッドは必ず残すこと!

特定の実装にのみメソッドを追加したい場合は、MyDataSortableDao extends MyDataDao のように、新しくインタフェースを作成し、MyDataDaoCriteria implements MyDataSortableDao とすると、このクラスだけに影響範囲を絞れる。
ここでは、2クラスしかないので、新しいインタフェースは作成しない。

MyDataDao.java

package jp.abc;

import java.util.List;

public interface MyDataDao<T> {
	public List<T> getAll();
	public List<T> getAll(String s);
	public T findById(long id);
	public List<T> findByName(String name);
	public List<T> find(String param);
	public void add(T data);
	public void update(T data);
	public void delete(T data);
	public void delete(long id);
}

MyDataDaoCriteriaとMyDataDaoImpleにgetAll(String s)を追加する。

MyDataDaoCriteria.java

	public List<MyData> getAll(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);
		if (s.equals("a")) {
			query.select(root).orderBy(builder.asc(root.get("name")));
		} else {
			query.select(root).orderBy(builder.desc(root.get("name")));
		}
		list = manager.createQuery(query).getResultList();
		return list;
	}

今は使ってないので、メソッドのテンプレート生成だけで済ませておく。
MyDataDaoImple.java

	public List<MyData> getAll(String s) {
		return null;
	}

パラメータが指定された場合は、コントローラから引数つきgetAll()を呼び出すようにする。

	@RequestMapping(value = "/mydata", method = RequestMethod.GET)
	public String helo(@RequestParam(value = "s", required = false)String s, Model model) {
		model.addAttribute("title", "Sample");
		model.addAttribute("message", "MyDataのサンプルです。" + s);
		MyData mydata = new MyData();
		model.addAttribute("myData", mydata);
		MyDataDao<MyData> dao = new MyDataDaoCriteria();
		List<MyData> list = null;
		if (s == null) {
			list = dao.getAll();
		} else {
			list = dao.getAll(s);
		}
		model.addAttribute("datalist", list);
		return "mydata";
	}

エンティティの連携

MsgDataエンティティを追加して、MyDataに対して複数のMsgDataを関連付けられるようにする。

MsgData.java

package jp.abc;

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

import org.hibernate.validator.constraints.NotEmpty;

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

	@Column
	private String title;

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

	@ManyToOne
	private MyData mydata;

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

	public long getId() {
		return id;
	}

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

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getMessage() {
		return message;
	}

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

	public MyData getMydata() {
		return mydata;
	}

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

MyDataにコードを追加する。

package jp.abc;

import java.util.List;

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

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

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

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

	@Column(nullable = true)
	private Integer age;

	@Column(nullable = true)
	private String memo;

	@OneToMany(cascade = CascadeType.ALL)
	@Column(nullable = true)
	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インタフェースを作成する。

package jp.abc;

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

public interface MsgDataDao<T> extends Serializable {
	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 の実装クラス MsgDataDaoImple を作成する。
内容的には MyDataDaoImple.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();
		MsgData msgdata = (MsgData)manager.createQuery("from MsgData where id = " + id).getSingleResult();
		manager.close();
		return msgdata;
	}

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

}

JSPを作成。mydata.jsp をコピーして、msgdata.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>
		<script src="<c:url value="/resources/js/main.js" />"></script>
		<title>${title}</title>
		<style type="text/css">
		h1 {
			font-size: 16pt;
			background-color: #ccccff;
			padding:3px;
		}
		p {
			color: #000066;
		}
		</style>
	</head>
	<body>
		<h1>${title}</h1>
		<h2>${message}</h2>
		<table>
		<form:form modelAttribute="msgdata">
			<form:errors path="*" element="div" />
			<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><input type="submit"></td></tr>
		</form:form>
		</table>
		<hr />
		<c:if test="${datalist != null}">
		<table border="1">
			<tr>
				<th>ID</th>
				<th>名前<a href="?s=d">↓</a>
				<a href="?s=a">↑</a></th>
			</tr>
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<tr>
					<td><c:out value="${obj.id}" /></td>
					<td><c:out value="${obj.name}" /></td>
				</tr>
			</c:forEach>
		</table>
		</c:if>
		<div id="footer"></div>
		<input type="button" value="テスト" onclick="testClicked()">
	</body>
</html>