今回も引き続き、Spring Bootの一覧画面上でのページング処理の実装について述べる。ここでは、具体的なサンプルプログラムのソースコードを共有する。
前提条件
下記記事を参照のこと。
作成したサンプルプログラムの内容
作成したサンプルプログラムの構成は以下の通り。
なお、上図の赤枠は、前提条件に記載したソースコードと比較し、変更になったソースコードを示す。赤枠のソースコードについては今後記載する。
まず、SQL文を発行するMapperのXMLファイルと、Mapperクラスの内容は以下の通り。findBySearchFormメソッドの内容を変更している。
<?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"> <resultMap id="userDataResultMap" type="com.example.demo.UserData" > <id column="id" property="id" jdbcType="BIGINT" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="birthY" property="birthY" jdbcType="VARCHAR" /> <result column="birthM" property="birthM" jdbcType="VARCHAR" /> <result column="birthD" property="birthD" jdbcType="VARCHAR" /> <result column="sex" property="sex" jdbcType="VARCHAR" /> <result column="memo" property="memo" jdbcType="VARCHAR" /> <result column="sex_value" property="sex_value" jdbcType="VARCHAR" /> </resultMap> <select id="findBySearchForm" resultMap="userDataResultMap"> SELECT u.id, u.name, u.birth_year as birthY, u.birth_month as birthM , u.birth_day as birthD, u.sex, u.memo, u.sex_value FROM ( SELECT u1.id, u1.name, u1.birth_year, u1.birth_month, u1.birth_day , u1.sex, u1.memo, m.sex_value , ROW_NUMBER() OVER (ORDER BY u1.id) AS rn FROM USER_DATA u1, M_SEX m WHERE u1.sex = m.sex_cd <if test="searchForm.searchName != null and searchForm.searchName != ''"> AND u1.name like '%' || #{searchForm.searchName} || '%' </if> <if test="searchForm.fromBirthYear != null and searchForm.fromBirthYear != ''"> AND #{searchForm.fromBirthYear} || lpad(#{searchForm.fromBirthMonth}, 2, '0') || lpad(#{searchForm.fromBirthDay}, 2, '0') <= u1.birth_year || lpad(u1.birth_month, 2, '0') || lpad(u1.birth_day, 2, '0') </if> <if test="searchForm.toBirthYear != null and searchForm.toBirthYear != ''"> AND u1.birth_year || lpad(u1.birth_month, 2, '0') || lpad(u1.birth_day, 2, '0') <= #{searchForm.toBirthYear} || lpad(#{searchForm.toBirthMonth}, 2, '0') || lpad(#{searchForm.toBirthDay}, 2, '0') </if> <if test="searchForm.searchSex != null and searchForm.searchSex != ''"> AND u1.sex = #{searchForm.searchSex} </if> ORDER BY u1.id ) u <if test="pageable != null and pageable.pageSize > 0"> <where> u.rn between #{pageable.offset} and (#{pageable.offset} + #{pageable.pageSize} - 1) </where> </if> </select> <select id="findById" resultMap="userDataResultMap"> SELECT id, name, birth_year as birthY, birth_month as birthM , birth_day as birthD, sex, memo 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, memo ) VALUES (#{id}, #{name}, #{birthY}, #{birthM} , #{birthD}, #{sex}, #{memo,jdbcType=VARCHAR}) </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}, memo = #{memo,jdbcType=VARCHAR} WHERE id = #{id} </update> <select id="findMaxId" resultType="long"> SELECT NVL(max(id), 0) FROM USER_DATA </select> </mapper>
package com.example.demo; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.springframework.data.domain.Pageable; import java.util.Collection; @Mapper public interface UserDataMapper { /** * ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する * @param searchForm 検索用Formオブジェクト * @param pageable ページネーションオブジェクト * @return ユーザーデータテーブル(user_data)の検索条件に合うデータ */ Collection<UserData> findBySearchForm( @Param("searchForm") SearchForm searchForm , @Param("pageable") Pageable pageable); /** * 指定した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クラスで参照している「org.springframework.data.domain.Pageable」クラスは、Spring Dataに入っているため、build.gradleにそのインポート定義を追加している。
plugins { id 'org.springframework.boot' version '2.1.7.RELEASE' id 'java' } apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '1.8' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' compileOnly 'org.projectlombok:lombok:1.18.10' annotationProcessor 'org.projectlombok:lombok:1.18.10' compile files('lib/ojdbc6.jar') implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1' //ページングを利用できるようにするために追加 compile group: 'org.springframework.data', name: 'spring-data-commons-core', version: '1.1.0.RELEASE' }
さらに、サービスクラスとその実装の内容は、以下の通り。demoFormListメソッドを変更し、getPageable・getAllPageNumの各メソッドを追加している。
package com.example.demo; import org.springframework.validation.BindingResult; import org.springframework.data.domain.Pageable; import java.util.List; public interface DemoService { /** * ユーザーデータリストを取得 * @param searchForm 検索用Formオブジェクト * @param pageable ページネーションオブジェクト * @return ユーザーデータリスト */ List<DemoForm> demoFormList(SearchForm searchForm, Pageable pageable); /** * 引数の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); /** * ユーザー検索時に利用するページング用オブジェクトを生成する * @param pageNumber ページ番号 * @return ページング用オブジェクト */ Pageable getPageable(int pageNumber); /** * 一覧画面の全ページ数を取得する * @param searchForm 検索用Formオブジェクト * @return 全ページ数 */ int getAllPageNum(SearchForm searchForm); }
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BindingResult; import org.springframework.data.domain.Pageable; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Service public class DemoServiceImpl implements DemoService{ /** * ユーザーデータテーブル(user_data)へアクセスするマッパー */ @Autowired private UserDataMapper mapper; /** * 1ページに表示する行数(application.propertiesから取得) */ @Value("${demo.list.pageSize}") private String listPageSize; /** * {@inheritDoc} */ @Override public List<DemoForm> demoFormList(SearchForm searchForm, Pageable pageable) { List<DemoForm> demoFormList = new ArrayList<>(); //ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する Collection<UserData> userDataList = mapper.findBySearchForm(searchForm, pageable); 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; } } /** * {@inheritDoc} */ @Override public Pageable getPageable(int pageNumber){ Pageable pageable = new Pageable() { @Override public int getPageNumber() { //現在ページ数を返却 return pageNumber; } @Override public int getPageSize() { //1ページに表示する行数を返却 //listPageSizeは、本プログラムの先頭に定義している return Integer.parseInt(listPageSize); } @Override public int getOffset() { //表示開始位置を返却 //例えば、1ページに2行表示する場合の、2ページ目の表示開始位置は //(2-1)*2+1=3 で計算される return ((pageNumber - 1) * Integer.parseInt(listPageSize) + 1); } @Override public Sort getSort() { //ソートは使わないのでnullを返却 return null; } }; return pageable; } /** * {@inheritDoc} */ @Override public int getAllPageNum(SearchForm searchForm) { //1ページに表示する行数を取得 int listPageSizeNum = Integer.parseInt(listPageSize); if(listPageSizeNum == 0){ return 1; } //一覧画面に表示する全データを取得 //第二引数のpageableにnullを設定することで、一覧画面に表示する全データが取得できる Collection<UserData> userDataList = mapper.findBySearchForm(searchForm, null); //全ページ数を計算 //例えば、1ページに2行表示する場合で、全データ件数が5の場合、 //(5+2-1)/2=3 と計算される int allPageNum = (userDataList.size() + listPageSizeNum - 1) / listPageSizeNum; return allPageNum == 0 ? 1 : allPageNum; } /** * 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.setMemo(userData.getMemo()); 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.setMemo(demoForm.getMemo()); 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; } } }
また、application.propertiesに「demo.list.pageSize」を追加している。この値を、先ほどの「DemoServiceImpl.java」内で取得し利用している。
server.port = 8084 # DB接続情報 spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe spring.datasource.username=USER01 spring.datasource.password=USER01 spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver # SQLログ出力 logging.level.org.springframework=warn logging.level.com.example.demo.UserDataMapper=debug # 一覧画面で1ページに表示する行数 demo.list.pageSize=2
さらに、「SearchForm.java」に、「一覧画面の現在ページ数」を追加している。「SearchForm.java」はセッションスコープで保持される。
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; /** 一覧画面の現在ページ数 */ private int currentPageNum; /** 生年月日_月の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; } }
また、コントローラクラスの内容は以下の通り。一覧画面表示に関する「search」等のメソッドを変更するとともに、「先頭へ」「前へ」「次へ」「最後へ」リンク押下処理を追加している。
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 org.springframework.data.domain.Pageable; 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; } //現在ページ数を1ページ目に設定し、一覧画面に遷移する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 一覧画面で「先頭へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/firstPage") public String firstPage(SearchForm searchForm, Model model){ //現在ページ数を先頭ページに設定する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 一覧画面で「前へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/backPage") public String backPage(SearchForm searchForm, Model model){ //現在ページ数を前ページに設定する searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() - 1); return movePageInList(model, searchForm); } /** * 一覧画面で「次へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/nextPage") public String nextPage(SearchForm searchForm, Model model){ //現在ページ数を次ページに設定する searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() + 1); return movePageInList(model, searchForm); } /** * 一覧画面で「最後へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/lastPage") public String lastPage(SearchForm searchForm, Model model){ //現在ページ数を最終ページに設定する searchForm.setCurrentPageNum(demoService.getAllPageNum(searchForm)); return movePageInList(model, searchForm); } /** * 更新処理を行う画面に遷移する * @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){ //一覧画面に戻り、1ページ目のリストを表示する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 削除確認画面から一覧画面に戻る * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面 */ @PostMapping(value = "/delete", params = "back") public String confirmDeleteBack(Model model, SearchForm searchForm){ //一覧画面に戻る return movePageInList(model, searchForm); } /** * 追加処理を行う画面に遷移する * @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){ //一覧画面に戻る return movePageInList(model, searchForm); } /** * 完了画面に遷移する * @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"; } /** * 一覧画面に戻り、指定した現在ページのリストを表示する * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面の表示処理 */ private String movePageInList(Model model, SearchForm searchForm){ //現在ページ数, 総ページ数を設定 model.addAttribute("currentPageNum", searchForm.getCurrentPageNum()); model.addAttribute("allPageNum", demoService.getAllPageNum(searchForm)); //ページング用オブジェクトを生成し、現在ページのユーザーデータリストを取得 Pageable pageable = demoService.getPageable(searchForm.getCurrentPageNum()); List<DemoForm> demoFormList = demoService.demoFormList(searchForm, pageable); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } }
さらに、一覧画面の「list.html」の内容は以下の通り。先頭ページでは「先頭へ」「前へ」リンクを非表示に、最終ページでは「次へ」「最後へ」リンクを非表示に設定している。
<!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/> <table border="0"> <tr> <td width="70"> <span th:if="${currentPageNum} != 1"> <a href="/firstPage" th:href="@{/firstPage}">先頭へ</a> </span> </td> <td width="50"> <span th:if="${currentPageNum} != 1"> <a href="/backPage" th:href="@{/backPage}">前へ</a> </span> </td> <td width="50"> <span th:text="${currentPageNum}"></span> / <span th:text="${allPageNum}"></span> </td> <td width="50"> <span th:if="${currentPageNum} != ${allPageNum}"> <a href="/nextPage" th:href="@{/nextPage}">次へ</a> </span> </td> <td width="70"> <span th:if="${currentPageNum} != ${allPageNum}"> <a href="/lastPage" th:href="@{/lastPage}">最後へ</a> </span> </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>
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-paging-2/demo
要点まとめ
- ページング処理を実装するには、ページネーションオブジェクトであるPageableを利用して、一覧を表示するSQL文に「Xレコード目~Yレコード目」という制限を加えると共に、現在ページ数をセッションにもたせた上で、ページングを行うリンクやボタン毎に、コントローラクラスのメソッドを作成すればよい。