I’ve actually blogged about this topic three years ago and introduced the PagedListDataModel. That solution worked at that time with some flows in design but now after three years, I’ve a much better solution to the same problem. Using PrimeFaces DataTable it’s a piece of cake to load millions of data lazily, just set the lazy to true and provide a LazyDataModel. Here’s is how it works.
<p:dataTable var="car" value="#{tableBean.lazyModel}" paginator="true" rows="10"
dynamic="true" lazy="true">
<p:column>
<f:facet name="header">
<h:outputText value="Model" />
</f:facet>
<h:outputText value="#{car.model}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Year" />
</f:facet>
<h:outputText value="#{car.year}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Manufacturer" />
</f:facet>
<h:outputText value="#{car.manufacturer}" />
</p:column>
<p:column>
<f:facet name="header">
<h:outputText value="Color" />
</f:facet>
<h:outputText value="#{car.color}" />
</p:column>
</p:dataTable>
And the lazyModel;
public class TableBean {
private LazyDataModel<Car> lazyModel;
public TableBean() {
/**
* Test with one hundred million records.
* In a real application use an sql Count query to get the row count.
*/
lazyModel = new LazyDataModel<Car>(100000000) {
/**
* Dummy implementation of loading a certain segment of data.
* In a real applicaiton, this method should access db and do a limit based query
*/
@Override
public List<Car> fetchLazyData(int first, int pageSize) {
logger.info("Loading the lazy car data between {} and {}", first, first+pageSize);
List<Car> lazyCars = new ArrayList<Car>();
populateLazyRandomCars(lazyCars, pageSize, first);
return lazyCars;
}
};
}
public LazyDataModel<Car> getLazyModel() {
return lazyModel;
}
private void populateLazyRandomCars(List<Car> list, int size, int first) {
for(int i = 0 ; i < size ; i++) {
int offset = i + first;
list.add(new Car("Model_" + offset, getRandomYear(), "Brand_" + offset, "Color_" + offset));
}
}
}
That’s just it, whenever a paging event occurs with ajax, your fetchLazyData implementation will be called with the offset and pageSize. In a real application, you need to use your specific data access methods to load a chunk of data between a certain interval. For example in JPA api setMaxResults(pageSize) and setFirstResult(first) would do the trick. Also you need to define how many virtual records are there to be displayed so that PrimeFaces DataTable can create it’s paginator using that value, as an example a count/projection query could be used.
So to sum up, with PrimeFaces DataTable lazy loading capabilities even if you have billions of records, you can enable lazy loading very easily since number of all records are not relevant, only the records on the current datatable page is loaded.
There’s an online demo that demonstrates how to display one hundred million(100000000) records with this feature with paging happening less than a second.

