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


概要

  • SpringBoot+Thymeleafで一覧画面をページングする
  • 完成時の画面イメージ

検証環境

  • Spring Boot : 2.2.5
  • H2 Database

実装

Entity

Player.java
@Entity
@Getter
@Setter
public class Player {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private Integer number;
    private String position;
}

Repository

PlayerRepository.java
@Repository
public interface PlayerRepository extends JpaRepository<Player, Integer> {

    public Page<Player> findAll(Pageable pageable);
}

Service

PlayerService.java
@Service
public class PlayerService {

    @Autowired
    private PlayerRepository playerRepository;

    public Page<Player> getPlayers(Pageable pageable) {
        return playerRepository.findAll(pageable);
    }
}

Controller

PlayerController.java
@Controller
@RequestMapping("/pages/players")
public class PlayerController {

    @Autowired
    private PlayerService playerService;

    @GetMapping
    public String index(Pageable pageable, Model model) {

        Page<Player> playerPage = playerService.getPlayers(pageable);

        model.addAttribute("page", playerPage);
        model.addAttribute("players", playerPage.getContent());

        return "pages/index";
    }
}

Configuration

WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {

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

        PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
        // 1ページに表示する最大件数(10件)を設定する
        resolver.setMaxPageSize(10);
        argumentResolvers.add(resolver);
    }
}
  • 1ページの最大件数設定のところは以下と同様となる
    • resolver.setFallbackPageable(PageRequest.of(0, 10));
      ※PageRequest.of(ページ番号, ページサイズ)

View(Thymeleaf)

index.html
<!-- body部 -->
<h1>INTER PLAYER</h1>
<table border="1" bordercolor="navy">
  <thead>
    <tr>
      <th>NAME</th>
      <th>NUMBER</th>
      <th>POSITION</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="player : ${players}">
      <td th:text="${player.name}"></td>
      <td th:text="${player.number}"></td>
      <td th:text="${player.position}"></td>
   </tr>
  </tbody>
</table>
<!-- 以降がページネーション部分 -->
<div>
  <ul>
    <li style="display:inline;">
      <span th:if="${page.first}">&lt;&lt;</span>
      <a
        th:if="${!page.first}"
        th:href="@{/pages/players(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}"
        th:href="@{/pages/players(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}"
        th:href="@{/pages/players(page = (${page.number} + 1))}"
      >&gt;&gt;
      </a>
    </li>
  </ul>
</div>

Interface Page<T>

メソッド 詳細
getContent() ページコンテンツをListで返却する
getNumber() 何ページ目であるか返却する
getSize() ページサイズを返却する
getTotalPages() 総ページ数を返却する
isFirst() 最初のページであるか真偽値を返却する
isLast() 最後のページであるか真偽値を返却する

最後に

  • Pageableを渡してあげることで簡単に実現できた
  • 全量取得してきてクライアント側でごにょごにょするのかと思ってたけど、今回検証したのは都度DBアクセスする方式だった
  • ソート要件を追加すると変わってくるので、後に検証してみる

参照


2020/3/14 追記

PlayerController.java
@Controller
@RequestMapping("/pages/players")
public class PlayerController {

    @Autowired
    private PlayerService playerService;

    @GetMapping
    public String index(@PageableDefault(page = 0, size = 10) Pageable pageable, Model model) {

        Page<Player> playerPage = playerService.getPlayers(pageable);

        model.addAttribute("page", playerPage);
        model.addAttribute("players", playerPage.getContent());

        return "pages/index";
    }
}