001package com.avaje.ebean;
002
003import java.util.List;
004import java.util.concurrent.Future;
005
006/**
007 * Represents a page of results.
008 * <p>
009 * The benefit of using PagedList over just using the normal Query with
010 * {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} is that it additionally wraps
011 * functionality that can call {@link Query#findFutureRowCount()} to determine total row count,
012 * total page count etc.
013 * </p>
014 * <p>
015 * Internally this works using {@link Query#setFirstRow(int)} and {@link Query#setMaxRows(int)} on
016 * the query. This translates into SQL that uses limit offset, rownum or row_number function to
017 * limit the result set.
018 * </p>
019 *
020 *
021 * <h4>Example: typical use including total row count</h4>
022 * <pre>{@code
023 *
024 *     // We want to find the first 50 new orders
025 *     //  ... so we don't really need setFirstRow(0)
026 *
027 *     PagedList<Order> pagedList
028 *       = ebeanServer.find(Order.class)
029 *       .where().eq("status", Order.Status.NEW)
030 *       .order().asc("id")
031 *       .setFirstRow(0)
032 *       .setMaxRows(50)
033 *       .findPagedList();
034 *
035 *     // Optional: initiate the loading of the total
036 *     // row count in a background thread
037 *     pagedList.loadRowCount();
038 *
039 *     // fetch and return the list in the foreground thread
040 *     List<Order> orders = pagedList.getList();
041 *
042 *     // get the total row count (from the future)
043 *     int totalRowCount = pagedList.getTotalRowCount();
044 *
045 * }</pre>
046 *
047 * <h4>Example: typical use including total row count</h4>
048 * <pre>{@code
049 *
050 *     // We want to find the first 100 new orders
051 *     //  ... 0 means first page
052 *     //  ... page size is 100
053 *
054 *     PagedList<Order> pagedList
055 *       = ebeanServer.find(Order.class)
056 *       .where().eq("status", Order.Status.NEW)
057 *       .order().asc("id")
058 *       .findPagedList(0, 100);
059 *
060 *     // Optional: initiate the loading of the total
061 *     // row count in a background thread
062 *     pagedList.loadRowCount();
063 *
064 *     // fetch and return the list in the foreground thread
065 *     List<Order> orders = pagedList.getList();
066 *
067 *     // get the total row count (from the future)
068 *     int totalRowCount = pagedList.getTotalRowCount();
069 *
070 * }</pre>
071 *
072 * <h4>Example: No total row count required</h4>
073 * <pre>{@code
074 *
075 *     // If you are not getting the 'first page' often
076 *     // you do not bother getting the total row count again
077 *     // so instead just get the page list of data
078 *
079 *     // fetch and return the list in the foreground thread
080 *     List<Order> orders = pagedList.getList();
081 *
082 * }</pre>
083 *
084 * @param <T>
085 *          the entity bean type
086 * 
087 * @see Query#findPagedList()
088 */
089public interface PagedList<T> {
090
091  /**
092   * Initiate the loading of the total row count in the background.
093   * <pre>{@code
094   *
095   *     // initiate the loading of the total row count
096   *     // in a background thread
097   *     pagedList.loadRowCount();
098   *
099   *     // fetch and return the list in the foreground thread
100   *     List<Order> orders = pagedList.getList();
101   *
102   *     // get the total row count (from the future)
103   *     int totalRowCount = pagedList.getTotalRowCount();
104   *
105   * }</pre>
106   *
107   * <p>
108   * Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
109   * means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
110   * wrapped in the unchecked PersistenceException (which might be preferrable).
111   * </p>
112   */
113  void loadCount();
114
115  /**
116   * Deprecated in favor of loadCount().
117   *
118   * @deprecated
119   */
120  void loadRowCount();
121
122  /**
123   * Return the Future row count. You might get this if you wish to cancel the total row count query
124   * or specify a timeout for the row count query.
125   * <p>
126   * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
127   * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
128   * </p>
129   * <pre>{@code
130   *
131   *     // initiate the row count query in the background thread
132   *     Future<Integer> rowCount = pagedList.getFutureRowCount();
133   *
134   *     // fetch and return the list in the foreground thread
135   *     List<Order> orders = pagedList.getList();
136   *
137   *     // now get the total count with a timeout
138   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
139   *
140   *     // or ge the total count without a timeout
141   *     Integer totalRowCountViaFuture = rowCount.get();
142   *
143   *     // which is actually the same as ...
144   *     int totalRowCount = pagedList.getTotalRowCount();
145   *
146   * }</pre>
147   */
148  Future<Integer> getFutureCount();
149
150  /**
151   * Deprecated in favor of getFutureCount().
152   *
153   * @deprecated
154   */
155  Future<Integer> getFutureRowCount();
156
157  /**
158   * Return the list of entities for this page.
159   */
160  List<T> getList();
161
162  /**
163   * Return the total row count for all pages.
164   * <p>
165   * If loadRowCount() has already been called then the row count query is already executing in a background thread
166   * and this gets the associated Future and gets the value waiting for the future to finish.
167   * </p>
168   * <p>
169   * If loadRowCount() has not been called then this executes the find row count query and returns the result and this
170   * will just occur in the current thread and not use a background thread.
171   * </p>
172   * <pre>{@code
173   *
174   *     // Optional: initiate the loading of the total
175   *     // row count in a background thread
176   *     pagedList.loadRowCount();
177   *
178   *     // fetch and return the list in the foreground thread
179   *     List<Order> orders = pagedList.getList();
180   *
181   *     // get the total row count (which was being executed
182   *     // in a background thread if loadRowCount() was used)
183   *     int totalRowCount = pagedList.getTotalRowCount();
184   *
185   * }</pre>
186   */
187  int getTotalCount();
188
189  /**
190   * Deprecated in favor of getTotalCount().
191   *
192   * @deprecated
193   */
194  int getTotalRowCount();
195
196  /**
197   * Return the total number of pages based on the page size and total row count.
198   * <p>
199   * This method requires that the total row count has been fetched and will invoke
200   * the total row count query if it has not already been invoked.
201   * </p>
202   */
203  int getTotalPageCount();
204
205  /**
206   * Return the page size used for this query. This is the same value as maxRows used by the query.
207   */
208  int getPageSize();
209
210  /**
211   * Return true if there is a next page.
212   * <p>
213   * This method requires that the total row count has been fetched and will invoke
214   * the total row count query if it has not already been invoked.
215   * </p>
216   */
217  boolean hasNext();
218
219  /**
220   * Return true if there is a previous page.
221   */
222  boolean hasPrev();
223
224  /**
225   * Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
226   * last row and Z the total row count.
227   * <p>
228   * This method requires that the total row count has been fetched and will invoke
229   * the total row count query if it has not already been invoked.
230   * </p>
231   *
232   * @param to
233   *          String to put between the first and last row
234   * @param of
235   *          String to put between the last row and the total row count
236   * 
237   * @return String of the format XtoYofZ.
238   */
239  String getDisplayXtoYofZ(String to, String of);
240}