2012-12-20 13 views
20

W moim JSF datatable zaimplementowałem leniwy ładowanie i kiedy będę stronił przez rekordy, to zajmuje to około 4 lub 5 sekund, aby wykonać następny zestaw rekordów, faktycznie powinien on być poświęcić mniej niż sekundę, aby wykonać wyniki.Jak wyszukiwać dane dla Primefaces dataTable za pomocą leniwego ładowania i stronicowania

To się stało w sposób, w jaki go wdrożyłem, nie wiem, jak mogę to rozwiązać.

klasa DataModel która rozciąga 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; 
} 

oraz w klasie 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(); 
} 

Może ktoś wyjaśnić, co spowodowało to opóźnienie w Stronicowanie poprzez zapisy?

Jeśli usunąć następujące

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

i wykonać, to wykonuje doskonale bez zwłoki, jednak problem jest this.setRowCount(requestList.size()); zawsze 5, który jest moją domyślną liczbę rekordów na stronie.

Aktualizacja 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; 
     }  
    } 

Używane innego zapytania dla uzyskania rachubę wynikowego używając następującej

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

i mojego 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(); 

     } 

Odpowiedz

26

W przypadku bardzo dużych list wynikających, strona Java zliczająca i podlistowanie operacje mogą być niebezpieczne dla użycia pamięci, a w konsekwencji także dla wydajności.

Zamiast I zwykle z następującym podejściu: stosowanie 2 odpytuje, jeden do liczenia przefiltrowaną wynikowego (I niech db zrobić licznik), a drugi do pobierania numeracją stron wynikowego (I niech ekstrakt db podlista). Nigdy nie doświadczyłem znacznych opóźnień, nawet w przypadku tabel zawierających miliony wierszy.

Wykonuje konkretny przykład z sortowaniem i filtrowaniem. Cały kod wykorzystuje standard JPA (bez funkcji Hibernuj i Spring). W takich sytuacjach szczególnie wskazane jest podejście CriteriaQuery.

klasa 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>())); 
    } 
} 

klasa 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(); 
    } 
} 
+0

Próbowałem z 2 różnymi zapytaniami, dodałem mój kod jako Update 2, edytując moje pytanie. Tutaj problem polega na tym, że po paginacji do strony drugiej dostaję 'java.lang.IndexOutOfBoundsException' pomimo tego, że łapię ten wyjątek, widzę mój kod. Co mogłoby być tego przyczyną? – user75ponic

+0

Musisz użyć filtrów również w zapytaniu o liczbę. – perissf

+0

Czy mógłbyś podać przykład, jak uzyskać 1) jeden dla zliczenia przefiltrowanego wyniku Ustaw (pozwoliłem db zrobić liczenie) 2) do pobierania paginated resultSet (pozwoliłem db wyodrębnić podlistę). Dzięki – user75ponic

2

Nie jestem pewien, czy to jest istotne w tym przypadku, ale dodanie do użytkownika @ perissf obserwacji, byłbym zaniepokojony o następującym:

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

Do tego będzie rozwiązać w zapytaniu podobny do

WHERE UPPER(request_no) LIKE '%VALUE%' 

który byłby pełny skan tabeli, jak indeks na request_no nie mogą być wykorzystane w tym przypadku, co byłoby bardzo powolne dla tabel z dużą ilością wierszy z dwóch powodów:

  • UPPER(request_no) potrzebowałby funkcjonalnego indeksu.
  • like '%anything' musiałby przejrzeć każdą wartość request_no niezależnie od tego, czy indeks funkcjonalny jest obecny, czy nie.
+0

Dzięki za wskazanie tego. Więc jakie jest najlepsze podejście, aby uniknąć pełnego skanowania tabeli? – user75ponic

+1

Zależy od tego, co próbujesz zrobić. Czy ma sens wyszukiwanie podłańcucha wewnątrz numeru zgłoszenia? Czy możesz żyć z "podobną" wartością% "'? Czy 'requestNo' jest liczbą lub w rzeczywistości ciągiem znaków? – beny23

+0

Warto rozważyć inne pytanie dotyczące tych problemów. – beny23

Powiązane problemy