Как запрашивать данные для Primefaces dataTable с помощью ленивой загрузки и разбивки на страницы
В моем JSF datatable я реализовал ленивую загрузку, и когда я разбиваю страницы на записи, для выполнения следующего набора записей требуется около 4 или 5 секунд, на самом деле для выполнения результатов требуется меньше секунды.
Это случилось с тем, как я его реализовал, не знаю, как я могу это решить.
Класс DataModel, который расширяет 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;
}
и в классе 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();
}
Может ли кто-нибудь объяснить, что вызвало эту задержку в разбивке по страницам на страницы?
Если я удалю следующие
if (requestList.size() > maxPerPage)
{
System.out.println("executing");
return requestList.subList(startingAt, startingAt + maxPerPage);
}
else
{
System.out.println("executing else ");
return requestList;
}
и выполнить, то он выполняется совершенно без задержек, однако проблема заключается в this.setRowCount(requestList.size());
always 5, который является моим числом записей по умолчанию на странице.
Обновление 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;
}
}
Используется другой запрос для получения количества результатов с помощью следующих
@Override
public int count() {
int count = ((Long) sessionFactory.getCurrentSession()
.createQuery("select count(*) from Request").uniqueResult())
.intValue();
System.out.println(" count size " + count);
return count;
}
и мой 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();
}
Ответы
Ответ 1
В случае очень больших результирующих списков операции подсчет на стороне Java и операции подкрепления могут быть опасны для использования памяти и, следовательно, также на стороне производительности.
Вместо этого я обычно использую следующий подход: использовать 2 запроса, один для подсчета отфильтрованного набора результатов (я позволяю db делать счет), а другой для получения разбитого на результат результирующего набора (I пусть db выберет подсписку).
Я никогда не испытывал значительных задержек, даже если таблицы содержат миллионы строк.
Выполняется конкретный пример сортировки и фильтрации. Весь код использует стандарт JPA (без функций Hibernate или Spring). В таких ситуациях особенно рекомендуется подход CriteriaQuery
.
класс 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> ()));
}
}
Класс 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();
}
}
Ответ 2
Я не уверен, насколько это актуально в этом случае, но добавив к наблюдениям @perissf, я буду обеспокоен следующим:
if (entry.getValue() != null)
{
criteria.add(Restrictions.ilike("requestNo",
"%" + entry.getValue() + "%"));
}
Чтобы это разрешилось в запрос, похожий на
WHERE UPPER(request_no) LIKE '%VALUE%'
который будет выполнять полное сканирование таблицы, поскольку индекс в request_no
не может быть использован в этом экземпляре, что было бы очень медленным для таблиц с большим количеством строк по двум причинам:
-
UPPER(request_no)
потребуется функциональный индекс.
-
like '%anything'
нужно будет просматривать каждое значение request_no
независимо от того, присутствует ли функциональный индекс или нет.