スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Spring MVC Spring Data JPAを使って検索する

前回まででデータベースから全レコードを取得して、一覧画面に表示するところまでできた。
今回は一歩進めて、検索できるようにしてみる。
なお、掲載内容については、全レコードを取得する記事との差分をメインとする。

■全レコードを取得する記事
Spring MVC Spring Data JPAを使ってみる

◎動作検証にあたっての各バージョンは以下の通り
  • SpringFramework 3.2.8.RELEASE
  • Java 1.7
  • Tomcat 7.0

1.BookController.java


package jp.co.sample.book.controller;

import java.util.List;

import javax.validation.Valid;

import jp.co.sample.book.entity.BookEntity;
import jp.co.sample.book.form.BookForm;
import jp.co.sample.book.form.BookSearchForm;
import jp.co.sample.book.service.BookService;
import jp.co.sample.util.BookUtil;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Controller
@RequestMapping("book")
public class BookController {

private static Logger logger = LoggerFactory.getLogger(BookController.class);

@Autowired
protected BookService<BookEntity> bookService;

@RequestMapping(method = RequestMethod.GET)
public String index(BookForm form) {
logger.info("index start");
return "newBook";
}

  //一覧
@RequestMapping(value = "list", method = RequestMethod.GET)
//public String list(Model model) {
public String list(BookSearchForm form, Model model) {
logger.info("list start");
List<BookEntity> entites = bookService.findAll();
model.addAttribute("books", entites);
//model.addAttribute("bookSearchForm", form);
return "list";
}

//検索
@RequestMapping(value = "list/search", method = RequestMethod.GET)
public String search(BookSearchForm form, Model model) {
logger.info("search start");
List<BookEntity> entites = bookService.findByNameLike(form.getBookName());
model.addAttribute("books", entites);
return "list";
}

@RequestMapping(value = "create", method = RequestMethod.POST)
public String create(@Valid BookForm form, BindingResult result, Model model) {
logger.info("create start");
if (result.hasErrors()) {
return "newBook";
}
//TODO bookServiceより返却されたBookオブジェクトをmodelに追加する
return "result";
}
}

コントローラクラス。全レコードを取得するlistメソッドにおける今回の変更点は、引数に「BookSearchForm form」を追加している点となる。(参考までに修正前メソッドのシグニチャをコメントアウトしている。)理由は、検索情報を格納するBookSearchForm オブジェクトを生成するためで、追加しない場合、「HTTPステータス 500 - java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'bookSearchForm' available as request attribute」の実行例外が発生する。詳細については過去の記事で取り上げているので紹介しておく。
※過去の参考記事
Spring MVC リクエストパラメータ格納オブジェクトの検証

あと、もうひとつコメントアウトしている「model.addAttribute("bookSearchForm", form);」については、Modelが裏で「bookSearchForm」の名前でaddAttributeしてくれるため、記述する必要はない。(もちろん記述してもよい。)このあたりがSpringMVCの便利なところでもあり、知らないとなぜ「model.addAttribute("bookSearchForm", form);」がないのか不思議に思うところかもしれない。

検索については、書籍名の部分一致としている。こちらも「model.addAttribute("bookSearchForm", form);」を省略している。

2.BookSearchForm.java


package jp.co.sample.book.form;

import java.io.Serializable;

public class BookSearchForm implements Serializable {

private static final long serialVersionUID = 1L;

private String bookName;

//getter・setterは省略
}

bookNameプロパティを持ったBean。

3.BookDao.java


package jp.co.sample.book.dao;

import java.util.List;

public interface BookDao<T> {

List<T> findAll();
List<T> findByNameLike(String name);
}

BookDaoインターフェース。「findByNameLike」が今回実装する検索用メソッド。

4.BookDaoImpl.java


