Best practices for filtering data with a default load and a number of records

advertisements

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.