mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 23:56:36 +00:00
#219 - Update PagedList javadoc - enable foreground query for getTotalRowCount() when loadRowCount() has not been used
This commit is contained in:
@@ -89,6 +89,13 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>avaje-agentloader</artifactId>
|
||||
@@ -241,8 +248,27 @@
|
||||
<doctitle>Ebean 4</doctitle>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
<!-- <excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames> -->
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
<!--<additionalparam>-Xdoclint:none</additionalparam>-->
|
||||
<source>1.8</source>
|
||||
<doclet>org.avaje.doclet.PygmentsDoclet</doclet>
|
||||
<excludePackageNames>com.avaje.ebeaninternal.*:com.avaje.ebean.util</excludePackageNames>
|
||||
<docletArtifact>
|
||||
<groupId>org.avaje</groupId>
|
||||
<artifactId>pygments-doclet</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</docletArtifact>
|
||||
<additionalparam>
|
||||
-Xdoclint:none
|
||||
-include-basedir ${project.basedir}
|
||||
-attributes "idseparator=-; project_name=${project.name}; \
|
||||
project_version=${project.version}; \
|
||||
project_desc=${project.description}"
|
||||
</additionalparam>
|
||||
<linksource>true</linksource>
|
||||
<overview>src/main/java/com/avaje/ebean/overview.html</overview>
|
||||
|
||||
</configuration>
|
||||
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
|
||||
@@ -16,7 +16,44 @@ import java.util.concurrent.Future;
|
||||
* the query. This translates into SQL that uses limit offset, rownum or row_number function to
|
||||
* limit the result set.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <h4>Example: typical use including total row count</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // We want to find the first 100 new orders
|
||||
* // ... 0 means first page
|
||||
* // ... page size is 100
|
||||
*
|
||||
* PagedList<Order> pagedList
|
||||
* = ebeanServer.find(Order.class)
|
||||
* .where().eq("status", Order.Status.NEW)
|
||||
* .order().asc("id")
|
||||
* .findPagedList(0, 100);
|
||||
*
|
||||
* // 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<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h4>Example: No total row count required</h4>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // If you are not getting the 'first page' often
|
||||
* // you do not bother getting the total row count again
|
||||
* // so instead just get the page list of data
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T>
|
||||
* the entity bean type
|
||||
*
|
||||
@@ -26,12 +63,53 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Initiate the loading of the total row count in the background.
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the loading of the total row count
|
||||
* // in a background thread
|
||||
* pagedList.loadRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (from the future)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
|
||||
* means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
|
||||
* wrapped in the unchecked PersistenceException (which might be preferrable).
|
||||
* </p>
|
||||
*/
|
||||
public void loadRowCount();
|
||||
|
||||
/**
|
||||
* Return the Future row count. You might get this if you wish to cancel the total row count query
|
||||
* or specify a timeout for that query.
|
||||
* or specify a timeout for the row count query.
|
||||
* <p>
|
||||
* The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
|
||||
* Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // initiate the row count query in the background thread
|
||||
* Future<Integer> rowCount = pagedList.getFutureRowCount();
|
||||
*
|
||||
* // fetch and return the list in the foreground thread
|
||||
* List<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // now get the total count with a timeout
|
||||
* Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
|
||||
*
|
||||
* // or ge the total count without a timeout
|
||||
* Integer totalRowCountViaFuture = rowCount.get();
|
||||
*
|
||||
* // which is actually the same as ...
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public Future<Integer> getFutureRowCount();
|
||||
|
||||
@@ -42,11 +120,37 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Return the total row count for all pages.
|
||||
* <p>
|
||||
* If loadRowCount() has already been called then the row count query is already executing in a background thread
|
||||
* and this gets the associated Future and gets the value waiting for the future to finish.
|
||||
* </p>
|
||||
* <p>
|
||||
* If loadRowCount() has not been called then this executes the find row count query and returns the result and this
|
||||
* will just occur in the current thread and not use a background thread.
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
*
|
||||
* // 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<Order> orders = pagedList.getList();
|
||||
*
|
||||
* // get the total row count (which was being executed
|
||||
* // in a background thread if loadRowCount() was used)
|
||||
* int totalRowCount = pagedList.getTotalRowCount();
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public int getTotalRowCount();
|
||||
|
||||
/**
|
||||
* Return the total number of pages based on the page size and total row count.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public int getTotalPageCount();
|
||||
|
||||
@@ -57,6 +161,10 @@ public interface PagedList<T> {
|
||||
|
||||
/**
|
||||
* Return true if there is a next page.
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
@@ -68,7 +176,11 @@ public interface PagedList<T> {
|
||||
/**
|
||||
* Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
|
||||
* last row and Z the total row count.
|
||||
*
|
||||
* <p>
|
||||
* This method requires that the total row count has been fetched and will invoke
|
||||
* the total row count query if it has not already been invoked.
|
||||
* </p>
|
||||
*
|
||||
* @param to
|
||||
* String to put between the first and last row
|
||||
* @param of
|
||||
|
||||
@@ -28,6 +28,8 @@ public class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
|
||||
private final Monitor monitor = new Monitor();
|
||||
|
||||
private int foregroundTotalRowCount = -1;
|
||||
|
||||
private Future<Integer> futureRowCount;
|
||||
|
||||
private List<T> list;
|
||||
@@ -51,13 +53,12 @@ public class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
return futureRowCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<T> getList() {
|
||||
synchronized (monitor) {
|
||||
if (list == null) {
|
||||
query.setFirstRow(pageIndex * pageSize);
|
||||
query.setMaxRows(pageSize);
|
||||
|
||||
list = server.findList(query, null);
|
||||
}
|
||||
return list;
|
||||
@@ -75,10 +76,21 @@ public class LimitOffsetPagedList<T> implements PagedList<T> {
|
||||
}
|
||||
|
||||
public int getTotalRowCount() {
|
||||
try {
|
||||
return getFutureRowCount().get();
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
synchronized (monitor) {
|
||||
if (futureRowCount != null) {
|
||||
try {
|
||||
// background query already initiated so get it with a wait
|
||||
return futureRowCount.get();
|
||||
} catch (Exception e) {
|
||||
throw new PersistenceException(e);
|
||||
}
|
||||
}
|
||||
// already fetched?
|
||||
if (foregroundTotalRowCount > -1) return foregroundTotalRowCount;
|
||||
|
||||
// just using foreground thread
|
||||
foregroundTotalRowCount = server.findRowCount(query, null);
|
||||
return foregroundTotalRowCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.avaje.ebean.elasticsearch;
|
||||
|
||||
/**
|
||||
* Created by rob on 2/12/14.
|
||||
*/
|
||||
public class TestBasicPush {
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.avaje.tests.query;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.PagedList;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestQueryFindPagedList extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test_noCount() throws ExecutionException, InterruptedException {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
PagedList<Order> pagedList = Ebean.find(Order.class).findPagedList(0, 3);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
List<Order> orders = pagedList.getList();
|
||||
|
||||
assertTrue(!orders.isEmpty());
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
assertEquals("Only 1 SQL statement, no count query",1, loggedSql.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_countInBackground() throws ExecutionException, InterruptedException, TimeoutException {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
PagedList<Order> pagedList = Ebean.find(Order.class).findPagedList(0, 3);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Future<Integer> rowCount = pagedList.getFutureRowCount();
|
||||
List<Order> 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();
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
assertTrue(orders.size() < totalRowCount);
|
||||
assertEquals(Integer.valueOf(totalRowCount), totalRowCountViaFuture);
|
||||
assertEquals(Integer.valueOf(totalRowCount), totalRowCountWithTimeout);
|
||||
assertEquals(2, loggedSql.size());
|
||||
|
||||
String firstTxn = loggedSql.get(0).substring(0, 10);
|
||||
String secTxn = loggedSql.get(1).substring(0, 10);
|
||||
|
||||
assertNotEquals(firstTxn, secTxn);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_countInBackground_withLoadRowCount() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
PagedList<Order> pagedList = Ebean.find(Order.class).findPagedList(0, 3);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
pagedList.loadRowCount();
|
||||
List<Order> orders = pagedList.getList();
|
||||
int totalRowCount = pagedList.getTotalRowCount();
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
assertTrue(orders.size() < totalRowCount);
|
||||
assertEquals(2, loggedSql.size());
|
||||
|
||||
String firstTxn = loggedSql.get(0).substring(0, 10);
|
||||
String secTxn = loggedSql.get(1).substring(0, 10);
|
||||
|
||||
assertNotEquals(firstTxn, secTxn);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void test_countUsingForegound() throws ExecutionException, InterruptedException {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
PagedList<Order> pagedList = Ebean.find(Order.class).findPagedList(0, 3);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
// kinda not normal but just wrap in a transaction to assert
|
||||
// the background fetch does not occur (which explicitly creates
|
||||
// its own transaction) ... so a bit naughty with the test here
|
||||
Ebean.beginTransaction();
|
||||
try {
|
||||
|
||||
List<Order> orders = pagedList.getList();
|
||||
int totalRowCount = pagedList.getTotalRowCount();
|
||||
|
||||
// invoke it again but cached...
|
||||
int totalRowCountAgain = pagedList.getTotalRowCount();
|
||||
|
||||
List<String> loggedSql = LoggedSqlCollector.stop();
|
||||
|
||||
assertTrue(orders.size() < totalRowCount);
|
||||
assertEquals(2, loggedSql.size());
|
||||
assertEquals(totalRowCount, totalRowCountAgain);
|
||||
|
||||
String firstTxn = loggedSql.get(0).substring(0, 10);
|
||||
String secTxn = loggedSql.get(1).substring(0, 10);
|
||||
|
||||
assertEquals(firstTxn, secTxn);
|
||||
|
||||
} finally {
|
||||
Ebean.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user