package com.thingtrack.konekti.dao.template.util;
import java.util.AbstractList;
import java.util.List;
import javax.persistence.Parameter;
import javax.persistence.TypedQuery;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.jpa.JpaQuery;
import org.eclipse.persistence.queries.ReadAllQuery;
import org.eclipse.persistence.queries.ReportQuery;
/**
* Example class that wraps the execution of a {@link TypedQuery} calculating
* the current size and then paging the results using the provided page size.
* The query should contain an ORDER BY and the usage of this may produce
* incorrect results if the matching data set changes on the database while the
* results are being paged.
*
* @author dclarke
* @since EclipseLink 2.3.1
*/
public class PagingList<X> extends AbstractList<X> {
/**
* The size determined by {@link #calculateSize()}
*/
private int size;
/**
* Provided page size
*/
private int pageSize;
/**
* Original query. This query is closed for size calculation and re-used
* with different first/max values for each page result.
*/
private TypedQuery<X> query;
/**
* Cached page results. This approach holds all retrieved pages. if the
* total volume of results is large this caching should be changed to only
* hold a limited set of pages or rely on garbage collection to clear out
* unused pages.
*/
private List<X>[] pages;
@SuppressWarnings("unchecked")
public PagingList(TypedQuery<X> query, int pageSize) {
this.query = query;
this.pageSize = pageSize;
this.size = calculateSize();
this.pages = new List[(this.size / this.pageSize) + (this.size % this.pageSize > 0 ? 1 : 0)];
}
@Override
public X get(int index) {
return getPage(index / this.pageSize).get(index % this.pageSize);
}
@Override
public int size() {
return this.size;
}
protected TypedQuery<X> getQuery() {
return query;
}
//protected List<X>[] getPages() {
public List<X>[] getPages() {
return pages;
}
//protected List<X> getPage(int pageNum) {
public List<X> getPage(int pageNum) {
List<X> page = getPages()[pageNum];
if (page == null) {
getQuery().setFirstResult(this.pageSize * pageNum);
getQuery().setMaxResults(this.pageSize);
page = getQuery().getResultList();
getPages()[pageNum] = page;
}
return page;
}
/**
* Using the provided {@link TypedQuery} to calculate the size. The query is
* copied to create a new query which just retrieves the count.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private int calculateSize() {
JpaQuery<X> queryImpl = (JpaQuery<X>) getQuery();
ReadAllQuery raq = JpaHelper.getReadAllQuery(getQuery());
ReportQuery rq = null;
if (raq.isReportQuery()) {
rq = (ReportQuery) raq.clone();
rq.getItems().clear();
rq.addCount();
rq.getGroupByExpressions().clear();
rq.getOrderByExpressions().clear();
} else {
rq = new ReportQuery();
rq.setReferenceClass(raq.getReferenceClass());
rq.addCount();
rq.setShouldReturnSingleValue(true);
rq.setSelectionCriteria(raq.getSelectionCriteria());
}
// Wrap new report query as JPA query for execution with parameters
TypedQuery<Number> countQuery = (TypedQuery<Number>) JpaHelper.createQuery(rq, queryImpl.getEntityManager());
// Copy parameters
for (Parameter p : getQuery().getParameters()) {
countQuery.setParameter(p, getQuery().getParameterValue(p));
}
return countQuery.getSingleResult().intValue();
}
}