[Spring Boot] ページング処理簡単レシピ

# 環境

言語 : java 1.8
フレームワーク : spring boot 2.2.4.RELEASE (spring-boot-starter)
テンプレートエンジン : thymeleaf 2.2.4.RELEASE (spring-boot-starter)
データベース : H2 1.4.200 (spring-boot-starter)
orm : data-jpa 2.2.4.RELEASE (spring-boot-starter)

# ソースコード

**Entity**

``` java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Board {

	@Id @GeneratedValue
	private String id;
	
	private String title;
	
	private String user; 
	
    // Getter・Setter省略
    
}
```

**Pagination**

``` java
public class Pagination {

	/** 1. ページことに表示する掲示物数 **/
	private int pageSize = 10;

	/** 2. ページングした、ブロック数 **/
	private int blockSize = 10;

	/** 3. 現在ページ **/
	private int page = 1;

	/** 4. 現在ブロック **/
	private int block = 1;

	/** 5. 総掲示物数 **/
	private int totalListCnt;

	/** 6. 総ページ数 **/
	private int totalPageCnt;

	/** 7. 総ブロック数 **/
	private int totalBlockCnt;

	/** 8. ブロックスタートページ **/
	private int startPage = 1;

	/** 9. ブロック最後ページ **/
	private int endPage = 1;

	/** 10. DB接近スタートインデックス **/
	private int startIndex = 0;

	/** 11. 以前ブロックの最後ページ **/
	private int prevBlock;

	/** 12. 次のブロックの最後ページ **/
	private int nextBlock;
    
    // Getter・Setter省略

	public Pagination(int totalListCnt, int page) {

		// 総掲示物数と現在ページはコントローラーからもらう。
		// 総掲示物数	- totalListCnt
		// 現在ページ	- page
		
		/** 3. 現在ページ **/
		setPage(page);
		
		/** 5. 総掲示物数 **/
		setTotalListCnt(totalListCnt);

		/** 6. 総ページ数 **/
		setTotalPageCnt((int) Math.ceil(totalListCnt * 1.0 / pageSize));

		/** 7. 総ブロック数 **/
		setTotalBlockCnt((int) Math.ceil(totalPageCnt * 1.0 / blockSize));
		
		/** 4. 現在ブロック **/
		setBlock((int) Math.ceil((page * 1.0)/blockSize)); 

		/** 8. ブロックスタートページ **/
		setStartPage((block - 1) * blockSize + 1);
		
		/** 9. ブロック最後ページ **/
		setEndPage(startPage + blockSize - 1);
        
		/* === ブラック最後ページについてバリエーション ===*/
		if(endPage > totalPageCnt){this.endPage = totalPageCnt;}
		
		/** 11. 以前ブロックの最後ページ **/
		setPrevBlock((block * blockSize) - blockSize);

		/* === 以前ブロックについてバリエーション === */
		if(prevBlock < 1) {this.prevBlock = 1;}

		/** 12. 次のブロックの最後ページ **/
		setNextBlock((block * blockSize) + 1);
		
		/* === 次のブロックについてバリエーション ===*/
		if(nextBlock > totalPageCnt) {nextBlock = totalPageCnt;}
        
		/** 10. DB接近スタートインデックス **/
		setStartIndex((page-1) * pageSize);
	
	}
}
```

**Controller**

``` java
@GetMapping("/")
public String home(Model model, @RequestParam(defaultValue = "1") int page) {

    // 総掲示物数 
    int totalListCnt = boardRepository.findAllCnt();

    // 総掲示物数と現在ページ
    Pagination pagination = new Pagination(totalListCnt, page);

    // DB接近スタートインデックス
    int startIndex = pagination.getStartIndex();
    
    // ページことに表示する掲示物最大数
    int pageSize = pagination.getPageSize();

	// 掲示物取得
    List<Board> boardList = boardRepository.findListPaging(startIndex, pageSize);

	// モデルオブジェクトにオブジェクト格納
    model.addAttribute("boardList", boardList);
    model.addAttribute("pagination", pagination);

    return "index";
}
```

**Repository**

```Java
@Repository
public class BoardRepository {

	@PersistenceContext
	private EntityManager em;

	public int findAllCnt() {
		return ((Number) em.createQuery("select count(*) from Board")
					.getSingleResult()).intValue();
	}

	public List<Board> findListPaging(int startIndex, int pageSize) {
		return em.createQuery("select b from Board b", Board.class)
					.setFirstResult(startIndex)
					.setMaxResults(pageSize)
					.getResultList();
	}
}
```

**html**

```html
<!DOCTYPE html>
<html 	lang="ja" 
        xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:th="http://www.thymeleaf.org">
			
<head>
	<meta charset="UTF-8">
	<title>paging</title>
		<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
	</head>
<body>
 
<table class="table table-striped">

  <thead class="thead-dark">
    <tr>
      <th scope="col" style="width: 10%">no</th>
      <th scope="col">title</th>
      <th scope="col" style="width: 15%">user</th>
    </tr>
  </thead>
  
  <tbody>
    <tr th:each="board : ${boardList}">
      <th scope="row" th:text="${boardStat.index + 1}">1</th>
      <td th:text="${board.title}"></td>
      <td th:text="${board.user}"></td>
    </tr>
  </tbody>
  
</table>

// ページング
<nav aria-label="Page navigation example ">
  <ul class="pagination">
  <li class="page-item">
      <a class="page-link" th:href="@{/?page=1}" aria-label="Previous">
        <span aria-hidden="true">＜＜</span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{/?page={page} (page = ${pagination.prevBlock})}" aria-label="Previous">
        <span aria-hidden="true">＜</span>
      </a>
    </li>
    <th:block  th:with="start = ${pagination.startPage}, end = ${pagination.endPage}">
	    <li class="page-item" 
	    		 th:with="start = ${pagination.startPage}, end = ${pagination.endPage}"
	    		th:each="pageButton : ${#numbers.sequence(start, end)}">
	    		<a class="page-link" th:href="@{/?page={page} (page = ${pageButton})}" th:text=${pageButton}></a>
	    </li>
    </th:block>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.nextBlock})}" aria-label="Next">
        <span aria-hidden="true">＞</span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.totalPageCnt})}" aria-label="Previous">
        <span aria-hidden="true">＞＞</span>
      </a>
    </li>
  </ul>
</nav>

</body>
</html>
```

# 備考

* Getter・Setterはlombokを使うと簡単に追加可能。（https://projectlombok.org/）