今回も引き続き、Spring BootのWEB画面上でデータ検索機能を追加した実装について述べる。ここでは、具体的なサンプルプログラムのソースコードを共有する。
前提条件
下記記事を参照のこと。
作成したサンプルプログラムの内容
作成したサンプルプログラムの構成は以下の通り。
なお、上図の赤枠は、前提条件に記載したソースコードと比較し、変更になったソースコードを示す。赤枠のソースコードについては今後記載する。
まず、検索用のFormオブジェクトは下記の通り。名前・生年月日(From,To)・性別の項目とプルダウンを設定している。
package com.example.demo; import lombok.Data; import java.util.LinkedHashMap; import java.util.Map; /** * Formオブジェクトのクラス */ @Data public class SearchForm { /** 検索用名前 */ private String searchName; /** 生年月日_年_from */ private String fromBirthYear; /** 生年月日_月_from */ private String fromBirthMonth; /** 生年月日_日_from */ private String fromBirthDay; /** 生年月日_年_to */ private String toBirthYear; /** 生年月日_月_to */ private String toBirthMonth; /** 生年月日_日_to */ private String toBirthDay; /** 検索用性別 */ private String searchSex; /** 生年月日_月のMapオブジェクト */ public Map<String,String> getMonthItems(){ Map<String, String> monthMap = new LinkedHashMap<String, String>(); for(int i = 1; i <= 12; i++){ monthMap.put(String.valueOf(i), String.valueOf(i)); } return monthMap; } /** 生年月日_日のMapオブジェクト */ public Map<String,String> getDayItems(){ Map<String, String> dayMap = new LinkedHashMap<String, String>(); for(int i = 1; i <= 31; i++){ dayMap.put(String.valueOf(i), String.valueOf(i)); } return dayMap; } /** 性別のMapオブジェクト */ public Map<String,String> getSexItems(){ Map<String, String> sexMap = new LinkedHashMap<String, String>(); sexMap.put("1", "男"); sexMap.put("2", "女"); return sexMap; } }
次に、データベースにアクセスするMapperクラスは以下の通り。全件検索するfindAllメソッドを、検索条件指定により検索するfindBySearchFormメソッドに変更している。
package com.example.demo; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; @Mapper public interface UserDataMapper { /** * ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する * @param searchForm 検索用Formオブジェクト * @return ユーザーデータテーブル(user_data)の検索条件に合うデータ */ Collection<UserData> findBySearchForm(SearchForm searchForm); /** * 指定したIDをもつユーザーデータテーブル(user_data)のデータを取得する * @param id ID * @return ユーザーデータテーブル(user_data)の指定したIDのデータ */ UserData findById(Long id); /** * 指定したIDをもつユーザーデータテーブル(user_data)のデータを削除する * @param id ID */ void deleteById(Long id); /** * 指定したユーザーデータテーブル(user_data)のデータを追加する * @param userData ユーザーデータテーブル(user_data)の追加データ */ void create(UserData userData); /** * 指定したユーザーデータテーブル(user_data)のデータを更新する * @param userData ユーザーデータテーブル(user_data)の更新データ */ void update(UserData userData); /** * ユーザーデータテーブル(user_data)の最大値IDを取得する * @return ユーザーデータテーブル(user_data)の最大値ID */ long findMaxId(); }
また、Mapperクラスに対応するXMLファイルは以下の通り。findBySearchFormメソッド内のifタグにより、条件分岐を利用した動的SQL文を生成している。
<?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.UserDataMapper"> <select id="findBySearchForm" parameterType="com.example.demo.SearchForm" resultType="com.example.demo.UserData"> SELECT u.id, u.name, u.birth_year as birthY, u.birth_month as birthM , u.birth_day as birthD, u.sex as sex, m.sex_value as sex_value FROM USER_DATA u, M_SEX m WHERE u.sex = m.sex_cd <if test="searchName != null and searchName != ''"> AND u.name like '%' || #{searchName} || '%' </if> <if test="fromBirthYear != null and fromBirthYear != ''"> AND #{fromBirthYear} || lpad(#{fromBirthMonth}, 2, '0') || lpad(#{fromBirthDay}, 2, '0') <= u.birth_year || lpad(u.birth_month, 2, '0') || lpad(u.birth_day, 2, '0') </if> <if test="toBirthYear != null and toBirthYear != ''"> AND u.birth_year || lpad(u.birth_month, 2, '0') || lpad(u.birth_day, 2, '0') <= #{toBirthYear} || lpad(#{toBirthMonth}, 2, '0') || lpad(#{toBirthDay}, 2, '0') </if> <if test="searchSex != null and searchSex != ''"> AND u.sex = #{searchSex} </if> ORDER BY u.id </select> <select id="findById" resultType="com.example.demo.UserData"> SELECT id, name, birth_year as birthY , birth_month as birthM , birth_day as birthD, sex FROM USER_DATA WHERE id = #{id} </select> <delete id="deleteById" parameterType="java.lang.Long"> DELETE FROM USER_DATA WHERE id = #{id} </delete> <insert id="create" parameterType="com.example.demo.UserData"> INSERT INTO USER_DATA ( id, name, birth_year, birth_month, birth_day, sex ) VALUES (#{id}, #{name}, #{birthY}, #{birthM}, #{birthD}, #{sex}) </insert> <update id="update" parameterType="com.example.demo.UserData"> UPDATE USER_DATA SET name = #{name}, birth_year = #{birthY} , birth_month = #{birthM}, birth_day = #{birthD}, sex = #{sex} WHERE id = #{id} </update> <select id="findMaxId" resultType="long"> SELECT NVL(max(id), 0) FROM USER_DATA </select> </mapper>
さらに、検索画面「search.html」の内容は以下の通り。
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <link th:href="@{/demo.css}" rel="stylesheet" type="text/css" /> <title>index page</title> </head> <body> <p>検索条件を指定し、「検索」ボタンを押下してください。</p><br/> <form method="post" th:action="@{/search}" th:object="${searchForm}"> <span th:if="*{#fields.hasErrors('fromBirthYear')}" th:errors="*{fromBirthYear}" class="errorMessage"></span> <span th:if="*{#fields.hasErrors('toBirthYear')}" th:errors="*{toBirthYear}" class="errorMessage"></span> <table border="1" cellpadding="5"> <tr> <th>名前</th> <td><input type="text" th:value="*{searchName}" th:field="*{searchName}" /></td> </tr> <tr> <th>生年月日</th> <td><input type="text" th:value="*{fromBirthYear}" size="4" maxlength="4" th:field="*{fromBirthYear}" th:errorclass="fieldError" />年 <select th:field="*{fromBirthMonth}" th:errorclass="fieldError"> <option value=""></option> <option th:each="item : *{getMonthItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>月 <select th:field="*{fromBirthDay}" th:errorclass="fieldError"> <option value=""></option> <option th:each="item : *{getDayItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>日~ <input type="text" th:value="*{toBirthYear}" size="4" maxlength="4" th:field="*{toBirthYear}" th:errorclass="fieldError" />年 <select th:field="*{toBirthMonth}" th:errorclass="fieldError"> <option value=""></option> <option th:each="item : *{getMonthItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>月 <select th:field="*{toBirthDay}" th:errorclass="fieldError"> <option value=""></option> <option th:each="item : *{getDayItems()}" th:value="${item.key}" th:text="${item.value}"/> </select>日 </td> </tr> <tr> <th>性別</th> <td> <select th:field="*{searchSex}"> <option value=""></option> <option th:each="item : *{getSexItems()}" th:value="${item.key}" th:text="${item.value}"/> </select> </td> </tr> </table> <br/><br/> <input type="submit" value="検索" /><br/><br/> <input type="button" value="閉じる" onclick="window.close();" /> </form> </body> </html>
その他、検索用Formのチェック処理を行うメソッド「checkSearchForm」をユーティリティクラスに追加している。
package com.example.demo; import java.time.LocalDate; import java.time.chrono.JapaneseChronology; import java.time.format.DateTimeFormatter; import java.time.format.ResolverStyle; import java.util.Locale; public class DateCheckUtil { /** 日付のフォーマット */ private final static String dateFormat = "uuuuMMdd"; /** * 日付チェック処理を行う * @param year 年 * @param month 月 * @param day 日 * @return 判定結果(1:年が空、2:月が空、3:日が空、4:年月日が不正、 * 5:年月日が未来日、0:正常) */ public static int checkDate(String year, String month, String day){ if(isEmpty(year)){ return 1; } if(isEmpty(month)){ return 2; } if(isEmpty(day)){ return 3; } String dateStr = year + addZero(month) + addZero(day); if(!isCorrectDate(dateStr, dateFormat)){ return 4; } if(isFutureDate(dateStr, dateFormat)){ return 5; } return 0; } /** * 検索用Formオブジェクトのチェック処理行う * @param searchForm 検索用Formオブジェクト * @return 判定結果(1:生年月日_fromが不正、2:生年月日_toが不正、 * 3:生年月日_from>生年月日_to、0:正常) */ public static int checkSearchForm(SearchForm searchForm){ //生年月日_fromが不正な場合 if(!checkSearchFormBirthday(searchForm.getFromBirthYear() , searchForm.getFromBirthMonth(), searchForm.getFromBirthDay())){ return 1; } //生年月日_toが不正な場合 if(!checkSearchFormBirthday(searchForm.getToBirthYear() , searchForm.getToBirthMonth(), searchForm.getToBirthDay())){ return 2; } //生年月日_from>生年月日_toの場合 if(!isEmpty(searchForm.getFromBirthYear()) && !isEmpty(searchForm.getToBirthYear())){ String fromBirthDay = searchForm.getFromBirthYear() + addZero(searchForm.getFromBirthMonth()) + addZero(searchForm.getFromBirthDay()); String toBirthDay = searchForm.getToBirthYear() + addZero(searchForm.getToBirthMonth()) + addZero(searchForm.getToBirthDay()); if(fromBirthDay.compareTo(toBirthDay) > 0){ return 3; } } //正常な場合 return 0; } /** * 検索用Form内の日付をチェックする * @param year 年 * @param month 月 * @param day 日 * @return 日付チェック結果 */ private static boolean checkSearchFormBirthday( String year, String month, String day){ //年・月・日が全て未指定の場合はチェックOKとする if(isEmpty(year) && isEmpty(month) && isEmpty(day)){ return true; } //年・月・日が全て指定されている場合は、日付が正しい場合にチェックOKとする if(!isEmpty(year) && !isEmpty(month) && !isEmpty(day)){ String dateStr = year + addZero(month) + addZero(day); if(isCorrectDate(dateStr, dateFormat)){ return true; } return false; } //年・月・日が指定あり/指定なしで混在している場合はチェックNGとする return false; } /** * DateTimeFormatterを利用して日付チェックを行う * @param dateStr チェック対象文字列 * @param dateFormat 日付フォーマット * @return 日付チェック結果 */ private static boolean isCorrectDate(String dateStr, String dateFormat){ if(isEmpty(dateStr) || isEmpty(dateFormat)){ return false; } //日付と時刻を厳密に解決するスタイルで、DateTimeFormatterオブジェクトを作成 DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat) .withResolverStyle(ResolverStyle.STRICT); try{ //チェック対象文字列をLocalDate型の日付に変換できれば、チェックOKとする LocalDate.parse(dateStr, df); return true; }catch(Exception e){ return false; } } /** * 日付の文字列が未来日かどうかを判定する * @param dateStr チェック対象文字列 * @param dateFormat 日付フォーマット * @return 判定結果 */ private static boolean isFutureDate(String dateStr, String dateFormat){ if(!isCorrectDate(dateStr, dateFormat)){ return false; } LocalDate dateStrDate = convertStrToLocalDate(dateStr, dateFormat); LocalDate now = LocalDate.now(); if(dateStrDate.isAfter(now)){ return true; } return false; } /** * 日付の文字列を日付型に変換した結果を返す * @param dateStr 日付の文字列 * @param dateFormat 日付のフォーマット * @return 変換後の文字列 */ private static LocalDate convertStrToLocalDate(String dateStr, String dateFormat){ if(isEmpty(dateStr) || isEmpty(dateFormat)){ return null; } //日付と時刻を厳密に解決するスタイルで、暦体系は和暦体系で、 //DateTimeFormatterオブジェクトを作成 DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat, Locale.JAPAN) .withChronology(JapaneseChronology.INSTANCE) .withResolverStyle(ResolverStyle.STRICT); //日付の文字列をLocalDate型に変換して返却 return LocalDate.parse(dateStr, df); } /** * 数値文字列が1桁の場合、頭に0を付けて返す * @param intNum 数値文字列 * @return 変換後数値文字列 */ private static String addZero(String intNum){ if(isEmpty(intNum)){ return intNum; } if(intNum.length() == 1){ return "0" + intNum; } return intNum; } /** * 引数の文字列がnull、空文字かどうかを判定する * @param str チェック対象文字列 * @return 文字列チェック結果 */ public static boolean isEmpty(String str){ if(str == null || "".equals(str)){ return true; } return false; } }
さらに、コントローラクラスの内容は以下の通り。searchメソッドに、検索用Formのチェック処理・検索処理を追加している。
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.validation.BindingResult; import org.springframework.web.bind.support.SessionStatus; import java.util.ArrayList; import java.util.List; @Controller @SessionAttributes(types = {DemoForm.class, SearchForm.class}) public class DemoController { /** * Demoサービスクラスへのアクセス */ @Autowired private DemoService demoService; /** * ユーザーデータテーブル(user_data)のデータを取得して返却する * @return ユーザーデータリスト */ @ModelAttribute("demoFormList") public List<DemoForm> userDataList(){ List<DemoForm> demoFormList = new ArrayList<>(); return demoFormList; } /** * 追加・更新用Formオブジェクトを初期化して返却する * @return 追加・更新用Formオブジェクト */ @ModelAttribute("demoForm") public DemoForm createDemoForm(){ DemoForm demoForm = new DemoForm(); return demoForm; } /** * 検索用Formオブジェクトを初期化して返却する * @return 検索用Formオブジェクト */ @ModelAttribute("searchForm") public SearchForm createSearchForm(){ SearchForm searchForm = new SearchForm(); return searchForm; } /** * 初期表示(検索)画面に遷移する * @return 検索画面へのパス */ @RequestMapping("/") public String index(){ return "search"; } /** * 検索処理を行い、一覧画面に遷移する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @param result バインド結果 * @return 一覧画面へのパス */ @PostMapping("/search") public String search(SearchForm searchForm, Model model, BindingResult result){ //検索用Formオブジェクトのチェック処理 String returnVal = demoService.checkSearchForm(searchForm, result); if(returnVal != null){ return returnVal; } //ユーザーデータリストを取得 List<DemoForm> demoFormList = demoService.demoFormList(searchForm); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } /** * 更新処理を行う画面に遷移する * @param id 更新対象のID * @param model Modelオブジェクト * @return 入力・更新画面へのパス */ @GetMapping("/update") public String update(@RequestParam("id") String id, Model model){ //更新対象のユーザーデータを取得 DemoForm demoForm = demoService.findById(id); //ユーザーデータを更新 model.addAttribute("demoForm", demoForm); return "input"; } /** * 削除確認画面に遷移する * @param id 更新対象のID * @param model Modelオブジェクト * @return 削除確認画面へのパス */ @GetMapping("/delete_confirm") public String delete_confirm(@RequestParam("id") String id, Model model){ //削除対象のユーザーデータを取得 DemoForm demoForm = demoService.findById(id); //ユーザーデータを更新 model.addAttribute("demoForm", demoForm); return "confirm_delete"; } /** * 削除処理を行う * @param demoForm 追加・更新用Formオブジェクト * @return 一覧画面の表示処理 */ @PostMapping(value = "/delete", params = "next") public String delete(DemoForm demoForm){ //指定したユーザーデータを削除 demoService.deleteById(demoForm.getId()); //一覧画面に遷移 return "redirect:/to_index"; } /** * 削除完了後に一覧画面に戻る * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面 */ @GetMapping("/to_index") public String toIndex(SearchForm searchForm, Model model){ //一覧画面に遷移 //ユーザーデータリストを取得 List<DemoForm> demoFormList = demoService.demoFormList(searchForm); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } /** * 削除確認画面から一覧画面に戻る * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面 */ @PostMapping(value = "/delete", params = "back") public String confirmDeleteBack(Model model, SearchForm searchForm){ //一覧画面に遷移 //ユーザーデータリストを取得 List<DemoForm> demoFormList = demoService.demoFormList(searchForm); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } /** * 追加処理を行う画面に遷移する * @param model Modelオブジェクト * @return 入力・更新画面へのパス */ @PostMapping(value = "/add", params = "next") public String add(Model model){ model.addAttribute("demoForm", new DemoForm()); return "input"; } /** * 追加処理を行う画面から検索画面に戻る * @return 検索画面へのパス */ @PostMapping(value = "/add", params = "back") public String addBack(){ return "search"; } /** * エラーチェックを行い、エラーが無ければ確認画面に遷移し、 * エラーがあれば入力画面のままとする * @param demoForm Formオブジェクト * @param result バインド結果 * @return 確認画面または入力画面へのパス */ @PostMapping(value = "/confirm", params = "next") public String confirm(@Validated DemoForm demoForm, BindingResult result){ //チェック処理を行い、画面遷移する return demoService.checkForm(demoForm, result, "confirm"); } /** * 一覧画面に戻る * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面の表示処理 */ @PostMapping(value = "/confirm", params = "back") public String confirmBack(Model model, SearchForm searchForm){ //ユーザーデータリストを取得 List<DemoForm> demoFormList = demoService.demoFormList(searchForm); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } /** * 完了画面に遷移する * @param demoForm 追加・更新用Formオブジェクト * @param result バインド結果 * @return 完了画面 */ @PostMapping(value = "/send", params = "next") public String send(@Validated DemoForm demoForm, BindingResult result){ //チェック処理を行い、エラーがなければ、更新・追加処理を行う String normalPath = "redirect:/complete"; String checkPath = demoService.checkForm(demoForm, result, "redirect:/complete"); if(normalPath.equals(checkPath)){ demoService.createOrUpdate(demoForm); } return checkPath; } /** * 完了画面に遷移する * @param sessionStatus セッションステータス * @return 完了画面 */ @GetMapping("/complete") public String complete(SessionStatus sessionStatus){ //セッションオブジェクトを破棄 sessionStatus.setComplete(); return "complete"; } /** * 入力画面に戻る * @return 入力画面 */ @PostMapping(value = "/send", params = "back") public String sendBack(){ return "input"; } }
https://www.purin-it.com/doctor-homenet
また、サービスクラスとその実装クラスは以下の通り。検索処理を呼び出すdemoFormListメソッドと、検索用Formのチェック処理を呼び出すcheckSearchFormメソッドを追加している。
package com.example.demo; import org.springframework.validation.BindingResult; import java.util.List; public interface DemoService { /** * ユーザーデータリストを取得 * @param searchForm 検索用Formオブジェクト * @return ユーザーデータリスト */ List<DemoForm> demoFormList(SearchForm searchForm); /** * 引数のIDに対応するユーザーデータを取得 * @param id ID * @return ユーザーデータ */ DemoForm findById(String id); /** * 引数のIDに対応するユーザーデータを削除 * @param id ID */ void deleteById(String id); /** * 引数のユーザーデータがあれば更新し、無ければ削除 * @param demoForm 追加・更新用Formオブジェクト */ void createOrUpdate(DemoForm demoForm); /** * 追加・更新用Formオブジェクトのチェック処理を行い、画面遷移先を返す * @param demoForm 追加・更新用Formオブジェクト * @param result バインド結果 * @param normalPath 正常時の画面遷移先 * @return 画面遷移先 */ String checkForm(DemoForm demoForm, BindingResult result, String normalPath); /** * 検索用Formオブジェクトのチェック処理を行い、画面遷移先を返す * @param searchForm 検索用Formオブジェクト * @param result バインド結果 * @return 画面遷移先 */ String checkSearchForm(SearchForm searchForm, BindingResult result); }
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BindingResult; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Service public class DemoServiceImpl implements DemoService{ /** * ユーザーデータテーブル(user_data)へアクセスするマッパー */ @Autowired private UserDataMapper mapper; /** * {@inheritDoc} */ @Override public List<DemoForm> demoFormList(SearchForm searchForm) { List<DemoForm> demoFormList = new ArrayList<>(); //ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する Collection<UserData> userDataList = mapper.findBySearchForm(searchForm); for (UserData userData : userDataList) { demoFormList.add(getDemoForm(userData)); } return demoFormList; } /** * {@inheritDoc} */ @Override public DemoForm findById(String id) { Long longId = stringToLong(id); UserData userData = mapper.findById(longId); return getDemoForm(userData); } /** * {@inheritDoc} */ @Override @Transactional(readOnly = false) public void deleteById(String id){ Long longId = stringToLong(id); mapper.deleteById(longId); } /** * {@inheritDoc} */ @Override @Transactional(readOnly = false) public void createOrUpdate(DemoForm demoForm){ //更新・追加処理を行うエンティティを生成 UserData userData = getUserData(demoForm); //追加・更新処理 if(demoForm.getId() == null){ userData.setId(mapper.findMaxId() + 1); mapper.create(userData); }else{ mapper.update(userData); } } /** * {@inheritDoc} */ @Override public String checkForm(DemoForm demoForm, BindingResult result, String normalPath){ //formオブジェクトのチェック処理を行う if(result.hasErrors()){ //エラーがある場合は、入力画面のままとする return "input"; } //生年月日の日付チェック処理を行う //エラーがある場合は、エラーメッセージ・エラーフィールドの設定を行い、 //入力画面のままとする int checkDate = DateCheckUtil.checkDate(demoForm.getBirthYear() , demoForm.getBirthMonth(), demoForm.getBirthDay()); switch(checkDate){ case 1: //生年月日_年が空文字の場合のエラー処理 result.rejectValue("birthYear", "validation.date-empty" , new String[]{"生年月日_年"}, ""); return "input"; case 2: //生年月日_月が空文字の場合のエラー処理 result.rejectValue("birthMonth", "validation.date-empty" , new String[]{"生年月日_月"}, ""); return "input"; case 3: //生年月日_日が空文字の場合のエラー処理 result.rejectValue("birthDay", "validation.date-empty" , new String[]{"生年月日_日"}, ""); return "input"; case 4: //生年月日の日付が不正な場合のエラー処理 result.rejectValue("birthYear", "validation.date-invalidate"); //生年月日_月・生年月日_日は、エラーフィールドの設定を行い、 //メッセージを空文字に設定している result.rejectValue("birthMonth", "validation.empty-msg"); result.rejectValue("birthDay", "validation.empty-msg"); return "input"; case 5: //生年月日の日付が未来日の場合のエラー処理 result.rejectValue("birthYear", "validation.date-future"); //生年月日_月・生年月日_日は、エラーフィールドの設定を行い、 //メッセージを空文字に設定している result.rejectValue("birthMonth", "validation.empty-msg"); result.rejectValue("birthDay", "validation.empty-msg"); return "input"; default: //性別が不正に書き換えられていないかチェックする if(!demoForm.getSexItems().keySet().contains(demoForm.getSex())){ result.rejectValue("sex", "validation.sex-invalidate"); return "input"; } //エラーチェックに問題が無いので、正常時の画面遷移先に遷移 return normalPath; } } /** * {@inheritDoc} */ @Override public String checkSearchForm(SearchForm searchForm, BindingResult result){ int checkDate =DateCheckUtil.checkSearchForm(searchForm); switch (checkDate){ case 1: //生年月日_fromが不正な場合のエラー処理 result.rejectValue("fromBirthYear", "validation.date-invalidate-from"); result.rejectValue("fromBirthMonth", "validation.empty-msg"); result.rejectValue("fromBirthDay", "validation.empty-msg"); return "search"; case 2: //生年月日_toが不正な場合のエラー処理 result.rejectValue("toBirthYear", "validation.date-invalidate-to"); result.rejectValue("toBirthMonth", "validation.empty-msg"); result.rejectValue("toBirthDay", "validation.empty-msg"); return "search"; case 3: //生年月日_from>生年月日_toの場合のエラー処理 result.rejectValue("fromBirthYear", "validation.date-invalidate-from-to"); result.rejectValue("fromBirthMonth", "validation.empty-msg"); result.rejectValue("fromBirthDay", "validation.empty-msg"); result.rejectValue("toBirthYear", "validation.empty-msg"); result.rejectValue("toBirthMonth", "validation.empty-msg"); result.rejectValue("toBirthDay", "validation.empty-msg"); return "search"; default: //正常な場合はnullを返却 return null; } } /** * DemoFormオブジェクトに引数のユーザーデータの各値を設定する * @param userData ユーザーデータ * @return DemoFormオブジェクト */ private DemoForm getDemoForm(UserData userData){ if(userData == null){ return null; } DemoForm demoForm = new DemoForm(); demoForm.setId(String.valueOf(userData.getId())); demoForm.setName(userData.getName()); demoForm.setBirthYear(String.valueOf(userData.getBirthY())); demoForm.setBirthMonth(String.valueOf(userData.getBirthM())); demoForm.setBirthDay(String.valueOf(userData.getBirthD())); demoForm.setSex(userData.getSex()); demoForm.setSex_value(userData.getSex_value()); return demoForm; } /** * UserDataオブジェクトに引数のフォームの各値を設定する * @param demoForm DemoFormオブジェクト * @return ユーザーデータ */ private UserData getUserData(DemoForm demoForm){ UserData userData = new UserData(); if(!DateCheckUtil.isEmpty(demoForm.getId())){ userData.setId(Long.valueOf(demoForm.getId())); } userData.setName(demoForm.getName()); userData.setBirthY(Integer.valueOf(demoForm.getBirthYear())); userData.setBirthM(Integer.valueOf(demoForm.getBirthMonth())); userData.setBirthD(Integer.valueOf(demoForm.getBirthDay())); userData.setSex(demoForm.getSex()); userData.setSex_value(demoForm.getSex_value()); return userData; } /** * 引数の文字列をLong型に変換する * @param id ID * @return Long型のID */ private Long stringToLong(String id){ try{ return Long.parseLong(id); }catch(NumberFormatException ex){ return null; } } }
さらに、検索用Formのエラーメッセージを下記プロパティファイルに追加している。
#メッセージ validation.date-empty={0}を入力してください。 validation.date-invalidate=生年月日が存在しない日付になっています。 validation.date-future=生年月日が未来の日付になっています。 validation.empty-msg= javax.validation.constraints.NotEmpty.message={0}を入力してください。 validation.sex-invalidate=性別に不正な値が入っています。 validation.date-invalidate-from=生年月日(From)の日付が不正です。 validation.date-invalidate-to=生年月日(To)の日付が不正です。 validation.date-invalidate-from-to=生年月日(From)が生年月日(To)より大きくなっています。 #フィールド名 name=名前 sex=性別 checked=確認チェック
また、一覧画面から検索画面に戻れるよう、下記HTMLファイルのsubmitタグの内容を変更している。
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>index page</title> </head> <body> ユーザーデータテーブル(user_data)の全データ<br/><br/> <table border="1" cellpadding="5"> <tr> <th>ID</th> <th>名前</th> <th>生年月日</th> <th>性別</th> <th></th> <th></th> </tr> <tr th:each="obj : ${demoFormList}"> <td th:text="${obj.id}"></td> <td th:text="${obj.name}"></td> <td th:text="|${obj.birthYear}年 ${obj.birthMonth}月 ${obj.birthDay}日|"></td> <td th:text="${obj.sex_value}"></td> <td><a href="/update" th:href="@{/update(id=${'__${obj.id}__'})}">更新</a></td> <td><a href="/delete_confirm" th:href="@{/delete_confirm(id=${'__${obj.id}__'})}">削除</a></td> </tr> </table> <br/><br/> <form method="post" th:action="@{/add}"> <input type="submit" name="next" value="データ追加" /><br/><br/> <input type="submit" name="back" value="戻る" /> </form> </body> </html>
また、完了画面に表示するボタン名を変更している。
<!DOCTYPE html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>完了画面</title> </head> <body> お申し込みが完了しました。<br/><br/> <form method="post" th:action="@{/}"> <input type="submit" value="一覧に戻る" /> </form> </body> </html>
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-search-2/demo
要点まとめ
- 検索機能を行うSQL文のような動的SQL文を生成するには、MyBatis提供のifタグによる条件分岐を利用すると便利である。