web-dev-qa-db-fra.com

Comment interroger des données pour la table de données Primefaces à l'aide du chargement paresseux et de la pagination

Dans la table de données de mon JSF, j'ai implémenté le chargement paresseux et lorsque je pagine dans les enregistrements, cela prend environ 4 ou 5 secondes pour exécuter le prochain ensemble d'enregistrements, en fait, cela devrait prendre moins d'une seconde pour exécuter les résultats.

Cela est arrivé à la façon dont je l'ai mis en œuvre, je ne sais pas comment pourrais-je résoudre ce problème.

Classe DataModel qui étend LazyDataModel

@Override
public List<Request> load(int startingAt, int maxPerPage, String sortField,
                          SortOrder sortOrder, Map<String, String> filters)
{
    requestList = requestService.getRequest(startingAt, maxPerPage,
                                            sortField, sortOrder, filters);
    this.setRowCount(requestList.size());
    if (requestList.size() > maxPerPage)
    {
        System.out.println("executing");
        return requestList.subList(startingAt, startingAt + maxPerPage);
    }
    else
    {
        System.out.println("executing else ");
        return requestList;
    }

    return requestList;
}

et en classe dao

@Override
public List<Request> getRequest(int startingAt, int maxPerPage,
                                String sortField, SortOrder sortOrder, Map<String, String> filters)
{
    Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
                            Request.class);
    criteria.addOrder(Order.desc("requestNo"));
    for (Map.Entry<String, String> entry : filters.entrySet())
    {
        if (entry.getValue() != null)
        {
            criteria.add(Restrictions.ilike("requestNo",
                                            "%" + entry.getValue() + "%"));
        }
    }
    //criteria.setMaxResults(maxPerPage);
    //criteria.setFirstResult(startingAt);
    return criteria.list();
}

Quelqu'un pourrait-il expliquer ce qui a causé ce retard dans la pagination des enregistrements?

Si je supprime ce qui suit

if (requestList.size() > maxPerPage)
{
    System.out.println("executing");
    return requestList.subList(startingAt, startingAt + maxPerPage);
}
else
{
    System.out.println("executing else ");
    return requestList;
}

et exécuter, puis il s'exécute parfaitement sans délai, cependant le problème est this.setRowCount(requestList.size()); toujours 5 qui est mon nombre par défaut d'enregistrements par page.

Update 2

@Override
    public List<Request> load(int startingAt, int maxPerPage, String sortField,
            SortOrder sortOrder, Map<String, String> filters) {
        requestList = requestService.getRequest(startingAt, maxPerPage,
                sortField, sortOrder, filters);
        this.setRowCount(requestService.getRequestCount());
        if (requestService.getRequestCount() > maxPerPage) {
            try {

                return requestList.subList(startingAt, startingAt + maxPerPage);
            } catch (IndexOutOfBoundsException e) {
                //e.printStackTrace();
                return requestList.subList(startingAt, startingAt
                        + (requestService.getRequestCount() % maxPerPage));
            }
        } else {
            return requestList;
        }       
    }

A utilisé une autre requête pour obtenir le nombre de résultats à l'aide des éléments suivants

@Override
    public int count() {
        int count = ((Long) sessionFactory.getCurrentSession()
                .createQuery("select count(*) from Request").uniqueResult())
                .intValue();
        System.out.println(" count size " + count);
        return count;
    }

et mon dao

@Override
        public List<Request> getRequest(int startingAt, int maxPerPage,
                String sortField, SortOrder sortOrder, Map<String, String> filters) {
            Criteria criteria = sessionFactory.getCurrentSession().createCriteria(
                    Request.class);
            criteria.addOrder(Order.desc("requestNo"));
            for (Map.Entry<String, String> entry : filters.entrySet()) {
                if (entry.getValue() != null) {
                    criteria.add(Restrictions.ilike("requestNo",
                            "%" + entry.getValue() + "%"));         }
            }
             criteria.setMaxResults(maxPerPage);
             criteria.setFirstResult(startingAt);       
                return criteria.list(); 

        }
