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 * <h4>Example: typical use including total row count</h4>
021 * <pre>{@code
022 *
023 *     // We want to find the first 100 new orders
024 *     //  ... 0 means first page
025 *     //  ... page size is 100
026 *
027 *     PagedList<Order> pagedList
028 *       = ebeanServer.find(Order.class)
029 *       .where().eq("status", Order.Status.NEW)
030 *       .order().asc("id")
031 *       .findPagedList(0, 100);
032 *
033 *     // Optional: initiate the loading of the total
034 *     // row count in a background thread
035 *     pagedList.loadRowCount();
036 *
037 *     // fetch and return the list in the foreground thread
038 *     List<Order> orders = pagedList.getList();
039 *
040 *     // get the total row count (from the future)
041 *     int totalRowCount = pagedList.getTotalRowCount();
042 *
043 * }</pre>
044 *
045 * <h4>Example: No total row count required</h4>
046 * <pre>{@code
047 *
048 *     // If you are not getting the 'first page' often
049 *     // you do not bother getting the total row count again
050 *     // so instead just get the page list of data
051 *
052 *     // fetch and return the list in the foreground thread
053 *     List<Order> orders = pagedList.getList();
054 *
055 * }</pre>
056 *
057 * @param <T>
058 *          the entity bean type
059 * 
060 * @see Query#findPagedList(int, int)
061 */
062public interface PagedList<T> {
063
064  /**
065   * Initiate the loading of the total row count in the background.
066   * <pre>{@code
067   *
068   *     // initiate the loading of the total row count
069   *     // in a background thread
070   *     pagedList.loadRowCount();
071   *
072   *     // fetch and return the list in the foreground thread
073   *     List<Order> orders = pagedList.getList();
074   *
075   *     // get the total row count (from the future)
076   *     int totalRowCount = pagedList.getTotalRowCount();
077   *
078   * }</pre>
079   *
080   * <p>
081   * Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
082   * means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
083   * wrapped in the unchecked PersistenceException (which might be preferrable).
084   * </p>
085   */
086  void loadRowCount();
087
088  /**
089   * Return the Future row count. You might get this if you wish to cancel the total row count query
090   * or specify a timeout for the row count query.
091   * <p>
092   * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
093   * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
094   * </p>
095   * <pre>{@code
096   *
097   *     // initiate the row count query in the background thread
098   *     Future<Integer> rowCount = pagedList.getFutureRowCount();
099   *
100   *     // fetch and return the list in the foreground thread
101   *     List<Order> orders = pagedList.getList();
102   *
103   *     // now get the total count with a timeout
104   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
105   *
106   *     // or ge the total count without a timeout
107   *     Integer totalRowCountViaFuture = rowCount.get();
108   *
109   *     // which is actually the same as ...
110   *     int totalRowCount = pagedList.getTotalRowCount();
111   *
112   * }</pre>
113   */
114  Future<Integer> getFutureRowCount();
115
116  /**
117   * Return the list of entities for this page.
118   */
119  List<T> getList();
120
121  /**
122   * Return the total row count for all pages.
123   * <p>
124   * If loadRowCount() has already been called then the row count query is already executing in a background thread
125   * and this gets the associated Future and gets the value waiting for the future to finish.
126   * </p>
127   * <p>
128   * If loadRowCount() has not been called then this executes the find row count query and returns the result and this
129   * will just occur in the current thread and not use a background thread.
130   * </p>
131   * <pre>{@code
132   *
133   *     // Optional: initiate the loading of the total
134   *     // row count in a background thread
135   *     pagedList.loadRowCount();
136   *
137   *     // fetch and return the list in the foreground thread
138   *     List<Order> orders = pagedList.getList();
139   *
140   *     // get the total row count (which was being executed
141   *     // in a background thread if loadRowCount() was used)
142   *     int totalRowCount = pagedList.getTotalRowCount();
143   *
144   * }</pre>
145   */
146  int getTotalRowCount();
147
148  /**
149   * Return the total number of pages based on the page size and total row count.
150   * <p>
151   * This method requires that the total row count has been fetched and will invoke
152   * the total row count query if it has not already been invoked.
153   * </p>
154   */
155  int getTotalPageCount();
156
157  /**
158   * Return the index position of this page. Zero based.
159   */
160  int getPageIndex();
161
162  /**
163   * Return the page size used for this query.
164   */
165  int getPageSize();
166
167  /**
168   * Return true if there is a next page.
169   * <p>
170   * This method requires that the total row count has been fetched and will invoke
171   * the total row count query if it has not already been invoked.
172   * </p>
173   */
174  boolean hasNext();
175
176  /**
177   * Return true if there is a previous page.
178   */
179  boolean hasPrev();
180
181  /**
182   * Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
183   * last row and Z the total row count.
184   * <p>
185   * This method requires that the total row count has been fetched and will invoke
186   * the total row count query if it has not already been invoked.
187   * </p>
188   *
189   * @param to
190   *          String to put between the first and last row
191   * @param of
192   *          String to put between the last row and the total row count
193   * 
194   * @return String of the format XtoYofZ.
195   */
196  String getDisplayXtoYofZ(String to, String of);
197}