4.5. ページネーション¶
4.5.1. Overview¶
本章では、検索条件に一致するデータをページ分割して表示する方法(ページネーション)について説明する。
- サーバ側のメモリ枯渇の発生。単発のリクエストで問題が発生しなくても、同時に複数実行された場合に
java.lang.OutOfMemoryError
が発生する可能性がある。 - ネットワーク負荷の発生。不要なデータがネットワークに流れることで、ネットワーク全体にかかる負荷が高くなり、システム全体のレスポンスタイムに影響を与える可能性がある。
- 画面のレスポンス遅延の発生。大量のデータを扱う場合、サーバの処理、ネットワークのトラフィック処理、クライアントの描画処理の全てで時間がかかるため、画面のレスポンスが遅くなる可能性がある。
4.5.1.1. ページ分割時の一覧画面の表示について¶
項番 |
説明 |
---|---|
(1)
|
ページ検索処理で取得したデータを表示する。
|
(2)
|
ページを移動するためのリンクを表示する。
リンク押下時には、該当ページを表示するためのリクエストを送信する。
|
(3)
|
ページネーションに関連する情報(合計件数、合計ページ数、表示ページ数など)を表示する。
|
4.5.1.2. ページ検索について¶
4.5.1.2.1. Spring Data提供のページ検索機能について¶
Spring Dataより提供されているページ検索用の機能は、以下の通り。
項番 |
説明 |
---|---|
1 |
リクエストパラメータよりページ検索に必要な情報(検索対象のページ位置、取得件数、ソート条件)を抽出し、抽出した情報を
org.springframework.data.domain.Pageable のオブジェクトとしてControllerの引数に引き渡す。この機能は、
org.springframework.data.web.PageableHandlerMethodArgumentResolver クラスとして提供されており、spring-mvc.xml の<mvc:argument-resolvers> 要素に追加することで有効となる。リクエストパラメータについては、「Note欄」を参照されたい。
|
2 |
ページ情報(合計件数、該当ページのデータ、検索対象のページ位置、取得件数、ソート条件)を保持する。
この機能は、
org.springframework.data.domain.Page インタフェースとして提供されており、デフォルトの実装クラスとしてorg.springframework.data.domain.PageImpl が提供されている。
|
Note
ページ検索用のリクエストパラメータについて
Spring Dataより提供されているページ検索用のリクエストパラメータは以下の3つとなる。
項番
パラメータ名
説明
page
検索対象のページ位置を指定するためのリクエストパラメータ。値には、0以上の数値を指定する。デフォルトの設定では、ページ位置の値は”0
“ から開始する。そのため、1ページ目のデータを取得する場合は”0
“を、2ページ目のデータを取得する場合は”1
“を指定する必要がある。
size
取得する件数を指定するためのリクエストパラメータ。値には、1以上の数値を指定する。PageableHandlerMethodArgumentResolver
のmaxPageSize
に指定された値より大きい値が指定された場合は、maxPageSize
の値がsize
の値となる。
sort
ソート条件を指定するためのパラメータ(複数指定可能)。値には、{ソート項目名(,ソート順)}
の形式で指定する。ソート順には、ASC
又はDESC
のどちらかの値を指定し、省略した場合はASC
が適用される。項目名は”,
“区切りで複数指定することが可能である。例えば、クエリ文字列としてsort=lastModifiedDate,id,DESC&sort=subId
が指定された場合、ORDER BY lastModifiedDate DESC, id DESC, subId ASC
のようなOrder By句をQueryに追加することになる。
4.5.1.3. ページネーションの表示について¶
「ページ分割時の一覧画面の表示について」にて説明した画面の各要素について説明する。
4.5.1.3.1. 取得データの表示について¶
4.5.1.3.2. ページネーションリンクの表示について¶
<t:pagination>
のデフォルト設定で出力されるHTMLを例に、ページネーションリンクの実装例を説明する。4.5.1.3.2.1. ページネーションリンクの構成¶
ページネーションリンクは、以下の要素から構成される。
項番 |
説明 |
---|---|
(1)
|
最初のページに移動するためのリンク。
|
(2)
|
前のページに移動するためのリンク。
|
(3)
|
指定したページに移動するためのリンク。
|
(4)
|
次のページに移動するためのリンク。
|
(5)
|
最後のページに移動するためのリンク。
|
ページネーションリンクは、以下の状態をもつ。
項番 |
説明 |
---|---|
(6)
|
現在表示しているページで操作することができないリンクであることを示す状態。
具体的には、1ページ目を表示している時の「最初のページに移動するためのリンク」「前のページに移動するためのリンク」と、最終ページを表示している時の「次のページに移動するためのリンク」「最後のページに移動するためのリンク」がこの状態となる。
この状態を
disabled と定義する。 |
(7)
|
現在表示しているページであることを示す状態。
この状態を
active と定義する。 |
<t:pagination page="${page}" />
Todo
ThymeleafのDialectは次版以降に作成予定となる。
出力されるHTML
4.5.1.3.2.2. ページネーションリンクのHTML構造¶
ページネーションリンクのHTMLは、以下の構造となる。
TERASOLUNA共通ライブラリで提供しているJSPタグを使用する。
HTML
画面イメージ
項番
説明
デフォルト値
(1)ページネーションリンクの構成要素をまとめるための要素。共通ライブラリでは、この部分を「Outer Element」と呼び、複数の「Inner Element」を保持する。使用する要素は、JSPタグライブラリのパラメータによって変更することが出来る。<ul>
要素(2)「Outer Element」のスタイルクラスを指定するための属性。共通ライブラリでは、この部分を「Outer Element Class」と呼び、属性値はJSPタグライブラリのパラメータによって指定する。指定なし(3)ページネーションリンクを構成するための要素。共通ライブラリでは、この部分を「Inner Element」と呼び、ページ移動するためのリクエストを送信するための<a>
要素を保持する。使用する要素は、JSPタグライブラリのパラメータによって変更することが出来る。<li>
要素(4)「Inner Element」のスタイルクラスを指定するための属性。共通ライブラリでは、この部分を「Inner Element Class」と呼び、属性値は表示しているページ位置によってJSPタグライブラリ内の処理で切り替わる。「Note欄」を参照されたい。(5)ページ移動するためのリクエストを送信するための要素。共通ライブラリでは、この部分を「Page Link」と呼ぶ。<a>
要素固定(6)ページ移動するためのURLを指定するための属性。共通ライブラリでは、この部分を「Page Link URL」と呼ぶ。下記の「Note欄」を参照されたい。(7)ページ移動するためのリンクの表示テキストを指定する。共通ライブラリでは、この部分を「Page Link Text」と呼ぶ。下記の「Note欄」を参照されたい。Note
「Inner Element」の数について
デフォルトの設定では、「Inner Element」は最大で14個となる。内訳は以下の通り。
最初のページに移動するためのリンク : 1
前のページに移動するためのリンク : 1
指定したページに移動するためのリンク : 最大10
次のページに移動するためのリンク : 1
最後のページに移動するためのリンク : 1
「Inner Element」の数は、JSPタグライブラリのパラメータの指定によって変更することができる。
Note
「Inner Element Class」の設定値について
デフォルトの設定では、ページ位置によって、以下3つの値となる。
disabled
: 現在表示しているページでは操作することができないリンクであることを示すためのスタイルクラス。
active
: 現在表示しているページのリンクであることを示すためのスタイルクラス。指定なし : 上記以外のリンクであることを示す。
disabled
とactive
は、JSPタグライブラリのパラメータの指定によって別の値に変更することができる。
Note
「Page Link URL」のデフォルト値について
リンクの状態が
disabled
とactive
の場合はjavascript:void(0)
、それ以外の場合は?page={page}&size={size}
となる。「Page Link URL」は、JSPタグライブラリのパラメータの指定によって別の値に変更することができる。
Note
「Page Link Text」のデフォルト値について
項番
リンク名
デフォルト値
最初のページに移動するためのリンク
<<
前のページに移動するためのリンク
<
指定したページに移動するためのリンク
該当ページのページ番号(変更不可)
次のページに移動するためのリンク
>
最後のページに移動するためのリンク
>>
「指定したページに移動するためのリンク」以外は、JSPタグライブラリのパラメータの指定によって、別の値に変更することができる。
スタイルクラスの指定は省略している。スタイルクラスは、作成するアプリケーションの要件に応じて適宜指定すること。
HTML
画面イメージ
項番
説明
(1)ページネーションリンクの構成要素をまとめるための要素。(2)ページネーションリンクを構成するための要素。(3)ページ移動するためのリクエストを送信するための要素。(4)ページ移動するためのURLを指定するための属性。(5)ページ移動するためのリンクの表示テキストを指定する。
4.5.1.3.2.3. JSPタブライブラリのパラメータについて¶
JSPタグライブラリのパラメータに値を指定することで、デフォルト動作を変更することができる。
以下にパラメータの一覧を示す。
レイアウトを制御するためのパラメータ
項番 |
パラメータ名 |
説明 |
---|---|---|
outerElement |
「Outer Element」として使用するHTML要素名を指定する。
例) div
|
|
outerElementClass |
「Outer Element」のclass属性に設定するスタイルシートのクラス名を指定する。
例) pagination
|
|
innerElement |
「Inner Element」として使用するHTML要素名を指定する。
例) span
|
|
innerElementClass |
「Inner Element」のclass属性に設定するスタイルシートのクラス名を指定する。5.6.0から追加
例) enablePage
|
|
disabledClass |
disabled 状態と判断された「Inner Element」のclass属性に設定する値を指定する。例) hiddenPage
|
|
activeClass |
active 状態の「Inner Element」のclass属性に設定する値を指定する。例) currentPage
|
|
anchorClass |
「Page Link」のclass属性に設定するスタイルシートのクラス名を指定する。5.7.0から追加
例) pageLink
|
|
firstLinkText |
「最初のページに移動するためのリンク」の「Page Link Text」に設定する値を指定する。
"" を指定すると、「最初のページに移動するためのリンク」自体が出力されなくなる。例) First
|
|
previousLinkText |
「前のページに移動するためのリンク」の「Page Link Text」に設定する値を指定する。
"" を指定すると、「前のページに移動するためのリンク」自体が出力されなくなる。例) Prev
|
|
nextLinkText |
「次のページに移動するためのリンク」の「Page Link Text」に設定する値を指定する。
"" を指定すると、「次のページに移動するためのリンク」自体が出力されなくなる。例) Next
|
|
lastLinkText |
「最後のページに移動するためのリンク」の「Page Link Text」に設定する値を指定する。
"" を指定すると、「次のページに移動するためのリンク」自体が出力されなくなる。例) Last
|
|
maxDisplayCount |
「指定したページに移動するためのリンク」の最大表示数を指定する。
“
0 “ を指定すると、「指定したページに移動するためのリンク」自体が出力されなくなる。例) 5
|
JSP
<t:pagination page="${page}" outerElement="div" outerElementClass="pagination" innerElement="span" innerElementClass="enablePage" disabledClass="hiddenPage" activeClass="currentPage" anchorClass="pageLink" firstLinkText="First" previousLinkText="Prev" nextLinkText="Next" lastLinkText="Last" maxDisplayCount="5" />
出力されるHTML
動作を制御するためのパラメータ
項番 |
パラメータ名 |
説明 |
---|---|---|
disabledHref |
disabled 状態のリンクの「Page Link URL」に設定する値を指定する。 |
|
pathTmpl |
「Page Link URL」に設定するリクエストパスのテンプレートを指定する。
ページ表示時のリクエストパスとページ移動するためのリクエストパスが異なる場合は、このパラメータにページ移動用のリクエストパスを指定する必要がある。
指定するリクエストパスのテンプレートには、ページ位置(page)や取得件数(size)などをパス変数(プレースホルダ)として指定することができる。
指定した値はUTF-8でURLエンコーディングされる。
|
|
queryTmpl |
「Page Link URL」のクエリ文字列のテンプレートを指定する。
ページ移動する際に必要となるページネーション用のクエリ文字列(page,size,sortパラメータ)を生成するためのテンプレートを指定する。
ページ位置や取得件数のリクエストパラメータ名をデフォルト以外の値にする場合は、このパラメータにクエリ文字列を指定する必要がある。
指定するクエリ文字列のテンプレートには、ページ位置(page)や取得件数(size)などをパス変数(プレースホルダ)として指定することができる。
指定した値はUTF-8でURLエンコーディングされる。
この属性は、ページネーション用のクエリ文字列(page,size,sortパラメータ)を生成するための属性であるため、検索条件を引き継ぐためのクエリ文字列はcriteriaQuery属性に指定すること。
|
|
criteriaQuery |
「Page Link URL」に追加する検索条件用のクエリ文字列を指定する。
「Page Link URL」に検索条件を引き継ぐ場合は、このパラメータに検索条件用のクエリ文字列を指定すること。
指定した値はURLエンコーディングされないため、URLエンコーディング済みのクエリ文字列を指定する必要がある。
フォームオブジェクトに格納されている検索条件をURLエンコーディング済みのクエリ文字列に変換する場合は、共通ライブラリから提供しているELファンクション(
f:query(Object) )を使用すると、簡単に条件を引き継ぐことができる。 |
|
disableHtmlEscapeOfCriteriaQuery |
criteriaQuery パラメータに指定された値に対するHTMLエスケープ処理を無効化するためのフラグ。true を指定する事で、criteriaQuery パラメータに指定された値に対してHTMLエスケープ処理が行われなくなる。(デフォルト値はfalse )trueを指定する場合は、XSS対策が必要な文字がクエリ文字列内に含まれない事が保証されていること。
|
|
enableLinkOfCurrentPage |
active 状態のページリンクを押下した際に、該当ページを再表示するためのリクエストを送信するためのフラグ。true を指定する事で、「Page Link URL」に該当ページを再表示するためのURL(デフォルト値は?page={page}&size={size} )が設定される。(デフォルト値はfalse で、「Page Link URL」にはdisabledHref 属性の値が設定される) |
Note
disabledHrefの設定値について
デフォルトでは、disabledHref
属性にはjavascript:void(0)
が設定されている。ページリンク押下時の動作を無効化するだけであれば、デフォルトのままでよい。
ただし、デフォルトの状態でページリンクにフォーカスを移動又はマウスオーバーした場合、ブラウザのステータスバーにjavascript:void(0)
が表示されることがある。この挙動を変えたい場合は、JavaScriptを使用してページリンク押下時の動作を無効化する必要がある。実装例については、「JavaScriptを使用したページリンクの無効化」を参照されたい。
Note
パス変数(プレースホルダ)について
pathTmpl
及びqueryTmpl
に指定できるパス変数は、以下の通り。
項番
パス変数名
説明
page
ページ位置を埋め込むためのパス変数。
size
取得件数を埋め込むためのパス変数。
sortOrderProperty
ソート条件のソート項目を埋め込むためのパス変数。
sortOrderDirection
ソート条件のソート順を埋め込むためのパス変数。
パス変数は、{パス変数名}
の形式で指定する。
Warning
ソート条件の制約事項
ソート条件のパス変数に設定される値は、ひとつのソート条件のみとなっている。そのため、複数のソート条件を指定して検索した結果を、ページネーション表示する必要がある場合は、共通ライブラリから提供しているJSPタグライブラリを拡張する必要がある。
JSP
<t:pagination page="${page}" disabledHref="#" pathTmpl="${pageContext.request.contextPath}/article/list/{page}/{size}" queryTmpl="sort={sortOrderProperty},{sortOrderDirection}" criteriaQuery="${f:query(articleSearchCriteriaForm)}" enableLinkOfCurrentPage="true" />
出力されるHTML
4.5.1.3.3. ページネーション情報の表示について¶
合計件数
検索対象のページ位置
取得件数
ソート条件
4.5.1.4. ページネーション機能使用時の処理フロー¶
項番 |
説明 |
---|---|
(1)
|
検索条件と共に、リクエストパラメータとして検索対象のページ位置(page)と取得件数(size)を指定してリクエストを送信する。
|
(2)
|
PageableHandlerMethodArgumentResolver は、リクエストパラメータに指定されている検索対象のページ位置(page)と取得件数(size)を取得し、Pageable オブジェクトを生成する。生成された
Pageable オブジェクトは、Controllerのハンドラメソッドの引数に設定される。 |
(3)
|
Controllerは、引数で受け取った
Pageable オブジェクトを、Serviceのメソッドに引き渡す。 |
(4)
|
Serviceは、引数で受け取った
Pageable オブジェクトを、RepositoryのQueryメソッドに引き渡す。 |
(5)
|
Repositoryは、検索条件に一致するデータの合計件数(totalElements)と、引数で受け取った
Pageable オブジェクトに指定されているページ位置(page)と取得件数(size)の範囲に存在するデータを、データベースより取得する。 |
(6)
|
Repositoryは、取得した合計件数(totalElements)、取得データ(content)、引数で受け取った
Pageable オブジェクトよりPage オブジェクトを作成し、Service及びControllerへ返却する。 |
(7)
|
Controllerは、返却された
Page オブジェクトを、Model オブジェクトに格納後、JSPに遷移する。 |
(8)
|
JSPは、
Model オブジェクトに格納されているPage オブジェクトを取得し、共通ライブラリから提供されているページネーション用のJSPタグライブラリ(<t:pagination> )を呼び出す。ページネーション用のJSPタグライブラリは
Page オブジェクトを参照し、ページネーションリンクを生成する。 |
(9)
|
JSPで生成したHTMLを、クライアント(ブラウザ)に返却する。
|
(10)
|
ページネーションリンクを押下すると、該当ページを表示するためリクエストが送信される。
|
項番 |
説明 |
---|---|
(1)
|
検索条件と共に、リクエストパラメータとして検索対象のページ位置(page)と取得件数(size)を指定してリクエストを送信する。
|
(2)
|
PageableHandlerMethodArgumentResolver は、リクエストパラメータに指定されている検索対象のページ位置(page)と取得件数(size)を取得し、Pageable オブジェクトを生成する。生成された
Pageable オブジェクトは、Controllerのハンドラメソッドの引数に設定される。 |
(3)
|
Controllerは、引数で受け取った
Pageable オブジェクトを、Serviceのメソッドに引き渡す。 |
(4)
|
Serviceは、引数で受け取った
Pageable オブジェクトを、RepositoryのQueryメソッドに引き渡す。 |
(5)
|
Repositoryは、検索条件に一致するデータの合計件数(totalElements)と、引数で受け取った
Pageable オブジェクトに指定されているページ位置(page)と取得件数(size)の範囲に存在するデータを、データベースより取得する。 |
(6)
|
Repositoryは、取得した合計件数(totalElements)、取得データ(content)、引数で受け取った
Pageable オブジェクトよりPage オブジェクトを作成し、Service及びControllerへ返却する。 |
(7)
|
Controllerは、返却された
Page オブジェクトを、Model オブジェクトに格納後、ThymeleafのテンプレートHTMLに遷移する。テンプレートHTMLは、
Model オブジェクトに格納されているPage オブジェクトを取得し、ページネーションリンクを生成する。 |
(8)
|
生成したHTMLを、クライアント(ブラウザ)に返却する。
|
(9)
|
ページネーションリンクを押下すると、該当ページを表示するためリクエストが送信される。
|
4.5.2. How to use¶
ページネーション機能の具体的な使用方法を以下に示す。
4.5.2.1. アプリケーションの設定¶
4.5.2.1.1. Spring Dataのページネーション機能を有効化するための設定¶
Pageable
オブジェクトとしてControllerの引数に設定するための機能を有効化する。SpringMvcConfig.java
@EnableAspectJAutoProxy
@EnableWebMvc
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(pageableHandlerMethodArgumentResolver()); // (1)
}
@Bean
public PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver() {
return new PageableHandlerMethodArgumentResolver();
}
項番 |
説明 |
---|---|
(1)
|
addArgumentResolvers でargumentResolvers にorg.springframework.data.web.PageableHandlerMethodArgumentResolver を追加する。PageableHandlerMethodArgumentResolver で指定できるプロパティについては、「PageableHandlerMethodArgumentResolverのプロパティ値について」を参照されたい。 |
spring-mvc.xml
<mvc:annotation-driven>
<mvc:argument-resolvers>
<!-- (1) -->
<bean
class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
項番 |
説明 |
---|---|
(1)
|
<mvc:argument-resolvers> にorg.springframework.data.web.PageableHandlerMethodArgumentResolver を指定する。PageableHandlerMethodArgumentResolver で指定できるプロパティについては、「PageableHandlerMethodArgumentResolverのプロパティ値について」を参照されたい。 |
4.5.2.2. ページ検索の実装¶
ページ検索を実現するための実装方法を以下に示す。
4.5.2.2.1. アプリケーション層の実装¶
ページ検索に必要な情報(検索対象のページ位置、取得件数、ソート条件)を、Controllerの引数として受け取り、Serviceのメソッドに引き渡す。
Controller
@GetMapping("list") public String list(@Validated ArticleSearchCriteriaForm form, BindingResult result, Pageable pageable, // (1) Model model) { ArticleSearchCriteria criteria = beanMapper.map(form); // (2) Page<Article> page = articleService.searchArticle(criteria, pageable); // (3) model.addAttribute("page", page); // (4) return "article/list"; }
項番
説明
(1)ハンドラメソッドの引数としてPageable
を指定する。Pageable
オブジェクトには、ページ検索に必要な情報(検索対象のページ位置、取得件数、ソート条件)が格納されている。(2)BeanのマッピングにはMapStructを用いて作成したマッパーインタフェースを使用する。Mapperインタフェースの定義方法についてはBeanマッピング(MapStruct)を参照されたい。(3)Serviceのメソッドの引数にPageable
オブジェクトを指定して呼び出す。(4)Serviceから返却された検索結果(Page
オブジェクト )をModel
に追加する。Model
に追加することで、View(JSP/テンプレートHTML)から参照できるようになる。Note
リクエストパラメータにページ検索に必要な情報の指定がない場合の動作について
ページ検索に必要な情報(検索対象のページ位置、取得件数、ソート条件)がリクエストパラメータに指定されていない場合は、デフォルト値が適用される。 デフォルト値は、以下の通り。
検索対象のページ位置 :
0
(1ページ目)取得件数 :
20
ソート条件 :
null
(ソート条件なし)
デフォルト値は、以下の2つの方法で変更することができる。
ハンドラメソッドの
Pageable
の引数に、@org.springframework.data.web.PageableDefault
アノテーションを指定してデフォルト値を定義する。PageableHandlerMethodArgumentResolver
のfallbackPageable
プロパティにデフォルト値を定義したPageable
オブジェクトを指定する。
@PageableDefault
アノテーションを使用してデフォルト値を指定する方法について説明する。@PageableDefault
アノテーションを使ってデフォルト値を指定する。@GetMapping("list")
public String list(@Validated ArticleSearchCriteriaForm form,
BindingResult result,
@PageableDefault( // (1)
page = 0, // (2)
size = 50, // (3)
direction = Direction.DESC, // (4)
sort = { // (5)
"publishedDate",
"articleId"
}
) Pageable pageable,
Model model) {
// ...
return "article/list";
}
項番 |
説明 |
デフォルト値 |
---|---|---|
(1)
|
Pageable の引数に@PageableDefault アノテーションを指定する。 |
-
|
(2)
|
ページ位置のデフォルト値を変更する場合は、
@PageableDefault のpage属性に値を指定する。通常変更する必要はない。
|
“
0 “(1ページ目)
|
(3)
|
取得件数のデフォルト値を変更する場合は、
@PageableDefault のsize又はvalue属性に値を指定する。 |
10 |
(4)
|
ソート条件のデフォルト値を変更する場合は、
@PageableDefault のdirection属性に値を指定する。 |
Direction.ASC (昇順)
|
(5)
|
ソート条件のソート項目を指定する場合は、
@PageableDefault のsort属性にソート項目を指定する。複数の項目でソートする場合は、ソートするプロパティ名を配列で指定する。
上記例のように設定した場合、
ORDER BY publishedDate DESC, articleId DESC のようなソート条件がQueryに追加される。 |
空の配列
(ソート項目なし)
|
Tip
@PageableDefaultアノテーションで指定できるソート順について
@PageableDefault
アノテーションで指定できるソート順は昇順か降順のどちらか一つなので、項目ごとに異なるソート順を指定したい場合は@org.springframework.data.web.SortDefaults
アノテーションを使用する必要がある。
具体的には、ORDER BY publishedDate DESC, articleId ASC
のようなソート順にしたい場合である。
Tip
取得件数のデフォルト値のみ変更する場合の指定方法
取得件数のデフォルト値のみ変更する場合は、@PageableDefault(50)
と指定することもできる。これは@PageableDefault(size = 50)
と同じ動作となる。
@SortDefaults
アノテーションを使用してデフォルト値を指定する方法について説明する。@SortDefaults
アノテーションは、ソート項目が複数あり、項目ごとに異なるソート順を指定したい場合に使用する。@RequestMapping("list")
public String list(
@Validated ArticleSearchCriteriaForm form,
BindingResult result,
@PageableDefault(size = 50)
@SortDefaults( // (1)
{
@SortDefault( // (2)
sort = "publishedDate", // (3)
direction = Direction.DESC // (4)
),
@SortDefault(
sort = "articleId"
)
}) Pageable pageable,
Model model) {
// ...
return "article/list";
}
項番 |
説明 |
デフォルト値 |
---|---|---|
(1)
|
Pageable の引数に@SortDefaults アノテーションを指定する。@SortDefaults アノテーションには、複数の@org.springframework.data.web.SortDefault アノテーションを配列として指定することができる。 |
-
|
(2)
|
@SortDefaults アノテーションの value属性に、@SortDefault アノテーションを指定する。複数指定する場合は配列として指定する。
|
-
|
(3)
|
@PageableDefault のsort又はvalue属性にソート項目を指定する。複数の項目を指定する場合は配列として指定する。
|
空の配列
(ソート項目なし)
|
(4)
|
ソート条件のデフォルト値を変更する場合は、
@PageableDefault のdirection属性に値を指定する。 |
Direction.ASC (昇順)
|
Tip
ソート項目のデフォルト値のみ指定する場合の指定方法
取得項目のみ指定する場合は、@PageableDefault("articleId")
と指定することもできる。これは@PageableDefault(sort = "articleId")
や@PageableDefault(sort = "articleId", direction = Direction.ASC)
と同じ動作となる。
アプリケーション全体のデフォルト値を変更する必要がある場合は、spring-mvc.xml
に定義したPageableHandlerMethodArgumentResolver
のfallbackPageable
プロパティにデフォルト値を定義したPageable
オブジェクトを指定する。
fallbackPageable
の説明や具体的な設定例については、「PageableHandlerMethodArgumentResolverのプロパティ値について」を参照されたい。
4.5.2.2.2. ドメイン層の実装(MyBatis3編)¶
Pageable
オブジェクトより、必要な情報を抜き出してRepositoryに引き渡す。ドメイン層で実装するページ検索処理の詳細については、
を参照されたい。
4.5.2.3. ページネーションの実装¶
ページ検索処理で取得したPage
オブジェクトを一覧画面に表示し、ページネーションリンク及びページネーション情報(合計件数、合計ページ数、表示ページ数など)を表示する方法を以下に示す。
4.5.2.3.1. 基本実装(JSP)¶
4.5.2.3.1.1. 取得データの表示¶
ページ検索処理で取得したデータを表示するための実装例を以下に示す。
Controller
@GetMapping("list") public String list(@Validated ArticleSearchCriteriaForm form, BindingResult result, Pageable pageable, Model model) { if (!StringUtils.hasLength(form.getWord())) { return "article/list"; } ArticleSearchCriteria criteria = beanMapper.map(form); Page<Article> page = articleService.searchArticle(criteria, pageable); model.addAttribute("page", page); // (1) return "article/list"; }
項番
説明
(1)page
という属性名でPage
オブジェクトをModel
に格納する。JSPではpage
という属性名を指定してPage
オブジェクトにアクセスすることになる。JSP
<%-- ... --%> <%-- (1) --%> <c:when test="${page != null && page.totalPages != 0}"> <table class="maintable"> <thead> <tr> <th class="no">No</th> <th class="articleClass">Class</th> <th class="title">Title</th> <th class="overview">Overview</th> <th class="date">Published Date</th> </tr> </thead> <%-- (2) --%> <c:forEach var="article" items="${page.content}" varStatus="rowStatus"> <tr> <td class="no"> ${(page.number * page.size) + rowStatus.count} </td> <td class="articleClass"> ${f:h(article.articleClass.name)} </td> <td class="title"> ${f:h(article.title)} </td> <td class="overview"> ${f:h(article.overview)} </td> <td class="date"> ${f:h(article.publishedDate)} </td> </tr> </c:forEach> </table> <div class="paginationPart"> <%-- ... --%> </div> </c:when> <%-- ... --%>
項番
説明
(1)上記例では、条件に一致するデータが存在するかチェックを行い、一致するデータがない場合はヘッダ行も含めて表示していない。一致するデータがない場合でもヘッダ行は表示させる必要がある場合は、この分岐は不要となる。(2)JSTLの<c:forEach>
タグを使用して、取得したデータの一覧を表示する。取得したデータは、Page
オブジェクトのcontent
プロパティにリスト形式で格納されている。上記JSPで出力される画面例
4.5.2.3.1.2. ページネーションリンクの表示¶
ページ移動するためのリンク(ページネーションリンク)を表示するための実装例を以下に示す。
共通ライブラリより提供しているJSPタグライブラリを使用して、ページネーションリンクを出力する。
include.jsp
共通ライブラリより提供しているJSPタグライブラリの宣言を行う。ブランクプロジェクトでは設定済みの状態となっている。
<%@ taglib uri="http://terasoluna.org/tags" prefix="t"%> <%-- (1) --%> <%@ taglib uri="http://terasoluna.org/functions" prefix="f"%> <%-- (2) --%>
項番
説明
(1)ページネーションリンクを表示するためのJSPタグが格納されている。(2)ページネーションリンクを使う際に利用するJSPのELファンクションが格納されている。JSP
<t:pagination page="${page}" /> <%-- (3) --%>
項番
説明
(3)<t:pagination>
タグを使用する。 page属性には、ControllerでModel
に格納したPage
オブジェクトを指定する。
出力されるHTML
下記の出力例は、
?page=0&size=6
を指定して検索した際の結果である。<ul> <li class="disabled"><a href="javascript:void(0)"><<</a></li> <li class="disabled"><a href="javascript:void(0)"><</a></li> <li class="active"><a href="javascript:void(0)">1</a></li> <li><a href="?page=1&size=6">2</a></li> <li><a href="?page=2&size=6">3</a></li> <li><a href="?page=3&size=6">4</a></li> <li><a href="?page=4&size=6">5</a></li> <li><a href="?page=5&size=6">6</a></li> <li><a href="?page=6&size=6">7</a></li> <li><a href="?page=7&size=6">8</a></li> <li><a href="?page=8&size=6">9</a></li> <li><a href="?page=9&size=6">10</a></li> <li><a href="?page=1&size=6">></a></li> <li><a href="?page=9&size=6">>></a></li> </ul>
画面イメージ
JSP
<%-- ... --%> <t:pagination page="${page}" outerElementClass="pagination" /> <%-- (4) --%> <%-- ... --%>
項番
説明
(4)ページネーションリンクであることを示すクラス名を指定する。クラス名を指定することによってスタイルシートで指定するスタイルの適用範囲をページネーションリンクに限定することができる。スタイルシート
.pagination li { display: inline; } .pagination li>a { margin-left: 10px; }
ページネーションリンクとして成立したが、以下2つの問題が残る。
押下できるリンクと押下できないリンクの区別ができない。
現在表示しているページ位置がわからない。
上記の問題を解決する手段として、Bootstrap v3.0.0のスタイルシートと適用すると、以下のような表示となる。
画面イメージ
スタイルシート
bootstrap v3.0.0 の cssファイルを$WEB_APP_ROOT/resources/vendor/bootstrap-3.0.0/css/bootstrap.css
に配置する。以下、ページネーション関連のスタイル定義の抜粋。.pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.428571429; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 4px; border-top-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { background-color: #eeeeee; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #ffffff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #999999; cursor: not-allowed; background-color: #ffffff; border-color: #dddddd; }
JSP
JSPでは配置したcssファイルを読み込む定義を追加する。
<link rel="stylesheet" href="${pageContext.request.contextPath}/resources/vendor/bootstrap-3.0.0/css/bootstrap.css" type="text/css" media="screen, projection">
4.5.2.3.1.3. ページネーション情報の表示¶
ページネーションに関連する情報(合計件数、合計ページ数、表示ページ数など)を表示するための実装例を以下に示す。
画面例
JSP
<div> <fmt:formatNumber value="${page.totalElements}" /> results <%-- (1) --%> </div> <div> ${f:h(page.number + 1) } / <%-- (2) --%> ${f:h(page.totalPages)} Pages <%-- (3) --%> </div>
項番
説明
(1)検索条件に一致するデータの合計件数を表示する場合は、Page
オブジェクトのtotalElements
プロパティから値を取得する。(2)表示しているページのページ数を表示する場合は、Page
オブジェクトのnumber
プロパティから値を取得し、+1
する。Page
オブジェクトのnumber
プロパティは”0
“ 開始のため、ページ番号を表示する際は+1
が必要となる。(3)検索条件に一致するデータの合計ページ数を表示する場合は、Page
オブジェクトのtotalPages
プロパティから値を取得する。
該当ページの表示データ範囲を表示するための実装例を以下に示す。
画面例
JSP
<div> <%-- (4) --%> <fmt:formatNumber value="${(page.number * page.size) + 1}" /> - <%-- (5) --%> <fmt:formatNumber value="${(page.number * page.size) + page.numberOfElements}" /> </div>
項番
説明
(4)開始位置を表示する場合は、Page
オブジェクトのnumber
プロパティとsize
プロパティを使って計算する。Page
オブジェクトのnumber
プロパティは”0
“開始のため、データ開始位置を表示する際は+1
が必要となる。(5)終了位置を表示する場合は、Page
オブジェクトのnumber
プロパティ、size
プロパティ、numberOfElements
プロパティ を使って計算する。最終ページは端数となる可能性があるので、numberOfElements
を加算する必要がある。Tip
数値のフォーマットについて
表示する数値をフォーマットする必要がある場合は、JSTLから提供されているタグライブラリ(
<fmt:formatNumber>
)を使用する。
4.5.2.3.1.4. ページリンクで検索条件を引き継ぐ¶
検索条件をページ移動時のリクエストに引き継ぐ方法を、以下に示す。
JSP
<%-- (1) --%> <div id="criteriaPart"> <form:form action="${pageContext.request.contextPath}/article/list" method="get" modelAttribute="articleSearchCriteriaForm"> <form:input path="word" /> <form:button>Search</form:button> <br> </form:form> </div> <%-- ... --%> <t:pagination page="${page}" outerElementClass="pagination" criteriaQuery="${f:query(articleSearchCriteriaForm)}" /> <%-- (2) --%>
項番
説明
(1)検索条件を指定するフォーム。検索条件としてword
が存在する。(2)ページ移動時のリクエストに検索条件を引き継ぐ場合は、criteriaQuery
属性にURLエンコーディング済みのクエリ文字列を指定する。検索条件をフォームオブジェクトに格納する場合は、共通ライブラリから提供しているELファンクション(f:query(Object)
) を使用すると、簡単に条件を引き継ぐことができる。上記例の場合、?page=ページ位置&size=取得件数&word=入力値
という形式のクエリ文字列が生成される。Note
f:query(Object) の仕様について
f:query
の引数には、フォームオブジェクトなどのJavaBeanとMap
オブジェクトを指定することができる。JavaBeanの場合はプロパティ名がリクエストパラメータ名となり、Map
オブジェクトの場合はマップのキー名がリクエストパラメータとなる。生成されるクエリ文字列は、UTF-8のURLエンコーディングが行われる。terasoluna-gfw-web 5.0.1.RELEASEより、ネスト構造をもつJavaBeanと
Map
オブジェクトを指定できるように改善されている。f:query
の詳細な仕様(URLエンコーディングの仕様など)については、「f:query()」を参照されたい。Warning
f:queryを使用して生成したクエリ文字列をqueryTmpl属性に指定した際の動作について
f:query
を使用して生成したクエリ文字列をqueryTmpl属性に指定すると、URLエンコーディングが重複してしまい、特殊文字の引き継ぎが正しく行われないことが判明している。URLエンコーディングが重複してしまう事象については、terasoluna-gfw-web 1.0.1.RELEASE以上で利用可能な
criteriaQuery
属性を使用することで回避する事が出来る。
4.5.2.3.1.5. ページリンクでソート条件を引き継ぐ¶
ソート条件をページ移動時のリクエストに引き継ぐ方法を、以下に示す。
JSP
<t:pagination page="${page}" outerElementClass="pagination" queryTmpl="page={page}&size={size}&sort={sortOrderProperty},{sortOrderDirection}" /> <%-- (1) --%>
項番
説明
(1)ページ移動時のリクエストにソート条件を引き継ぐ場合は、queryTmpl
を指定し、クエリ文字列にソート条件を追加する。ソート条件を指定するためのパラメータの仕様については、「ページ検索用のリクエストパラメータについて」を参照されたい。上記例の場合、?page=0&size=20&sort=ソート項目,ソート順(ASC or DESC)
がクエリ文字列となる。
4.5.2.3.2. レイアウト変更(JSP)¶
4.5.2.3.2.1. 先頭ページと最終ページに移動するリンクの削除¶
「最初のページに移動するためのリンク」と「最後のページに移動するためのリンク」を削除するための実装例を、以下に示す。
画面例
JSP
<t:pagination page="${page}" outerElementClass="pagination" firstLinkText="" lastLinkText="" /> <%-- (1) (2) --%>
項番
説明
(1)「最初のページに移動するためのリンク」を非表示にする場合は、<t:pagination>
タグのfirstLinkText
属性に""
を指定する。(2)「最後のページに移動するためのリンク」を非表示にする場合は、<t:pagination>
タグのlastLinkText
属性に""
を指定する。
4.5.2.3.2.2. 前ページと次ページに移動するリンクの削除¶
「前のページに移動するためのリンク」と「次のページに移動するためのリンク」を削除するための実装例を、以下に示す。
画面例
JSP
<t:pagination page="${page}" outerElementClass="pagination" previousLinkText="" nextLinkText="" /> <%-- (1) (2) --%>
項番
説明
(1)「前のページに移動するためのリンク」を非表示にする場合は、<t:pagination>
タグの previousLinkText属性に""
を指定する。(2)「次のページに移動するためのリンク」を非表示にする場合は、<t:pagination>
タグの nextLinkText属性に""
を指定する。
4.5.2.3.2.3. disabled状態のリンクの削除¶
disabled
状態のリンクを削除するための実装例を、以下に示す。disabled
時のスタイルシートに、以下の定義を追加する。画面例
スタイルシート
.pagination .disabled { display: none; /* (1) */ }
項番
説明
(1)disabled
クラスの属性値として、display: none;
を指定する。
4.5.2.3.2.4. 指定ページへ移動するリンクの最大表示数の変更¶
指定したページに移動するためのリンクの最大表示数を変更するための実装例を、以下に示す。
画面例
JSP
<t:pagination page="${page}" outerElementClass="pagination" maxDisplayCount="5" /> <%-- (1) --%>
項番
説明
(1)指定したページに移動するためのリンクの最大表示数を変更する場合は、<t:pagination>
タグのmaxDisplayCount
属性に値を指定する。
4.5.2.3.2.5. 指定ページへ移動するリンクの削除¶
画面例
JSP
<t:pagination page="${page}" outerElementClass="pagination" maxDisplayCount="0" /> <%-- (1) --%>
項番
説明
(1)指定したページに移動するためのリンクを非表示にする場合は、<t:pagination>
タグのmaxDisplayCount
属性に”0
“を指定する。
4.5.2.3.3. 動作変更(JSP)¶
4.5.2.3.3.1. ソート条件の指定¶
クライアントからソート条件を指定するための実装例を、以下に示す。
画面例
JSP
<div id="criteriaPart"> <form:form action="${pageContext.request.contextPath}/article/search" method="get" modelAttribute="articleSearchCriteriaForm"> <form:input path="word" /> <%-- (1) --%> <form:select path="sort"> <form:option value="publishedDate,DESC">Newest</form:option> <form:option value="publishedDate,ASC">Oldest</form:option> </form:select> <form:button>Search</form:button> <br> </form:form> </div>
項番
説明
(1)クライアントからソート条件を指定する場合は、ソート条件を指定するためのパラメータを追加する。ソート条件を指定するためのパラメータの仕様については、「ページ検索用のリクエストパラメータについて」を参照されたい。上記例では、publishedDateの昇順と降順をプルダウンで選択できるようにしている。
4.5.2.3.3.2. JavaScriptを使用したページリンクの無効化¶
デフォルトでは、disabled
状態とactive
状態のページリンク押下時の動作を無効化するために、<t:pagination>
タグのdisabledHref
属性にjavascript:void(0)
を設定している。
この状態でページリンクにフォーカスを移動又はマウスオーバーすると、ブラウザのステータスバーにjavascript:void(0)
が表示されることがある。
この挙動を変えたい場合は、JavaScriptを使用してページリンク押下時の動作を無効化する必要がある。
以下に実装例を示す。
JSP
<%-- (1) --%>
<script type="text/javascript"
src="${pageContext.request.contextPath}/resources/vendor/js/jquery.js"></script>
<%-- (2) --%>
<script type="text/javascript">
$(function(){
$(document).on("click", ".disabled a, .active a", function(){
return false;
});
});
</script>
<%-- ... --%>
<%-- (3) --%>
<t:pagination page="${page}" disabledHref="#" />
項番 |
説明 |
---|---|
(1)
|
jQueryのjsファイルを読み込む。 上記例では、JavaScriptを使用してページリンク押下時の動作を無効化するためにjQueryのAPIを利用する。 |
(2)
|
jQueryのAPIを使用して、 ただし、 |
(3)
|
|
4.5.2.4. Thymeleafの実装¶
4.5.2.4.1. テンプレートHTMLの実装¶
ページ検索処理で取得したPage
オブジェクトからデータを取得し、ページネーションを行う画面に取得したデータ、ページネーションリンク、ページネーションに関する情報(合計件数、合計ページ数、表示ページ数など)を表示する方法について説明する。
そのため、ここでは以下の構成でテンプレートHTMLの実装を行う例を示す。
成果物の構成要素 |
説明 |
---|---|
テンプレートHTML(ページネーションを行う画面)
|
取得したデータの一覧の表示を実装する
|
テンプレートHTML(フラグメント)
|
すべてのテンプレートHTML(ページネーションを行う画面)で同様の、ページネーションリンクやページネーション情報の表示の実装を共通化する
|
式オブジェクト
|
ページネーションリンクの出力範囲や該当ページの表示データ範囲の計算ロジックを実装する
|
page
という名前を指定してPage
オブジェクトにアクセスすることができる。4.5.2.4.1.1. 取得データの表示¶
ページ検索処理で取得したデータを表示するための実装例を以下に示す。 データの表示内容は画面ごとに異なるため、共通化せずテンプレートHTML(ページネーションを行う画面)に実装すると良い。
テンプレートHTML(ページネーションを行う画面)
<!--/* ... */--> <!--/* (1) */--> <div th:if="${page} != null" th:remove="tag"> <table class="maintable"> <thead> <tr> <th>No</th> <th>Class</th> <th>Title</th> <th>Overview</th> <th>Published Date</th> </tr> </thead> <!--/* (2) */--> <tbody> <tr th:each="article, status : ${page.content}" th:object="${article}"> <td class="no" th:text="*{articleId}"></td> <td class="articleClass" th:text="*{articleClass.name}"></td> <td class="title" th:text="*{title}"></td> <td class="overview" th:text="*{overview}"></td> <td class="date" th:text="*{#dates.format(publishDate, 'yyyy/MM/dd HH:mm:ss')}"></td> </tr> </tbody> </table> </div> <!--/* ... */-->
項番
説明
(1)上記例では、条件に一致するデータが存在するかチェックを行い、一致するデータがない場合はヘッダ行も含めて表示していない。一致するデータがない場合でもヘッダ行は表示させる必要がある場合は、この分岐は不要となる(2)th:each
属性を使用して、取得したデータの一覧を表示する。取得したデータは、Page
オブジェクトのcontent
プロパティにリスト形式で格納されている。上記テンプレートHTMLで出力される画面例
4.5.2.4.1.2. ページネーションリンクの表示¶
最初のページに移動するためのリンク
前のページに移動するためのリンク
指定したページに移動するためのリンク
次のページに移動するためのリンク
最後のページに移動するためのリンク
Note
指定したページに移動するためのリンクの出力範囲の計算の実装方法について
この章で説明している式オブジェクトを使用した実装方法はあくまでも一例であり、実装方法を規定するものではない。他に以下のような実装方法があるため、プロジェクトの開発方針に則り実装していただきたい。
Page
オブジェクトを拡張し、表示するリンクの範囲(開始位置、終了位置)を計算、保持する。ControllerのHelperクラスでリンクの範囲(開始位置、終了位置)を計算する。
4.5.2.4.1.2.1. リンクの出力範囲を計算する式オブジェクトを実装する¶
テンプレート記述例
#pageInfo.sequence( Pageオブジェクト, リンクの最大表示数)
メソッドを追加する。これをth:each
属性の引数として利用することで、指定したページに移動するためのリンクを繰り返し出力する。disabled
及びactive
)は行っていない。<li th:each="i : ${#pageInfo.sequence(page, 5)}">
<a th:href="@{/sample(page=${i-1},size=${page.size})}" th:text="${i}"></a>
</li>
独自属性の処理結果
ページサイズが10、ページリンクの出力範囲が1から5までの場合、以下のように出力される。
<li><a href="/sample?page=0&size=10">1</a></li>
<li><a href="/sample?page=1&size=10">2</a></li>
<li><a href="/sample?page=2&size=10">3</a></li>
<li><a href="/sample?page=3&size=10">4</a></li>
<li><a href="/sample?page=4&size=10">5</a></li>
実装例
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.domain.Page;
import org.thymeleaf.util.NumberUtils;
public class PageInfo {
// (1)
public Integer[] sequence(Page<T> page, int pageLinkMaxDispNum) {
// (2)
int begin = Math.max(1, page.getNumber() + 1 - pageLinkMaxDispNum / 2);
int end = begin + (pageLinkMaxDispNum - 1);
if (end > page.getTotalPages() - 1) {
end = page.getTotalPages();
begin = Math.max(1, end - (pageLinkMaxDispNum - 1));
}
// (3)
return NumberUtils.sequence(begin, end);
}
}
項番 |
説明 |
---|---|
(1)
|
Page オブジェクトとリンクの最大表示数を引数に取り、Integer[] 型を返すメソッドを定義する。 |
(2)
|
表示するリンクの範囲(開始位置、終了位置)を計算する。
Page オブジェクトのnumber プロパティは0 開始のため、ページ番号を表示する際は+1 が必要となる。 |
(3)
|
計算したリンクの範囲(開始位置、終了位置)のリストを生成し返却する。
|
実装例(式オブジェクトの登録)
public class PageInfoDialect implements IExpressionObjectDialect {
// (1)
private static final String PAGE_INFO_DIALECT_NAME = "pageInfo";
private static final Set<String> EXPRESSION_OBJECT_NAMES = Collections
.singleton(PAGE_INFO_DIALECT_NAME);
@Override
public IExpressionObjectFactory getExpressionObjectFactory() {
return new IExpressionObjectFactory() {
// (1)
@Override
public Set<String> getAllExpressionObjectNames() {
return EXPRESSION_OBJECT_NAMES;
}
// (2)
@Override
public Object buildObject(IExpressionContext context,
String expressionObjectName) {
if (PAGE_INFO_DIALECT_NAME.equals(expressionObjectName)) {
return new PageInfo();
}
return null;
}
@Override
public boolean isCacheable(String expressionObjectName) {
return true;
}
};
}
// omitted
}
項番 |
説明 |
---|---|
(1)
|
pageInfo という名前で式オブジェクトを登録する。 |
(2)
|
実装した式オブジェクトを登録する。
|
最後に、作成したカスタムダイアレクトの設定を行う。設定例を以下に示す。
spring-mvc.xml
<bean id="templateEngine" class="org.thymeleaf.spring6.SpringTemplateEngine">
<!-- omitted -->
<property name="additionalDialects">
<set>
<bean class="org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect" />
<bean class="com.example.sample.dialect.PageInfoDialect"/> <!-- (1) -->
</set>
</property>
</bean>
項番 |
説明 |
---|---|
(1)
|
テンプレートエンジンに作成したカスタムダイアレクトを追加する。
|
以上でカスタムダイアレクトの実装及び設定は完了となる。
4.5.2.4.1.2.2. フラグメントにページネーションリンクのHTMLを実装する¶
ページネーションリンクのテンプレートHTMLのフラグメントの実装例を以下に示す。
th:fragment
属性を利用してページネーションリンクのHTMLを部品化している。${requestURI}
は、ThymeleafからModel
を介してHttpServletRequest
内のrequestURI
を利用する実装の例である。requestURI
の利用は、アプリケーション全体で共通的に必要になるケースが多いと考えられるため、ここでは各ハンドラメソッドで個別にModel
に配置するのではなく、@ControllerAdvice
を利用してController全体の共通処理として実装する例を示す。HttpServletRequest
の内容を利用する際の注意点については、「Web オブジェクト(HttpServletRequest、HttpSession等)の利用」を参照されたい。以降、以下のファイル構成を前提に実装例を示す。
File Path
WEB-INF └─ views └─ pgnt ├─ fragment.html (フラグメントを定義するテンプレートHTML) └─ serchResult.html (ページネーションを行う画面を実装するテンプレートHTML)
ControllerAdvice
@ControllerAdvice public class ThymeleafCommonControllerAdvice { @ModelAttribute("requestURI") // (1) public String requestURI(HttpServletRequest request) { return request.getRequestURI(); } }
項番
説明
(1)テンプレートHTML(フラグメント)の${requestURI}
がアクセスするための@ModelAttribute
。テンプレートHTML(フラグメント)
<!--/* ... */--> <!--/* (1), (2) */--> <div th:fragment="pagination (page)" th:object="${page}" th:remove="tag"> <!--/* (3), (4) */--> <ul th:if="*{totalElements} != 0" class="pagination" th:with="pageLinkMaxDispNum = 10, disabledHref = 'javascript:void(0)', currentUrl = ${requestURI}"> <!--/* (5) */--> <li th:class="*{isFirst()} ? 'disabled'"> <!--/* (6) */--> <a th:href="*{isFirst()} ? ${disabledHref} : @{{currentUrl}(currentUrl=${currentUrl},page=0,size=*{size})}"><<</a> </li> <!--/* (7) */--> <li th:class="*{isFirst()} ? 'disabled'"> <a th:href="*{isFirst()} ? ${disabledHref} : @{{currentUrl}(currentUrl=${currentUrl},page=*{number - 1},size=*{size})}"><</a> </li> <!--/* (8) */--> <li th:each="i : ${#pageInfo.sequence(page, pageLinkMaxDispNum)}" th:with="isActive=${i} == *{number + 1}" th:class="${isActive} ? 'active'"> <a th:href="${isActive} ? ${disabledHref} : @{{currentUrl}(currentUrl=${currentUrl},page=${i - 1},size=*{size})}" th:text="${i}"></a> </li> <!--/* (9) */--> <li th:class="*{isLast()} ? 'disabled'"> <a th:href="*{isLast()} ? ${disabledHref} : @{{currentUrl}(currentUrl=${currentUrl},page=*{number + 1},size=*{size})}">></a> </li> <!--/* (10) */--> <li th:class="*{isLast()} ? 'disabled'"> <a th:href="*{isLast()} ? ${disabledHref} : @{{currentUrl}(currentUrl=${currentUrl},page=*{totalPages - 1},size=*{size})}">>></a> </li> </ul> </div> <!--/* ... */-->
項番
説明
(1)th:fragment
属性を使用し、pagination
という名前でフラグメント化する。Page
オブジェクトをパラメータとして受け取るための引数page
を定義する。(2)th:object
属性にフラグメントの引数で受け取ったPage
オブジェクトを指定し、以降の処理でオブジェクト名を省略してプロパティを指定可能にする。(3)<ul>
要素を出力する。th:if
属性を用いてページの要素数が0ではない場合のみ<ul>
要素を出力するように制御している。ページネーションリンクであることを示すクラス名pagination
を指定している。(4)th:with
属性にてページネーションリンクを表示する際に使用するローカル変数を定義している。pageLinkMaxDispNum
には、「指定したページに移動するためのリンク」の最大表示数を指定する。disabledHref
には、ページリンク押下時の動作を無効化する場合(active
状態、またはdisabled
状態)にth:href
属性に指定する値を指定する。currentUrl
には、各ページリンクのth:href
属性の設定に使用するURLのパスを設定する。Note
disabledHrefの設定値について
実装例では、
disabledHref
にはjavascript:void(0)
を設定している。ページリンク押下時の動作を無効化するだけであれば、実装例と同じ設定でよい。ただし、実装例の設定でページリンクにフォーカスを移動又はマウスオーバーした場合、ブラウザのステータスバーに
javascript:void(0)
が表示されることがある。この挙動を変えたい場合は、JavaScriptを使用してページリンク押下時の動作を無効化する必要がある。Note
currentUrlの設定値について
実装例では
th:href
属性に前回リクエストのURLを指定するため、currentUrl
にHttpServletRequest
オブジェクトから取得したリクエストURIを設定している。th:href
属性に前回リクエストのURLを指定するのであれば、実装例と同じ設定でよい。th:href
属性に前回リクエストのURL以外を設定する場合は、アプリケーションの要件に応じてth:href
属性の指定方法を変更すること。th:href
属性に設定する値はフラグメントのパラメータとして受け取ることで対応可能である。(5)最初のページに移動するためのリンクを出力する。リンク先が現在のページである場合はdisabled
状態としている。リンクが不要な場合は<li>
要素ごと削除すること。(6)ページ移動するためのリクエストを送信する<a>
要素を出力する。th:href
属性は、現在のページが最初のページである場合は、ページリンク押下時の動作を無効化するためdisabledHref
を指定する。そうでない場合には、リンクURL式@{}
を使用してリクエストURLを生成して指定する。リンクURL式@{}
に指定するパスには(4)で定義したcurrentUrl
を、パラメータにはページ位置と取得件数を指定する。th:href
属性に指定するリクエストURLの生成の詳細については、「リクエストURLを生成する」を参照されたい。リンクとして表示する文字列は直接記載するかth:text
属性を用いて出力する。<a>
要素の基本的な構造は、以降の<a>
要素も同様であるため説明は省略する。(7)前のページに移動するためのリンクを出力する。リンク先が現在のページである場合はdisabled
状態としている。<a>
要素の属性は、現在のページが最初のページであるかを判定し、th:href
属性に値を設定している。リンクが不要な場合は<li>
要素ごと削除すること。(8)指定したページに移動するためのリンクをth:each
属性を利用し、繰り返し処理を行うことで出力する。th:each
属性に指定する配列は、「指定したページに移動するためのリンクの出力範囲の計算」で作成したカスタムダイアレクトを使用して取得する。リンクが不要な場合は<li>
要素ごと削除すること。(9)次のページに移動するためのリンクを出力する。リンク先が現在のページである場合はdisabled
状態としている。リンクが不要な場合は<li>
要素ごと削除すること。(10)最後のページに移動するためのリンクを出力する。リンク先が現在のページである場合はdisabled
状態としている。リンクが不要な場合は<li>
要素ごと削除すること。
4.5.2.4.1.2.3. フラグメントを利用してページネーションリンクを表示する¶
「テンプレートHTML(フラグメント)にページネーションリンクのHTMLを実装する」で実装したフラグメントを利用してページネーションリンクを表示するテンプレートHTML実装例を以下に示す。
テンプレートHTML(ページネーションを行う画面)
<!--/* ... */--> <!--/* (1) */--> <div th:replace="~{pgnt/fragment :: pagination (${page})}"></div> <!--/* ... */-->
項番
説明
(1)th:replace
属性を使用して、テンプレートであるpgnt/fragment.html
のpagination
フラグメントの内容でdiv
タグ以下の内容を置換している。パラメータとしてPage
オブジェクトを指定している。出力されるHTML
下記の出力例は、
?page=0&size=10
を指定して検索した際の結果である。<ul> <li class="disabled"><a href="javascript:void(0)"><<</a></li> <li class="disabled"><a href="javascript:void(0)"><</a></li> <li class="active"><a href="javascript:void(0)">1</a></li> <li><a href="?page=1&size=10">2</a></li> <li><a href="?page=2&size=10">3</a></li> <li><a href="?page=3&size=10">4</a></li> <li><a href="?page=4&size=10">5</a></li> <li><a href="?page=5&size=10">6</a></li> <li><a href="?page=6&size=10">7</a></li> <li><a href="?page=7&size=10">8</a></li> <li><a href="?page=8&size=10">9</a></li> <li><a href="?page=9&size=10">10</a></li> <li><a href="?page=1&size=10">></a></li> <li><a href="?page=9&size=10">>></a></li> </ul>
画面イメージ
ページネーションリンクとして成立したが、以下2つの問題が残る。
押下できるリンクと押下できないリンクの区別ができない。
現在表示しているページ位置がわからない。
上記の問題を解決する手段として、Bootstrap v3.0.0のスタイルシートと適用すると、以下のような表示となる。
画面イメージ
スタイルシート
bootstrap v3.0.0 の cssファイルを$WEB_APP_ROOT/resources/vendor/bootstrap-3.0.0/css/bootstrap.css
に配置する。以下、ページネーション関連のスタイル定義の抜粋。.pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.428571429; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 4px; border-top-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { background-color: #eeeeee; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #ffffff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #999999; cursor: not-allowed; background-color: #ffffff; border-color: #dddddd; }
テンプレートHTML
テンプレートHTMLでは配置したcssファイルを読み込む定義を追加する。
<!--/* ... */--> <link rel="stylesheet" th:href="@{/resources/vendor/bootstrap/dist/css/bootstrap.min.css}"> <!--/* ... */-->
4.5.2.4.1.3. ページネーション情報の表示¶
ページネーションに関連する情報(合計件数、合計ページ数、表示ページ数など)を表示するための実装例を以下に示す。
ここでは、どの画面でも共通のページネーション情報を表示するため、テンプレートHTML(フラグメント)に実装する。また、ページネーション情報に出力する「該当ページの表示データ範囲」については、計算ロジックを伴うため式オブジェクトに実装する。
画面例
テンプレートHTML(フラグメント)
<!--/* ... */--> <div th:fragment="paginationInfo (page)" th:object="${page}" th:remove="tag"> <!--/* (1) */--> <div class="text-center" th:text="|*{totalElements} results|"></div> <!--/* (2), (3) */--> <div th:if="*{totalElements} != 0" class="text-center" th:text="|*{number + 1} / *{totalPages} Pages|"></div> </div> <!--/* ... */-->
項番
説明
(1)検索条件に一致するデータの合計件数を表示する場合は、Page
オブジェクトのtotalElements
プロパティから値を取得する。(2)表示しているページのページ数を表示する場合は、Page
オブジェクトのnumber
プロパティから値を取得し、+1
する。Page
オブジェクトのnumber
プロパティは0
開始のため、ページ番号を表示する際は+1
が必要となる。(3)検索条件に一致するデータの合計ページ数を表示する場合は、Page
オブジェクトのtotalPages
プロパティから値を取得する。
画面例
「リンクの出力範囲を計算する式オブジェクトを実装する」で作成した式オブジェクトに新たにメソッドを実装する。実装例を以下に示す。
式オブジェクト
public class PageInfo { // omitted // (1) public int firstItemNumInPage(Page<T> page) { return page.getNumber() * page.getSize() + 1; } // (2) public int lastItemNumInPage(Page<T> page) { return page.getNumber() * page.getSize() + page.getNumberOfElements(); } }
項番
説明
(1)Page
オブジェクトを引数に取り、該当ページの表示データの開始位置を返すメソッドを定義する。Page
オブジェクトのnumber
プロパティは0
開始のため、データ開始位置を表示する際は+1
が必要となる。(2)Page
オブジェクトを引数に取り、該当ページの表示データの終了位置を返すメソッドを定義する。最終ページは端数となる可能性があるので、numberOfElements
を加算する必要がある。テンプレートHTML(フラグメント)
<!--/* ... */--> <div th:fragment="paginationInfo (page)" th:object="${page}" th:remove="tag"> <!--/* (4), (5) */--> <div class="text-center" th:text="|${#pageInfo.firstItemNumInPage(page)} - ${#pageInfo.lastItemNumInPage(page)}|"> </div> </div> <!--/* ... */-->
項番
説明
(4)開始位置を式オブジェクトを使用して取得する。(5)終了位置を式オブジェクトを使用して取得する。Tip
数値のフォーマットについて
表示する数値をフォーマットする必要がある場合は、Thymeleafから提供されているユーティリティメソッド
#numbers.formatInteger()
を使用する。
上記で定義したフラグメントを使用してページネーション情報を表示するテンプレートHTML(ページネーションを行う画面)の実装例は以下の通りとなる。
テンプレートHTML(ページネーションを行う画面)
<!--/* ... */--> <div th:replace="~{pgnt/fragment :: paginationInfo (${page})}"></div> <!--/* ... */-->
4.5.3. Appendix¶
4.5.3.1. PageableHandlerMethodArgumentResolver
のプロパティ値について¶
PageableHandlerMethodArgumentResolver
で指定できるプロパティは以下の通り。
項番
プロパティ名
説明
デフォルト値
maxPageSize
取得件数として許可する最大値を指定する。指定された取得件数がmaxPageSize
を超えていた場合は、maxPageSize
が取得件数となる。2000
fallbackPageable
アプリケーション全体のページ位置、取得件数、ソート条件のデフォルト値を指定する。ページ位置、取得件数、ソート条件が指定されていない場合は、fallbackPageableに設定されている値が適用される。 ページ位置 :0
取得件数 :20
ソート条件 :null
oneIndexedParameters
ページ位置の開始値を指定する。false
を指定した場合はページ位置の開始値は0
となり、true
を指定した場合はページ位置の開始値は1
となる。false
pageParameterName
ページ位置を指定するためのリクエストパラメータ名を指定する。page
sizeParameterName
取得件数を指定するためのリクエストパラメータ名を指定する。size
prefix
ページ位置と取得件数を指定するためのリクエストパラメータの接頭辞(ネームスペース)を指定する。デフォルトのパラメータ名がアプリケーションで使用するパラメータと衝突する場合は、ネームスペースを指定することでリクエストパラメータ名の衝突を防ぐことが出来る。prefixを指定すると、ページ位置を指定するためのリクエストパラメータ名はprefix + pageParameterName
、取得件数を指定するためのリクエストパラメータ名はprefix + sizeParameterName
となる。""
(ネームスペースなし)
qualifierDelimiter
同一リクエストで複数のページ検索が必要になる場合、ページ検索に必要な情報(検索対象のページ位置、取得件数など)を区別するために、リクエストパラメータ名はqualifier + delimiter + 標準パラメータ名
の形式で指定する。本プロパティは、上記形式の中のdelimiter
の値を設定する。この設定を変更する場合は、SortHandlerMethodArgumentResolver
のqualifierDelimiter
設定も合わせて変更する必要がある。 “_
“Note
maxPageSizeの設定値について
デフォルト値は
2000
であるが、アプリケーションが許容する最大値に設定を変更することを推奨する。アプリケーションが許可する最大値が
100
ならば、maxPageSize
も100
に設定する。Note
fallbackPageableの設定方法について
アプリケーション全体に適用するデフォルト値を変更する場合は、
fallbackPageable
プロパティにデフォルト値が定義されているPageable
(org.springframework.data.domain.PageRequest
) オブジェクトを設定する。ソート条件のデフォルト値を変更する場合は、
SortHandlerMethodArgumentResolver
のfallbackSort
プロパティにデフォルト値が定義されているorg.springframework.data.domain.Sort
オブジェクトを設定する。
開発するアプリケーション毎に変更が想定される以下の項目について、デフォルト値を変更する際の設定例を以下に示す。
取得件数として許可する最大値(
maxPageSize
)アプリケーション全体のページ位置、取得件数のデフォルト値(
fallbackPageable
)ソート条件のデフォルト値(
fallbackSort
)
@EnableAspectJAutoProxy
@EnableWebMvc
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(pageableHandlerMethodArgumentResolver());
}
@Bean
public PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver() {
PageableHandlerMethodArgumentResolver bean = new PageableHandlerMethodArgumentResolver(sortHandlerMethodArgumentResolver()); // (5)
bean.setMaxPageSize(100); // (1)
bean.setFallbackPageable(pageRequest()); // (2)
return bean;
}
@Bean
public PageRequest pageRequest() {
return PageRequest.of(0, 50); // (3)(4)
}
@Bean
public SortHandlerMethodArgumentResolver sortHandlerMethodArgumentResolver() {
SortHandlerMethodArgumentResolver bean = new SortHandlerMethodArgumentResolver();
bean.setFallbackSort(sort()); // (6)
return bean;
}
@Bean
public Sort sort() {
List<Order> list = new ArrayList<Order>();
list.add(order()); // (8)
return Sort.by(list); // (7)
}
@Bean
public Order order() {
return new Order(Direction.DESC, "article_id"); // (9)(10)
}
項番 |
説明 |
---|---|
(1)
|
上記例では取得件数の最大値を
100 に設定している。取得件数(size)に101 以上が指定された場合は、100 に切り捨てて検索が行われる。 |
(2)
|
org.springframework.data.domain.PageRequest のインスタンスを生成し、fallbackPageable に設定する。spring-data-commons 2.2.0より
PageRequest クラスからpublicなコンストラクタが削除されたため、factory-methodを利用してstaticなPageRequest#of メソッドによりBeanを生成する必要がある。 |
(3)
|
PageRequest#of メソッドの第1引数に、ページ位置のデフォルト値を指定する。上記例では
0 を指定しているため、デフォルト値は変更していない。 |
(4)
|
PageRequest#of メソッドの第2引数に、取得件数のデフォルト値を指定する。上記例ではリクエストパラメータに取得件数の指定がない場合の取得件数は
50 となる。 |
(5)
|
PageableHandlerMethodArgumentResolver のコンストラクタとして、SortHandlerMethodArgumentResolver のインスタンスを設定する。 |
(6)
|
Sort のインスタンスを生成し、fallbackSort に設定する。spring-data-commons 2.2.0より
Sort クラスからpublicなコンストラクタが削除されたため、factory-methodを利用してstaticなSort#by メソッドによりBeanを生成する必要がある。 |
(7)
|
Sort#by メソッドの第1引数に、デフォルト値として使用するOrder オブジェクトのリストを設定する。 |
(8)
|
Order のインスタンスを生成し、デフォルト値として使用するOrder オブジェクトのリストに追加する。 |
(9)
|
Order のコンストラクタの第1引数に、ソート順(ASC/DESC)を指定する。 |
(10)
|
Order のコンストラクタの第2引数に、ソート項目を指定する。 |
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean
class="org.springframework.data.web.PageableHandlerMethodArgumentResolver">
<!-- (1) -->
<property name="maxPageSize" value="100" />
<!-- (2) -->
<property name="fallbackPageable">
<bean class="org.springframework.data.domain.PageRequest" factory-method="of">
<!-- (3) -->
<constructor-arg index="0" value="0" />
<!-- (4) -->
<constructor-arg index="1" value="50" />
</bean>
</property>
<!-- (5) -->
<constructor-arg index="0">
<bean class="org.springframework.data.web.SortHandlerMethodArgumentResolver">
<!-- (6) -->
<property name="fallbackSort">
<bean class="org.springframework.data.domain.Sort" factory-method="by">
<!-- (7) -->
<constructor-arg index="0">
<list>
<!-- (8) -->
<bean class="org.springframework.data.domain.Sort.Order">
<!-- (9) -->
<constructor-arg index="0" value="DESC" />
<!-- (10) -->
<constructor-arg index="1" value="lastModifiedDate" />
</bean>
<!-- (8) -->
<bean class="org.springframework.data.domain.Sort.Order">
<constructor-arg index="0" value="ASC" />
<constructor-arg index="1" value="id" />
</bean>
</list>
</constructor-arg>
</bean>
</property>
</bean>
</constructor-arg>
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>
項番 |
説明 |
---|---|
(1)
|
上記例では取得件数の最大値を
100 に設定している。取得件数(size)に101 以上が指定された場合は、100 に切り捨てて検索が行われる。 |
(2)
|
org.springframework.data.domain.PageRequest のインスタンスを生成し、fallbackPageable に設定する。spring-data-commons 2.2.0より
PageRequest クラスからpublicなコンストラクタが削除されたため、factory-methodを利用してstaticなPageRequest#of メソッドによりBeanを生成する必要がある。 |
(3)
|
PageRequest#of メソッドの第1引数に、ページ位置のデフォルト値を指定する。上記例では
0 を指定しているため、デフォルト値は変更していない。 |
(4)
|
PageRequest#of メソッドの第2引数に、取得件数のデフォルト値を指定する。上記例ではリクエストパラメータに取得件数の指定がない場合の取得件数は
50 となる。 |
(5)
|
PageableHandlerMethodArgumentResolver のコンストラクタとして、SortHandlerMethodArgumentResolver のインスタンスを設定する。 |
(6)
|
Sort のインスタンスを生成し、fallbackSort に設定する。spring-data-commons 2.2.0より
Sort クラスからpublicなコンストラクタが削除されたため、factory-methodを利用してstaticなSort#by メソッドによりBeanを生成する必要がある。 |
(7)
|
Sort#by メソッドの第1引数に、デフォルト値として使用するOrder オブジェクトのリストを設定する。 |
(8)
|
Order のインスタンスを生成し、デフォルト値として使用するOrder オブジェクトのリストに追加する。 |
(9)
|
Order のコンストラクタの第1引数に、ソート順(ASC/DESC)を指定する。 |
(10)
|
Order のコンストラクタの第2引数に、ソート項目を指定する。 |
4.5.3.2. SortHandlerMethodArgumentResolver
のプロパティ値について¶
SortHandlerMethodArgumentResolver
で指定できるプロパティは以下の通り。項番 |
プロパティ名 |
説明 |
デフォルト値 |
---|---|---|---|
fallbackSort |
アプリケーション全体のソート条件のデフォルト値を指定する。
ソート条件が指定されていない場合は、fallbackSortに設定されている値が適用される。
|
null (ソート条件なし)
|
|
sortParameter |
ソート条件を指定するためのリクエストパラメータ名を指定する。
デフォルトのパラメータ名がアプリケーションで使用するパラメータと衝突する場合は、リクエストパラメータ名を変更することで衝突を防ぐことができる。
|
sort |
|
propertyDelimiter |
ソート項目及びソート順(ASC,DESC)の区切り文字を指定する。
|
“
, “ |
|
qualifierDelimiter |
同一リクエストで複数のページ検索が必要になる場合、ページ検索に必要な情報(ソート条件)を区別するために、リクエストパラメータ名は
qualifier + delimiter + sortParameter の形式で指定する。本プロパティは、上記形式の中の
delimiter の値を設定する。 |
“
_ “ |