diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java
index 0bf18b041..4f582c591 100644
--- a/src/main/java/com/avaje/ebean/EbeanServer.java
+++ b/src/main/java/com/avaje/ebean/EbeanServer.java
@@ -1020,7 +1020,7 @@ public interface EbeanServer {
SqlFutureList findFutureList(SqlQuery query, Transaction transaction);
/**
- * Return a PagedList for this query.
+ * Return a PagedList for this query using pageIndex and pageSize.
*
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
@@ -1042,6 +1042,38 @@ public interface EbeanServer {
*/
PagedList findPagedList(Query query, Transaction transaction, int pageIndex, int pageSize);
+ /**
+ * Return a PagedList for this query using firstRow and maxRows.
+ *
+ * The benefit of using this over findList() is that it provides functionality to get the
+ * total row count etc.
+ *
+ *
+ * If maxRows is not set on the query prior to calling findPagedList() then a
+ * PersistenceException is thrown.
+ *
+ *
+ * {@code
+ *
+ * PagedList pagedList = Ebean.find(Order.class)
+ * .setFirstRow(50)
+ * .setMaxRows(20)
+ * .findPagedList();
+ *
+ * // fetch the total row count in the background
+ * pagedList.loadRowCount();
+ *
+ * List orders = pagedList.getList();
+ * int totalRowCount = pagedList.getTotalRowCount();
+ *
+ * }
+ *
+ * @return The PagedList
+ *
+ * @see Query#findPagedList()
+ */
+ PagedList findPagedList(Query query, Transaction transaction);
+
/**
* Execute the query returning a set of entity beans.
*
diff --git a/src/main/java/com/avaje/ebean/ExpressionList.java b/src/main/java/com/avaje/ebean/ExpressionList.java
index b91237c20..f5b6e002a 100644
--- a/src/main/java/com/avaje/ebean/ExpressionList.java
+++ b/src/main/java/com/avaje/ebean/ExpressionList.java
@@ -245,7 +245,7 @@ public interface ExpressionList extends Serializable {
FutureList findFutureList();
/**
- * Return a PagedList for this query.
+ * Return a PagedList for this query using pageIndex and pageSize.
*
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
@@ -265,6 +265,38 @@ public interface ExpressionList extends Serializable {
*/
PagedList findPagedList(int pageIndex, int pageSize);
+ /**
+ * Return a PagedList for this query using firstRow and maxRows.
+ *
+ * The benefit of using this over findList() is that it provides functionality to get the
+ * total row count etc.
+ *
+ *
+ * If maxRows is not set on the query prior to calling findPagedList() then a
+ * PersistenceException is thrown.
+ *
+ *
+ * {@code
+ *
+ * PagedList pagedList = Ebean.find(Order.class)
+ * .setFirstRow(50)
+ * .setMaxRows(20)
+ * .findPagedList();
+ *
+ * // fetch the total row count in the background
+ * pagedList.loadRowCount();
+ *
+ * List orders = pagedList.getList();
+ * int totalRowCount = pagedList.getTotalRowCount();
+ *
+ * }
+ *
+ * @return The PagedList
+ *
+ * @see Query#findPagedList()
+ */
+ PagedList findPagedList();
+
/**
* Return versions of a @History entity bean.
*
diff --git a/src/main/java/com/avaje/ebean/PagedList.java b/src/main/java/com/avaje/ebean/PagedList.java
index 52749948e..97fd2c04a 100644
--- a/src/main/java/com/avaje/ebean/PagedList.java
+++ b/src/main/java/com/avaje/ebean/PagedList.java
@@ -17,6 +17,33 @@ import java.util.concurrent.Future;
* limit the result set.
*
*
+ *
+ * Example: typical use including total row count
+ * {@code
+ *
+ * // We want to find the first 50 new orders
+ * // ... so we don't really need setFirstRow(0)
+ *
+ * PagedList pagedList
+ * = ebeanServer.find(Order.class)
+ * .where().eq("status", Order.Status.NEW)
+ * .order().asc("id")
+ * .setFirstRow(0)
+ * .setMaxRows(50)
+ * .findPagedList();
+ *
+ * // Optional: initiate the loading of the total
+ * // row count in a background thread
+ * pagedList.loadRowCount();
+ *
+ * // fetch and return the list in the foreground thread
+ * List orders = pagedList.getList();
+ *
+ * // get the total row count (from the future)
+ * int totalRowCount = pagedList.getTotalRowCount();
+ *
+ * }
+ *
* Example: typical use including total row count
* {@code
*
@@ -156,11 +183,15 @@ public interface PagedList {
/**
* Return the index position of this page. Zero based.
+ *
+ * Note that if firstRows/maxRows is used rather than pageIndex/pageSize then
+ * this always returns 0.
+ *
*/
int getPageIndex();
/**
- * Return the page size used for this query.
+ * Return the page size used for this query. This is the same value as maxRows used by the query.
*/
int getPageSize();
diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java
index d1978b07a..074ced3c9 100644
--- a/src/main/java/com/avaje/ebean/Query.java
+++ b/src/main/java/com/avaje/ebean/Query.java
@@ -822,7 +822,7 @@ public interface Query extends Serializable {
FutureList findFutureList();
/**
- * Return a PagedList for this query.
+ * Return a PagedList for this query using pageIndex and pageSize.
*
* The benefit of using this over just using the normal {@link Query#setFirstRow(int)} and
* {@link Query#setMaxRows(int)} is that it additionally wraps an optional call to
@@ -867,6 +867,36 @@ public interface Query extends Serializable {
*/
PagedList findPagedList(int pageIndex, int pageSize);
+ /**
+ * Return a PagedList for this query using firstRow and maxRows.
+ *
+ * The benefit of using this over findList() is that it provides functionality to get the
+ * total row count etc.
+ *
+ *
+ * If maxRows is not set on the query prior to calling findPagedList() then a
+ * PersistenceException is thrown.
+ *
+ *
+ * {@code
+ *
+ * PagedList pagedList = Ebean.find(Order.class)
+ * .setFirstRow(50)
+ * .setMaxRows(20)
+ * .findPagedList();
+ *
+ * // fetch the total row count in the background
+ * pagedList.loadRowCount();
+ *
+ * List orders = pagedList.getList();
+ * int totalRowCount = pagedList.getTotalRowCount();
+ *
+ * }
+ *
+ * @return The PagedList
+ */
+ PagedList findPagedList();
+
/**
* Set a named bind parameter. Named parameters have a colon to prefix the name.
*
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index 429665cc2..da07b5bf9 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -1384,6 +1384,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return new LimitOffsetPagedList(this, (SpiQuery)query, pageIndex, pageSize);
}
+ @Override
+ public PagedList findPagedList(Query query, Transaction transaction) {
+
+ SpiQuery spiQuery = (SpiQuery)query;
+ int maxRows = spiQuery.getMaxRows();
+ if (maxRows == 0) {
+ throw new PersistenceException("maxRows must be specified for findPagedList() query");
+ }
+
+ return new LimitOffsetPagedList(this, spiQuery);
+ }
+
public void findEach(Query query, QueryEachConsumer consumer, Transaction t) {
SpiOrmQueryRequest request = createQueryRequest(Type.ITERATE, query, t);
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
index 9b4e3bba2..85168ca5a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/JunctionExpression.java
@@ -314,6 +314,11 @@ abstract class JunctionExpression implements Junction, SpiExpression, Expr
return exprList.findPagedList(pageIndex, pageSize);
}
+ @Override
+ public PagedList findPagedList() {
+ return exprList.findPagedList();
+ }
+
@Override
public int findRowCount() {
return exprList.findRowCount();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java
index 46b34be19..3a61112e6 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/query/LimitOffsetPagedList.java
@@ -21,7 +21,9 @@ public class LimitOffsetPagedList implements PagedList {
private final SpiQuery query;
- private final int pageSize;
+ private final int firstRow;
+
+ private final int maxRows;
private final int pageIndex;
@@ -33,11 +35,29 @@ public class LimitOffsetPagedList implements PagedList {
private List list;
+ /**
+ * Construct with pageIndex/pageSize.
+ */
public LimitOffsetPagedList(EbeanServer server, SpiQuery query, int pageIndex, int pageSize) {
this.server = server;
this.query = query;
- this.pageSize = pageSize;
+ this.maxRows = pageSize;
+ this.firstRow = pageIndex * pageSize;
this.pageIndex = pageIndex;
+
+ query.setFirstRow(firstRow);
+ query.setMaxRows(pageSize);
+ }
+
+ /**
+ * Construct with firstRow/maxRows.
+ */
+ public LimitOffsetPagedList(EbeanServer server, SpiQuery query) {
+ this.server = server;
+ this.query = query;
+ this.maxRows = query.getMaxRows();
+ this.firstRow = query.getFirstRow();
+ this.pageIndex = 0;
}
public void loadRowCount() {
@@ -56,8 +76,6 @@ public class LimitOffsetPagedList implements PagedList {
public List getList() {
synchronized (monitor) {
if (list == null) {
- query.setFirstRow(pageIndex * pageSize);
- query.setMaxRows(pageSize);
list = server.findList(query, null);
}
return list;
@@ -70,7 +88,7 @@ public class LimitOffsetPagedList implements PagedList {
if (rowCount == 0) {
return 0;
} else {
- return ((rowCount - 1) / pageSize) + 1;
+ return ((rowCount - 1) / maxRows) + 1;
}
}
@@ -94,11 +112,11 @@ public class LimitOffsetPagedList implements PagedList {
}
public boolean hasNext() {
- return pageIndex < (getTotalPageCount() - 1);
+ return (firstRow + maxRows) < getTotalRowCount();
}
public boolean hasPrev() {
- return pageIndex > 0;
+ return firstRow > 0;
}
public int getPageIndex() {
@@ -106,13 +124,13 @@ public class LimitOffsetPagedList implements PagedList {
}
public int getPageSize() {
- return pageSize;
+ return maxRows;
}
public String getDisplayXtoYofZ(String to, String of) {
- int first = pageIndex * pageSize + 1;
- int last = first + getList().size() - 1;
+ int first = firstRow + 1;
+ int last = firstRow + getList().size();
int total = getTotalRowCount();
return first + to + last + of + total;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
index b3f5fdda2..ca801d039 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java
@@ -1094,6 +1094,11 @@ public class DefaultOrmQuery implements SpiQuery {
return server.findPagedList(this, null, pageIndex, pageSize);
}
+ @Override
+ public PagedList findPagedList() {
+ return server.findPagedList(this, null);
+ }
+
/**
* Set an ordered bind parameter according to its position. Note that the position starts at 1 to
* be consistent with JDBC PreparedStatement. You need to set a parameter value for each ? you
diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
index b2d84946d..f50c146de 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
@@ -201,6 +201,11 @@ public class DefaultExpressionList implements SpiExpressionList {
return query.findPagedList(pageIndex, pageSize);
}
+ @Override
+ public PagedList findPagedList() {
+ return query.findPagedList();
+ }
+
@Override
public int findRowCount() {
return query.findRowCount();
diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
index 172601c66..5ff481340 100644
--- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
+++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java
@@ -512,6 +512,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer {
return null;
}
+ @Override
+ public PagedList findPagedList(Query query, Transaction transaction) {
+ return null;
+ }
+
@Override
public Set findSet(Query query, Transaction transaction) {
return null;
diff --git a/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java b/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java
index 7c972fc26..5c3269db0 100644
--- a/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java
+++ b/src/test/java/com/avaje/tests/query/TestQueryFindPagedList.java
@@ -8,6 +8,7 @@ import com.avaje.tests.model.basic.ResetBasicData;
import org.avaje.ebeantest.LoggedSqlCollector;
import org.junit.Test;
+import javax.persistence.PersistenceException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -16,10 +17,126 @@ import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class TestQueryFindPagedList extends BaseTestCase {
+
+ @Test(expected = PersistenceException.class)
+ public void test_noMaxRows() throws ExecutionException, InterruptedException {
+
+ Ebean.find(Order.class).findPagedList();
+ }
+
+ @Test
+ public void test_maxRows_NoCount() throws ExecutionException, InterruptedException {
+
+ ResetBasicData.reset();
+
+ PagedList pagedList = Ebean.find(Order.class)
+ .setMaxRows(4)
+ .findPagedList();
+
+ LoggedSqlCollector.start();
+
+ List orders = pagedList.getList();
+
+ assertTrue(!orders.isEmpty());
+ List loggedSql = LoggedSqlCollector.stop();
+
+ assertEquals("Only 1 SQL statement, no count query",1, loggedSql.size());
+ }
+
+
+ @Test
+ public void test_maxRows_countInBackground() throws ExecutionException, InterruptedException, TimeoutException {
+
+ ResetBasicData.reset();
+
+ PagedList pagedList = Ebean.find(Order.class)
+ .setMaxRows(3)
+ .findPagedList();
+
+ Future rowCount = pagedList.getFutureRowCount();
+ List orders = pagedList.getList();
+
+ // these are each getting the total row count
+ int totalRowCount = pagedList.getTotalRowCount();
+ Integer totalRowCountWithTimeout = rowCount.get(30, TimeUnit.SECONDS);
+ Integer totalRowCountViaFuture = rowCount.get();
+
+ assertTrue(orders.size() < totalRowCount);
+ assertEquals(Integer.valueOf(totalRowCount), totalRowCountViaFuture);
+ assertEquals(Integer.valueOf(totalRowCount), totalRowCountWithTimeout);
+ }
+
+ @Test
+ public void test_maxRows_countInBackground_withLoadRowCount() throws InterruptedException {
+
+ ResetBasicData.reset();
+
+ // fetch less that total orders (page size 3)
+ PagedList pagedList = Ebean.find(Order.class)
+ .setMaxRows(3)
+ .findPagedList();
+
+ pagedList.loadRowCount();
+ List orders = pagedList.getList();
+ int totalRowCount = pagedList.getTotalRowCount();
+
+ assertThat(orders.size()).isLessThan(totalRowCount);
+ assertTrue(pagedList.hasNext());
+ assertFalse(pagedList.hasPrev());
+
+
+ // fetch less that total orders (page size 3)
+ PagedList pagedList2 = Ebean.find(Order.class)
+ .setFirstRow(1)
+ .setMaxRows(3)
+ .findPagedList();
+
+ pagedList2.loadRowCount();
+ List orders2 = pagedList2.getList();
+ int totalRowCount2 = pagedList2.getTotalRowCount();
+ assertTrue(pagedList2.hasNext());
+ assertTrue(pagedList2.hasPrev());
+
+ assertThat(totalRowCount).isEqualTo(totalRowCount2);
+ assertThat(orders2.size()).isLessThan(totalRowCount);
+
+
+ PagedList pagedList3 = Ebean.find(Order.class)
+ .setFirstRow(2)
+ .setMaxRows(150)
+ .findPagedList();
+
+ assertFalse(pagedList3.hasNext());
+ assertTrue(pagedList3.hasPrev());
+
+ List list3 = pagedList3.getList();
+ String xtoYofZ = pagedList3.getDisplayXtoYofZ(" to ", " of ");
+ assertThat(xtoYofZ).isEqualTo("3 to "+totalRowCount+" of "+totalRowCount);
+ assertThat(list3.size()).isEqualTo(totalRowCount - 2);
+
+ PagedList pagedList4 = Ebean.find(Order.class)
+ .setFirstRow(0)
+ .setMaxRows(totalRowCount)
+ .findPagedList();
+
+ assertFalse(pagedList4.hasNext());
+ assertFalse(pagedList4.hasPrev());
+ assertThat(pagedList4.getDisplayXtoYofZ(" to ", "of ")).isEqualTo("1 to "+totalRowCount+" of "+totalRowCount);
+
+ PagedList pagedList5 = Ebean.find(Order.class)
+ .setFirstRow(0)
+ .setMaxRows(totalRowCount - 1)
+ .findPagedList();
+
+ assertTrue(pagedList5.hasNext());
+ assertFalse(pagedList5.hasPrev());
+ }
+
@Test
public void test_noCount() throws ExecutionException, InterruptedException {