今回も引き続き、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レコード目」という制限を加えると共に、現在ページ数をセッションにもたせた上で、ページングを行うリンクやボタン毎に、コントローラクラスのメソッドを作成すればよい。





