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(int, int)
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 loadRowCount();
114
115  /**
116   * Return the Future row count. You might get this if you wish to cancel the total row count query
117   * or specify a timeout for the row count query.
118   * <p>
119   * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
120   * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
121   * </p>
122   * <pre>{@code
123   *
124   *     // initiate the row count query in the background thread
125   *     Future<Integer> rowCount = pagedList.getFutureRowCount();
126   *
127   *     // fetch and return the list in the foreground thread
128   *     List<Order> orders = pagedList.getList();
129   *
130   *     // now get the total count with a timeout
131   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
132   *
133   *     // or ge the total count without a timeout
134   *     Integer totalRowCountViaFuture = rowCount.get();
135   *
136   *     // which is actually the same as ...
137   *     int totalRowCount = pagedList.getTotalRowCount();
138   *
139   * }</pre>
140   */
141  Future<Integer> getFutureRowCount();
142
143  /**
144   * Return the list of entities for this page.
145   */
146  List<T> getList();
147
148  /**
149   * Return the total row count for all pages.
150   * <p>
151   * If loadRowCount() has already been called then the row count query is already executing in a background thread
152   * and this gets the associated Future and gets the value waiting for the future to finish.
153   * </p>
154   * <p>
155   * If loadRowCount() has not been called then this executes the find row count query and returns the result and this
156   * will just occur in the current thread and not use a background thread.
157   * </p>
158   * <pre>{@code
159   *
160   *     // Optional: initiate the loading of the total
161   *     // row count in a background thread
162   *     pagedList.loadRowCount();
163   *
164   *     // fetch and return the list in the foreground thread
165   *     List<Order> orders = pagedList.getList();
166   *
167   *     // get the total row count (which was being executed
168   *     // in a background thread if loadRowCount() was used)
169   *     int totalRowCount = pagedList.getTotalRowCount();
170   *
171   * }</pre>
172   */
173  int getTotalRowCount();
174
175  /**
176   * Return the total number of pages based on the page size and total row count.
177   * <p>
178   * This method requires that the total row count has been fetched and will invoke
179   * the total row count query if it has not already been invoked.
180   * </p>
181   */
182  int getTotalPageCount();
183
184  /**
185   * Return the index position of this page. Zero based.
186   * <p>
187   * Note that if firstRows/maxRows is used rather than pageIndex/pageSize then
188   * this always returns 0.
189   * </p>
190   */
191  int getPageIndex();
192
193  /**
194   * Return the page size used for this query. This is the same value as maxRows used by the query.
195   */
196  int getPageSize();
197
198  /**
199   * Return true if there is a next page.
200   * <p>
201   * This method requires that the total row count has been fetched and will invoke
202   * the total row count query if it has not already been invoked.
203   * </p>
204   */
205  boolean hasNext();
206
207  /**
208   * Return true if there is a previous page.
209   */
210  boolean hasPrev();
211
212  /**
213   * Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
214   * last row and Z the total row count.
215   * <p>
216   * This method requires that the total row count has been fetched and will invoke
217   * the total row count query if it has not already been invoked.
218   * </p>
219   *
220   * @param to
221   *          String to put between the first and last row
222   * @param of
223   *          String to put between the last row and the total row count
224   * 
225   * @return String of the format XtoYofZ.
226   */
227  String getDisplayXtoYofZ(String to, String of);
228}