Open2

SpringBoot+Thymeleaf+Doma2でページングを実装する

ピン留めされたアイテム
mIO1000mIO1000

やりたいこと

一覧参照系の画面で、ページャーを実装したい。

ポイント

  • PageableオブジェクトをControllerのハンドラメソッドで受け取る。
  • Doma2のSelectOptionsを利用して、ページ単位の検索をする。

実装

※煩雑さを避けるために、package文、import文を削除している。

WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {
	
	@Value("${page-line-max}")
	private Integer pageLineMax;

	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {

		// PageableHandlerMethodArgumentResolverの実装クラスのうち、ページネーション関連のクラスを利用
		PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
		// 1ページに表示する最大件数を設定する
		resolver.setMaxPageSize(pageLineMax);
		// Controllerのハンドラメソッド引数に独自のオブジェクトをバインドできるようにする
		argumentResolvers.add(resolver);
	}

}

TableReferenceController.java
package com.sample.controller;
@Controller
@RequestMapping("/table_reference")
public class TableReferenceController {

	@Autowired
	TableReferenceService sv;

	// 省略

	/**
	 * 検索(ページングあり)
	 * @param form form フォームオブジェクト
	 * @param model Model
	 * @param pageable ページャブルオブジェクト
	 * @return ページ名
	 */
	@RequestMapping(path = "/search", method= {RequestMethod.GET, RequestMethod.POST})
	public String search(TableReferenceForm form, Model model, Pageable pageable) {

		// 検索
		Page<TableReferenceRecord> page = sv.search(form, pageable);
		// ページング情報をModelにセット
		model.addAttribute("page", page);

		return "table_reference";
	}

}
TableReferenceService.java
@Service
public class TableReferenceService {

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

	@Autowired
	public TableReferenceDao dao;

	/**
	 * 検索(ページングあり)
	 * @param form フォームオブジェクト
	 * @param pageable ページャブルオブジェクト
	 * @return ページオブジェクト
	 */
	public Page<TableReferenceRecord> search(TableReferenceForm form, Pageable pageable) {

		// 検索オプション設定
		SelectOptions options = createSearchOptions(pageable);
		List<Map<String, Object>> detail = dao.selectDetailPageable(options, form);
		
		List<TableReferenceRecord> list = new ArrayList<>();

		// 「No」を取得するためにオフセット+1を算出
		int cnt = pageable.getPageSize() * (pageable.getPageNumber()) + 1;

		for (Map<String, Object> map : detail) {

			TableReferenceRecord rec = new TableReferenceRecord();

			rec.setNo(Integer.toString(cnt++));
			rec.setEmployeeId(Util.convertToString(map.get("employee_id")));
			rec.setName(Util.convertToString(map.get("name")));
			rec.setGenderName(Util.convertToString(map.get("gender_name")));
			rec.setBirthday(Util.convertDateTimeString(map.get("birthday"), "yyyyMMdd", "yyyy/MM/dd"));
			rec.setEnteringDate(Util.convertDateTimeString(map.get("entering_date"), "yyyyMMdd", "yyyy/MM/dd"));
			rec.setRetirementDate(Util.convertDateTimeString(map.get("retirement_date"), "yyyyMMdd", "yyyy/MM/dd"));
			rec.setDepartmentId(Util.convertToString(map.get("department_id")));
			rec.setDepartmentName(Util.convertToString(map.get("department_name")));
			rec.setUpdateDate(Util.convertToString(map.get("update_date")));

			list.add(rec);
		}
		logger.debug("全体件数=" + options.getCount());

		// ページオブジェクトを生成
		Page<TableReferenceRecord> page = new PageImpl<>(list, pageable, options.getCount());
		form.setDetail(page.getContent());


		return page;
	}

	private SelectOptions createSearchOptions(Pageable pageable) {
		return SelectOptions.get()
				// オフセットの指定
				.offset(pageable.getPageSize() * (pageable.getPageNumber()))
				// 1ページあたりの行数
				.limit(pageable.getPageSize())
				// 全行の行数を取得する
				.count();
	}

}

