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