遅延読み込みしたいですよね?10000件とか下手したら無限とも言えるデータを全部メモリに抱え込んでとか、特にサーバーサイドにステートを持つのがデフォのJSFでやったら死んでしまいます。RichFacesには、そんな要望に応えるためのクラスがあります!!
個人的に残念なのは、JSFのクラスに依存してるところ…。POJOPOJOしたいです。
レッツトライ
とりあえず、やり方。NetBeansでmavenプロジェクトを追加してpomにrichfacesへの依存関係を追加します。
<dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-components-ui</artifactId> <version>4.3.1.Final</version> </dependency> <dependency> <groupId>org.richfaces.core</groupId> <artifactId>richfaces-core-impl</artifactId> <version>4.3.1.Final</version> </dependency>
テーブルに表示するデータの準備
とりあえず、テーブルに表示するためのデータを適当に作ります。サンプルなので、別にデータを取得するためのクラスを作るのがめんどくさいため、このクラスにstaticメソッドとしてデータを取得するメソッドも追加します。
データの件数は、とりあえず10000件を想定してます。
package com.mycompany.lazypager; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Person implements Serializable { private static final long serialVersionUID = 6101343577835043120L; private int id; private String name; public Person() { } public Person(int id, String name) { this.id = id; this.name = name; } // 指定した範囲のデータを返すメソッド public static Person[] getRange(int start, int count) { List<Person> r = new ArrayList<>(); for (int i = start; i < start + count; i++) { r.add(new Person(i, "田中 太郎" + i)); } return r.toArray(new Person[r.size()]); } // とりあえず一万件のデータを想定 public static int getLength() { return 10000; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
最大のポイント ExtendedDataModelを継承したクラス
ExtendedDataModel
package com.mycompany.lazypager; import javax.faces.context.FacesContext; import org.ajax4jsf.model.DataVisitor; import org.ajax4jsf.model.ExtendedDataModel; import org.ajax4jsf.model.Range; import org.ajax4jsf.model.SequenceRange; public class PersonDataModel extends ExtendedDataModel<Person> { // 現在表示中のページのデータ private Person[] currentPageData; // 現在行を識別するためのキー private Integer rowKey; // ExtendedDataModel<T>で、int以外も行を識別できるようにするために追加されたメソッド… // 個人的にはいびつだと思う…。 @Override public void setRowKey(Object o) { this.rowKey = (Integer)o; } @Override public Object getRowKey() { return this.rowKey; } // ここで必要なデータだけとって、DataVisitorにRowKeyを渡して処理をよろしくしてもらう @Override public void walk(FacesContext fc, DataVisitor dv, Range range, Object arg) { // Rangeから(SequenceRangeがわたってくるみたい)現在表示対象のデータを取得 SequenceRange seq = (SequenceRange)range; this.currentPageData = Person.getRange(seq.getFirstRow(), seq.getRows()); for (int i = 0; i < this.currentPageData.length; i++) { // 第二引数にrowKeyを渡す点がポイント // 今回は取得したデータのインデックス dv.process(fc, i, arg); } } @Override public boolean isRowAvailable() { // getRowDataで取得可能な現在行が有効かどうかを返すらしい。rowKeyがnullじゃなかったらOK return this.rowKey != null; } @Override public int getRowCount() { // データ件数 return Person.getLength(); } @Override public Person getRowData() { // rowKeyを元にデータを取得 return this.currentPageData[this.rowKey]; } // ここから下は実装しなくてもいい // 特にrowIndexはrowKeyにとってかわられたいらない子 @Override public int getRowIndex() { return -1; // 何かしら返さないとだめみたい } @Override public void setRowIndex(int rowIndex) { throw new UnsupportedOperationException("not supported."); } @Override public Object getWrappedData() { // とりあえず使わない throw new UnsupportedOperationException("not supported."); } @Override public void setWrappedData(Object data) { // とりあえず使わない throw new UnsupportedOperationException("not supported."); } }
管理ビーンを作る
もう一息です。管理ビーンを作ります。こいつは、単純にPersonDataModelを公開するだけの管理ビーンです。なんてことはないクラスですが、ManagedBeanやRequestScopeアノテーションはパッケージ名を確認して別の同名のアノテーションをつけないようにしましょう!
package com.mycompany.lazypager; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class IndexPage implements Serializable { private static final long serialVersionUID = 1L; private PersonDataModel people = new PersonDataModel(); public PersonDataModel getPeople() { return people; } public void setPeople(PersonDataModel people) { this.people = people; } }
ページ作成
あとはdataTableとdataScrollerを配置した画面を作って完了です。ポイントはdataTableにrowsプロパティできちんと1ページに表示する行を指定してやるところです。dataScrollerは、forプロパティでテーブルのidを指定します。
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:rich="http://richfaces.org/rich" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>Lazy</title> </h:head> <h:body> <h:form> <rich:dataTable id="table" var="person" value="#{indexPage.people}" rows="10"> <rich:column> <f:facet name="header"> ID </f:facet> <h:outputText value="#{person.id}" /> </rich:column> <rich:column> <f:facet name="header"> Name </f:facet> <h:outputText value="#{person.name}" /> </rich:column> <f:facet name="footer"> <rich:dataScroller for="table" /> </f:facet> </rich:dataTable> </h:form> </h:body> </html>