概要
・検索フォームから条件を指定して検索すると、条件に一致する結果が一覧画面に表示される。
・検索条件は複合的に指定できることとする。
・検索結果は適切にフォーマットされることとする。
(日付はyyyy年MM月dd日など)
検索条件のリクエスト
リクエストの受信(Controller)
前回(【SpringBootチュートリアル】#7~ 検索画面の作成)作成した画面で、
検索ボタン押下のリクエスト「th:action=”@{/book/search}”」に対応させるメソッドをControllerに定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class BookContorller { @Autowired BookService service; @GetMapping("book/search") public String getSearch(@RequestParam Map<String,String> params, Model model) { // 取得したリクエストパラメータを引数として、サービス層に検索処理を委譲 ArrayList<Book> books = service.selectMany(params); // 検索結果のオブジェクトをmodelに格納 model.addAttribute("books", books); // 取得結果をReturn return "book/list"; } } |
・@RequestParam Map<String,String>
リクエストするパラメータが複数ある場合、Mapとして取得することができます。
Mapでは、<Key, Value>を<画面項目名, 画面項目値>として保持しています。
(例)書籍名の場合:<name, Javaの基本>
処理の委譲(Service)
サービス層ではビジネスロジックを組み立てます。
検索処理においては特別なロジックはないため、レポジトリ層に直接パラメータを渡した結果を受け取るだけです。
1 2 3 4 5 6 7 8 |
public class BookService { @Autowired BookRepository repository; public ArrayList<Book> selectMany(Map<String, String> params) { return repository.selectMany(params); } } |
DB接続(Repository)
1 2 3 |
public interface BookRepository { public ArrayList<Book> selectMany(Map<String, String> params); } |
・selectMany(Map<String, String> params);
selectMany:後述のXMLファイルのidになります。
引数には検索パラメータが渡されます。
SQL(XMLファイル定義)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo.domain.repository.BookRepository"> <resultMap type="com.example.demo.domain.model.Book" id="book"> <id column="id" property="id"></id> <result column="name" property="name"></result> <result column="isbn" property="isbn"></result> <result column="description" property="description"></result> <result column="publisher" property="publisher"></result> <result column="price" property="price"></result> <result column="publication_date" property="publication_date"></result> <result column="created_user" property="created_user"></result> <result column="created_at" property="created_at"></result> <result column="updated_user" property="updated_user"></result> <result column="updated_at" property="updated_at"></result> <result column="deleted_user" property="deleted_user"></result> <result column="deleted_at" property="deleted_at"></result> <result column="version" property="version"></result> </resultMap> <select id="selectMany" resultMap="book"> SELECT * FROM book WHERE deleted_at IS NULL <if test="name != ''"> <bind name="name_pattern" value="'%' + name + '%'" /> AND name LIKE #{name_pattern} </if> <if test="isbn != ''"> <bind name="isbn_pattern" value="isbn + '%'" /> AND isbn LIKE #{isbn_pattern} </if> <if test="publisher != ''"> <bind name="publisher_pattern" value="'%' + publisher + '%'" /> AND publisher LIKE #{publisher_pattern} </if> <choose> <when test="price_from != '' and price_to != ''"> AND price BETWEEN #{price_from} and #{price_to} </when> <when test="price_from != ''"> AND price <![CDATA[ >= ]]> #{price_from} </when> <when test="price_to != ''"> AND price <![CDATA[ <= ]]> #{price_to} </when> </choose> <choose> <when test="publication_date_from != '' and publication_date_to != ''"> AND publication_date BETWEEN #{publication_date_from} and #{publication_date_to} </when> <when test="publication_date_from != ''"> AND publication_date <![CDATA[ >= ]]> #{publication_date_from} </when> <when test="publication_date_to != ''"> AND publication_date <![CDATA[ <= ]]> #{publication_date_to} </when> </choose> </select> </mapper> |
・<resultMap type=”com.example.demo.domain.model.Book” id=”book”>
ここではBook(エンティティ)のフィールド名とテーブルのカラム名をマッピングさせています。
column:テーブルのカラム名
property:Book(エンティティ)のフィールド名
・<select id=”selectMany” resultMap=”book”>
BookRepositoryのメソッド名(selectMany)が対応しています。
また、resultMapは取得結果のマッピング先の指定であり、
<resultMap type=”com.example.demo.domain.model.Book” id=”book”>のidに対応しています。
・if test
指定したフィールドの値を検査し、条件に合致した場合のみif内のステートメントを追加します。
・<bind name=”name_pattern” value=”‘%’ + name + ‘%'” />
name(書籍名)は部分一致検索を実装するため、nameの前後に%を付与します。
ただ、%を文字列として直接ステートメントに記述できないため、name_patternに置き替えるためにbind(紐づけて)させています。
・<choose> <when test >
choose:タグ内の複数条件のうち、最初に合致した条件のみを適用します。
(例)price_from/price_to
price_from/price_toの両方が入力されていた場合、
最初の条件に合致するため、後続のwhenは判定されません。
もし、price_from/price_toのいずれかが未入力であれば、
後続のwhenへと処理が進みます。
・<![CDATA[ >= ]]>
XML内で記述した記号を、ただの文字列として扱うための指定です。
これが無いと、「=, <, >」をXML文書として認識してしまうので、
文字列として扱わせるために記号をCDATAセクションで囲っています。
検索結果の表示
検索一覧画面に表示する書式は、データによって整形することとしていますので、以下のようにします。
ISBN | 3桁-1桁-6桁-2桁-1桁区切り |
価格 | 0を3つ毎にカンマ区切り |
出版年月日 | yyyy年MM月dd日 |
※イメージは前回記事(【SpringBootチュートリアル】#7~ 検索画面の作成)参照
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class Book { // ISBN:3桁-1桁-6桁-2桁-1桁区切り public String isbnFormat() { String prefix = isbn.substring(0,3); // 接頭記号(3桁) String group = isbn.substring(3,4); // グループ記号(1桁) String publisher = isbn.substring(4,10); // 出版者記号(6桁) String bookName = isbn.substring(10,12); // 書名記号(2桁) String checkDigit = isbn.substring(12,13); // チェックディジット(1桁) return String.join("-", prefix, group, publisher, bookName, checkDigit); } // 価格:0を3つ毎にカンマ区切り public String priceCommaOf1000() { return String.format("%,d", price); } // 出版年月日:yyyy年MM月dd日 public String publicationDateOfYyyymmdd() { if(publication_date == null) return ""; SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日"); return sdf.format(publication_date).toString(); } } |
これでView側(list.html)に、整形された書籍情報が表示されます。
まとめ
・複数パラメータのリクエストは、@RequestParamでMapにマッピングして取得可能。
・Mybatisのif, choose-whenステートメントによって検索条件を動的に指定できる。
・検索結果一覧への表示用にメソッドを用意すると、画面では表示メソッドをコールするだけで済む。
コメント