TableReferenceDao.java
@ConfigAutowireable
@Dao
public interface TableReferenceDao {
	@Select
	public List<Map<String, Object>> selectDetailPageable(SelectOptions options, TableReferenceForm form);
}

table_reference.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>一覧参照</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<script th:src="@{/webjars/jquery/jquery.min.js}"></script>
<link rel="stylesheet" type="text/css" th:href="@{/css/table_reference.css}" />
</head>
<body>
  <div>
    <h2>社員マスタ検索</h2>
  </div>
  <form id="form" method="post" th:object="${tableReferenceForm}">
    <div id="condition-area">
      <label for="employeeId" th:text="#{table_reference.employeeId}">社員ID</label>
      <input type="text" th:field="*{employeeId}">
      <label for="name">社員名</label>
      <input type="text" th:field="*{name}">
      <label>性別</label> <label for="gender_m"></label>
      <input type="checkbox" th:field="*{gender_m}">
      <label for="gender_f"></label>
      <input type="checkbox" th:field="*{gender_f}">
      <label for="enteringYear">入社年</label>
      <input type="text" th:field="*{enteringYear}">
      <label for="retired">退職済</label>
      <input type="checkbox" th:field="*{retired}">
      <label for="departmentId">部署</label>
      <input type="text" th:field="*{departmentId}">
      <button id="searchB" type="submit" th:formaction="@{/table_reference/search}">検索</button>
    </div>

    <div id="table-area">
      <table>
        <thead>
          <tr>
            <th>No.</th>
            <th>社員ID</th>
            <th>社員名</th>
            <th>性別</th>
            <th>生年月日</th>
            <th>入社年月日</th>
            <th>退社年月日</th>
            <th>部署</th>
            <th>編集</th>
          </tr>
        </thead>
        <tbody>
          <tr th:each="dt : *{detail}">
            <td th:text="${dt.no}">No.</td>
            <td th:text="${dt.employeeId}">社員ID</td>
            <td th:text="${dt.name}">社員名</td>
            <td th:text="${dt.genderName}">性別</td>
            <td th:text="${dt.birthday}">生年月日</td>
            <td th:text="${dt.enteringDate}">入社年月日</td>
            <td th:text="${dt.retirementDate}">退社年月日</td>
            <td th:text="${dt.departmentName}">部署</td>
            <td>
              <button th:data-employeeId="${dt.employeeId}" class="btn btn-link">編集</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </form>
  <div th:if="${page != null}">
    <ul>
      <li style="display: inline;">
        <span th:if="${page.first}">&lt;&lt;</span>
        <a th:if="${!page.first}" href="javascript:document.form.submit; return false;" th:data-page="${page.number} - 1"> &lt;&lt;</a>
      </li>
      <li th:each="i : ${#numbers.sequence(0, page.totalPages - 1)}" style="display: inline; margin-left: 10px;">
        <span th:if="${i} == ${page.number}" th:text="${i + 1}">1</span>
        <a th:if="${i} != ${page.number}" href="javascript:document.form.submit; return false;" th:data-page="${i}">
          <span th:text="${i+1}">1</span>
        </a>
      </li>
      <li style="display: inline; margin-left: 10px;">
        <span th:if="${page.last}">&gt;&gt;</span>
        <a th:if="${!page.last}" href="javascript:document.form.submit; return false;" th:data-page="${page.number} + 1">&gt;&gt; </a>
      </li>
    </ul>
  </div>
  <script th:src="@{/js/table_reference.js}"></script>
</body>
</html>
table_reference.js
$(function() {

	// ページャ押下時
	$("a").on("click", function() {
		// 現在のページインデックスを取得
		let page = $(this).attr("data-page");
		let form = document.getElementById("form");
		form.action = "/webappSample/table_reference/search";
		form.method = "post";
		form.addEventListener("formdata",(e) => {
			let fd = e.formData;
			// リクエスト対象のページインデックスをリクエストパラメータに追加
			fd.set("page", page);
		});
		form.submit();
	});
})

実装コード全体

下記のGitHubにあげています。
https://github.com/mIO1000-g/webappSampleSpringBoot