PageNavigator の 表示要素として、 PageableListView
を使用していたのを、
DataView
に書き換えて、Limit Offset を指定できるようにした話を書きます。
経緯
Stateless な ページを作っていて、後々気づいて書き直した部分になります。
実装経緯は以下の記事を参照して頂くと分かるかと思います。1
[1] 意味不明だったらごめんなさい。。
PageableListView について
PagingNavigator (StatelessPagingNavigator の含む) の表示要素として、
IPageable な インスタンスを渡す必要があります。
PageableListView は、 IPageable を実装しており、
以下のように、IPageable な インスタンスとして設定することができます。
-
抜粋
// Add FestivalListView PageableListView<Festival> festivals = newFestivalListView(parameters.get(PAGING_PAGE_PARAMETER).toLong(0)); add(new StatelessPagingNavigator("navigator", parameters, festivals)); add(festivals);
-
newFestivalListView(long l) ページング表示件数に関わらず、
PageableListView
は要素全件を取得する実装になります。
1万件あって、1万件取得しても、20件しかなければ、20件だけ描画となります。
private PageableListView<Festival> newFestivalListView(long l) { LoadableDetachableModel<List<Festival>> festivals = new LoadableDetachableModel<List<Festival>>() { private static final long serialVersionUID = 7474274077691068779L; @Override protected List<Festival> load() { // Get ListView Elements FestivalRepository repository = _new(FestivalRepository.class); return repository.findAll(); } }; int itemsPerPage = 20; PageableListView<Festival> listView = new PageableListView<Festival>("festivals", festivals, itemsPerPage) { private static final long serialVersionUID = -6536722749788422260L; @Override protected void populateItem(ListItem<Festival> item) { item.add(new Label("siteUrl")); PageParameters param = new PageParameters(); Festival fes = item.getModelObject(); param.add("id", fes.getId()); Link link = new BookmarkablePageLink<>("festivalLink", FestivalDetailPage.class, param); link.add(new Label("festivalName", fes.getName())); item.add(link); byte[] imageData = fes.getThumbalizr(); Component component = null; if (imageData != null) { component = new InlineImage("festvalImage", imageData); } else { component = new ExternalImage("festvalImage", "/static/images/no_image.png"); } item.add(component); // item の css に category を追加 String css = "category-" + String.valueOf(fes.getHeldYearId().getHeldYear()); item.add(AttributeModifier.append("class", css)); } }; // ListView を 表示する listView.setVisible(true); // MarkupI を 表示する listView.setOutputMarkupId(true); return listView; }
DataView について
PageableListView
の実装を、DataView
と、 IDataProvider
の実装クラスを使用する形式に書き換えると、
1万件ある際、表示件数のみ取得できるようになります。2
[2] 件数次第ですが、システム的な負荷が削減できるかと。
-
newFestivalDataView(long l)
private DataView<Festival> newFestivalDataView(long l) { final FestivalRepository repository = _new(FestivalRepository.class); IDataProvider<Festival> dataProvider = new IDataProvider<Festival>() { @Override public void detach() { // Do Nothing... } @Override public Iterator iterator(long offset, long limit) { return repository.findWithHeldYearAndThumbalizrByLimitAndOffset((int) limit, (int) offset).iterator(); } @Override public long size() { return repository.getRecordCount(); } @Override public IModel model(Festival o) { return new LoadableDetachableModel() { @Override protected Festival load() { return o; } }; } }; int itemsPerPage = 20; DataView<Festival> dataView = new DataView<Festival>("festivals", dataProvider, itemsPerPage) { private static final long serialVersionUID = -9200004825371647245L; @Override protected void populateItem(Item<Festival> item) { item.add(new Label("siteUrl")); PageParameters param = new PageParameters(); Festival fes = item.getModelObject(); param.add("id", fes.getId()); Link link = new BookmarkablePageLink<>("festivalLink", FestivalDetailPage.class, param); link.add(new Label("festivalName", fes.getName())); item.add(link); byte[] imageData = fes.getThumbalizr(); Component component = null; if (imageData != null) { component = new InlineImage("festvalImage", imageData); } else { component = new ExternalImage("festvalImage", "/static/images/no_image.png"); } item.add(component); // item の css に category を追加 String css = "category-" + String.valueOf(fes.getHeldYearId().getHeldYear()); item.add(AttributeModifier.append("class", css)); } }; // ListView を 表示する dataView.setVisible(true); // Markup を 表示する dataView.setOutputMarkupId(true); return dataView; }
-
説明1
IDataProvider
をインナークラス定義して、必要なメソッドを実装しています。
で必要な範囲のデータを取得する。@Override public Iterator iterator(long offset, long limit) { return repository.findWithHeldYearAndThumbalizrByLimitAndOffset((int) limit, (int) offset).iterator(); }
でデータ件数を返すように実装しています。@Override public long size() { return repository.getRecordCount(); }
のロジックの妥当性はちょっとわからないですが、 Users forum - Loadable-detachable model for ListView@Override public IModel model(Festival o) { return new LoadableDetachableModel() { @Override protected Festival load() { return o; } }; }
等で、似た様な実装を見かけたので同じように実装しています。3
[3] おそらく、セッションを確立して30分以内とかそういうレベルであれば、問題なく動作すると思います。 -
説明2
DataView
自体の実装は、PageableListView
を使う実装とほぼ変わりないです。
肝は、IDataProvider
なのかと思いました。 -
説明3
StatelessPagingNavigator
に対してDataView
を設定していますが、
DataView
自体がStateless
なのか問題なく動作しました。
Google で StatelessDataView で検索すると、ヒットしますが、DataView
を直接使用しても問題なくStateless
になりました。
以上です。
Stateless
を意識しはじめると途端に実装が面倒になりますが、
面倒くさいですが。パターンとしてはそれなりに、網羅し始めているように感じていて、
大きなハマりポイントはそれなりになくなってる気がしてきました。
面倒であることはかわりないですが、
Stateful にしないようにするメリットも大きいと思うので、
もうしばらく、調べたりしようかと思います。
コメント