I'm using the Datatable from Primefaces with lazy loading. When the values are filtered, the size of the returned records is smaller than the absolute count I used for the pager. So there are empty pages in the datatable. But I also could not use the size of the record size I get from the query because it's possible the query returns only a subset.
Example: 30 records in the database, page size in the datatable is 10, so there are 3 pages and every lazy load request I return 10 records.
When I enter a filter so that there are only 20 records left. The pager shows still 3 pages because of the absolute count. But when I change the record count to the 10 records I return, there is no second page. So how can I get the count of all the filtered records? With a own query?
I use the following strategy for that.
First I created a generic datamodel. This datamodel depends on the way I design my entities (JPA), my filter classes and my paginable services. Note that I use a pojo to transport the search criteria and you would need to do some small changes in order to use the Map filters parameter.
Here are the resulting datamodel:
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortMeta;
import org.primefaces.model.SortOrder;
public class GenericDataModel<ENTITY extends AbstractEntity, FILTER extends AbstractFilter, SERVICE extends Paginable<ENTITY, FILTER>>
extends LazyDataModel<ENTITY> {
private FILTER filter;
private SERVICE service;
private boolean started;
//use this constructor if you want to show some result on page load
public GenericDataModel(FILTER filter, SERVICE service) {
this(filter, service, true);
}
public GenericDataModel(FILTER filter, SERVICE service, boolean autoStart) {
this.filter = filter;
this.service = service;
started = autoStart;
count();
}
@Override
public List<ENTITY> load(int first, int pageSize,
List<SortMeta> multiSortMeta, Map<String, String> filters) {
if (!started) {
return new ArrayList<ENTITY>();
}
if (getRowCount() <= 0) {
count();
}
//deal with sorting here. I do that by inserting them into the filter.
return service.find(filter);
}
@Override
public List<ENTITY> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, String> filters) {
if (!started) {
return new ArrayList<ENTITY>();
}
if (getRowCount() <= 0) {
count();
}
//deal with sorting here. I do that by inserting them into the filter.
return service.find(filter);
}
public void count() {
if (!started) {
setRowCount(0);
return;
}
Long rowCount = service.count(filter);
setRowCount(rowCount == null ? 0 : rowCount.intValue());
}
public void start() {
started = true;
}
}
And here is how I use it on my managed beans:
@Inject
private ProfessorService professorService;
private ProfessorFilter professorFilter;
private GenericDataModel<Professor, ProfessorFilter, ProfessorService> dataModel;
@PostConstruct
public String setup() {
professorFilter = new ProfessorFilter();
dataModel = new GenericDataModel(professorFilter, professorService, false);
return null;
}
//action for the search button on the view
public void search() {
dataModel.start();
dataModel.count();
}
The paginableService.count method does a select count(*)
using the same join and where clause used by the resulting query of paginableService.find(filter)
method.
For really huge data in you may find that this extra query may be a waste of time and resources, but remember that this extra query is done only when your search criteria changes.