package jp.co.sample.book.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import jp.co.sample.book.entity.BookEntity;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class BookDaoImpl implements BookDao<BookEntity> {

private static Logger logger = LoggerFactory.getLogger(BookDaoImpl.class);

@PersistenceContext
private EntityManager manager;

@Override
@Transactional(readOnly=true)
public List<BookEntity> findAll() {
logger.info("findAll start");
Query query = manager.createQuery("from BookEntity");
return query.getResultList();
}

@Override
@Transactional(readOnly=true)
public List<BookEntity> findByNameLike(String name) {
logger.info("findByNameLike start");
Query query = manager.createQuery("from BookEntity where bookName like '%" + name + "%'");
return query.getResultList();
}
}

「findByNameLike」メソッドの実装。JPQLでのLike文はサンプルのようにすれば部分一致検索ができる。
また、「BookEntity」「bookName」はテーブル名、テーブル項目ではなく、「BookEntity」クラス、「bookName」プロパティである点を補足しておく。

■追加(2015/04/14)---------------------------------------------------------------------------------
下記記事にて紹介しているが、「Ordinal Parameters」「Named Parameters」を使用するのが望ましい点を追記しておく。
Spring Data JPA JPQLインジェクション対策
Query query = manager.createQuery("from BookEntity where bookName like '%" + name + "%'");

Query query = manager.createQuery("from BookEntity where bookName like ?1");
query.setParameter(1, "%" + name + "%");
----------------------------------------------------------------------------------------------------

5.BookService.java


package jp.co.sample.book.service;

import java.util.List;

public interface BookService<T> {

List<T> findAll();
List<T> findByNameLike(String name);
}


6.BookServiceImpl.java

package jp.co.sample.book.service;

import java.util.List;

import jp.co.sample.book.dao.BookDao;
import jp.co.sample.book.entity.BookEntity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class BookServiceImpl implements BookService<BookEntity> {

@Autowired
@Qualifier(value="bookDaoImpl")
BookDao<BookEntity> dao;

@Override
public List<BookEntity> findAll() {
return dao.findAll();
}

@Override
public List<BookEntity> findByNameLike(String name) {
return dao.findByNameLike(name);
}
}

「BookDao<BookEntity> dao」で「@Qualifier」アノテーションを使っているのは、BookDaoインターフェースを実装した別クラスが存在するため、どちらを使用するのかを指定している。BookDaoインターフェースを実装したクラスがひとつであれば「@Qualifier」アノテーションは不要。

7.list.java


<%@ 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>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<link href="<c:url value="/css/bootstrap.min.css" />" rel="stylesheet">
<link href="<c:url value="/css/bootstrap-theme.min.css" />" rel="stylesheet">
<script src="<c:url value="/js/bootstrap.min.js" />"></script>
<title>list</title>
</head>
<body>
<div class="container-fluid">
<form:form
action="${pageContext.request.contextPath}/book/list/search"
method="get" modelAttribute="bookSearchForm">
<form:label path="bookName" class="col-sm-3">Book Name</form:label>
<form:input path="bookName" class="col-sm-6"/>
<input type="submit" value="Search" class="btn btn-info btn-sm"/>
</form:form>
<hr/>
<table class="table table-striped">
<tr>
<th>Book Id</th>
<th>Book Name</th>
<th>Price</th>
</tr>
<c:forEach items="${books}" var="book">
<tr>
<td>${book.bookId}</td>
<td>${book.bookName}</td>
<td>${book.price}</td>
</tr>
</c:forEach>
</table>
</div>
</body>
</html>

CSSにBootstrapを適用している。

8.動作確認
①検索初期画面

検索

②検索結果
検索結果

正しく検索できることが確認できた。
ちなみに小文字の「aop」の場合、ヒットしない。また、未入力で検索した場合は、全件取得される。
また、URLに「?bookName=AOP」のクエリ文字列が追加され、GETメソッドで実行されていることも確認できる。


■過去のSpring関連記事
Spring関連記事 Index

スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

bookmount8

Author:bookmount8
システムエンジニア。サーバーサイドでjavaを扱うことが多い。最近は、ミドルやフロント周りも関心あり。

最新記事
カテゴリ
検索フォーム
最新コメント
月別アーカイブ
これまでの訪問者数
ブロとも申請フォーム

この人とブロともになる

RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。