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