23
Jåcob

En cas de très grandes listes résultantes, les opérations côté Java comptage et sous-liste peuvent être dangereuses pour l'utilisation de la mémoire et par conséquent également du côté des performances.

Au lieu de cela, j'utilise généralement l'approche suivante: tilisez 2 requêtes, une pour compter le resultSet filtré (je laisse le db faire le compte), et une autre pour récupérer le resultSet paginé (je laisse le db extraire la sous-liste). Je n'ai jamais connu de retards importants, même avec des tables contenant des millions de lignes.

Suit un exemple concret de tri et de filtrage. Tout le code utilise la norme JPA (pas de fonctionnalités personnalisées Hibernate ou Spring) L'approche CriteriaQuery est particulièrement indiquée dans de telles situations.

classe MyBean

@ManagedBean
@ViewScoped
public class MyBean {
    @EJB
    private MyObjFacade myObjFacade;
    private LazyDataModel<MyObjType> model;        // getter and setter

    @PostConstruct
    public void init() {
        model = new LazyDataModel<MyObjType> () {

            @Override
            public List<MyObjType> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
                model.setRowCount(myObjFacade.count(filters));
                return myObjFacade.getResultList(first, pageSize, sortField, sortOrder, filters);
            }
        };
        model.setRowCount(myObjFacade.count(new HashMap<String, String> ()));
    }
}

classe MyObjFacade

@Stateless
public class MyObjFacade {
    @PersistenceContext
    private EntityManager em;
    @EJB
    private MyObjFacade myObjFacade;

    private Predicate getFilterCondition(CriteriaBuilder cb, Root<MyObjType> myObj, Map<String, String> filters) {
        Predicate filterCondition = cb.conjunction();
        String wildCard = "%";
        for (Map.Entry<String, String> filter : filters.entrySet()) {
            String value = wildCard + filter.getValue() + wildCard;
            if (!filter.getValue().equals("")) {
                javax.persistence.criteria.Path<String> path = myObj.get(filter.getKey());
                filterCondition = cb.and(filterCondition, cb.like(path, value));
            }
        }
        return filterCondition;
    }

    public int count(Map<String, String> filters) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
        Root<MyObjType> myObj = cq.from(MyObjType.class);
        cq.where(myObjFacade.getFilterCondition(cb, myObj, filters));
        cq.select(cb.count(myObj));
        return em.createQuery(cq).getSingleResult().intValue();
    }

    public List<MyObjType> getResultList(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
        CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<MyObjType> cq = cb.createQuery(MyObjType.class);
        Root<MyObjType> myObj = cq.from(MyObjType.class);
        cq.where(myObjFacade.getFilterCondition(cb, myObj, filters));
        if (sortField != null) {
            if (sortOrder == SortOrder.ASCENDING) {
                cq.orderBy(cb.asc(myObj.get(sortField)));
            } else if (sortOrder == SortOrder.DESCENDING) {
                cq.orderBy(cb.desc(myObj.get(sortField)));
            }
        }
        return em.createQuery(cq).setFirstResult(first).setMaxResults(pageSize).getResultList();
    }
}
30
perissf

Je ne sais pas si cela est pertinent dans ce cas, mais en ajoutant aux observations de @ perissf, je serais préoccupé par les points suivants:

if (entry.getValue() != null)
{
    criteria.add(Restrictions.ilike("requestNo",
                                    "%" + entry.getValue() + "%"));
}

Car cela se résoudrait en une requête semblable à

WHERE UPPER(request_no) LIKE '%VALUE%'

qui ferait une analyse complète des tables, car un index sur request_no ne pouvait pas être utilisé dans ce cas, ce qui serait très lent pour les tables avec une grande quantité de lignes pour deux raisons:

  • UPPER(request_no) aurait besoin d'un index fonctionnel.
  • like '%anything' Devrait parcourir toutes les valeurs de request_no, Qu'un index fonctionnel soit présent ou non.
2
beny23