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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です