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   * Initiate the loading of the total row count in the background.
065   * <pre>{@code
066   *
067   *     // initiate the loading of the total row count
068   *     // in a background thread
069   *     pagedList.loadRowCount();
070   *
071   *     // fetch and return the list in the foreground thread
072   *     List<Order> orders = pagedList.getList();
073   *
074   *     // get the total row count (from the future)
075   *     int totalRowCount = pagedList.getTotalRowCount();
076   *
077   * }</pre>
078   * <p>
079   * Also note that using loadRowCount() and getTotalRowCount() rather than getFutureRowCount()
080   * means that exceptions ExecutionException, InterruptedException, TimeoutException are instead
081   * wrapped in the unchecked PersistenceException (which might be preferrable).
082   * </p>
083   */
084  void loadCount();
085
086  /**
087   * Return the Future row count. You might get this if you wish to cancel the total row count query
088   * or specify a timeout for the row count query.
089   * <p>
090   * The loadRowCount() & getTotalRowCount() methods internally make use of this getFutureRowCount() method.
091   * Generally I expect people to prefer loadRowCount() & getTotalRowCount() over getFutureRowCount().
092   * </p>
093   * <pre>{@code
094   *
095   *     // initiate the row count query in the background thread
096   *     Future<Integer> rowCount = pagedList.getFutureRowCount();
097   *
098   *     // fetch and return the list in the foreground thread
099   *     List<Order> orders = pagedList.getList();
100   *
101   *     // now get the total count with a timeout
102   *     Integer totalRowCount = rowCount.get(30, TimeUnit.SECONDS);
103   *
104   *     // or ge the total count without a timeout
105   *     Integer totalRowCountViaFuture = rowCount.get();
106   *
107   *     // which is actually the same as ...
108   *     int totalRowCount = pagedList.getTotalRowCount();
109   *
110   * }</pre>
111   */
112  @Nonnull
113  Future<Integer> getFutureCount();
114
115  /**
116   * Return the list of entities for this page.
117   */
118  @Nonnull
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 getTotalCount();
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 page size used for this query. This is the same value as maxRows used by the query.
159   */
160  int getPageSize();
161
162  /**
163   * Return the index position of this page (Zero based).
164   * <p>
165   * This is a calculated value based on firstRow/maxRows.
166   * </p>
167   */
168  int getPageIndex();
169
170  /**
171   * Return true if there is a next page.
172   * <p>
173   * This method requires that the total row count has been fetched and will invoke
174   * the total row count query if it has not already been invoked.
175   * </p>
176   */
177  boolean hasNext();
178
179  /**
180   * Return true if there is a previous page.
181   */
182  boolean hasPrev();
183
184  /**
185   * Helper method to return a "X to Y of Z" string for this page where X is the first row, Y the
186   * last row and Z the total row count.
187   * <p>
188   * This method requires that the total row count has been fetched and will invoke
189   * the total row count query if it has not already been invoked.
190   * </p>
191   *
192   * @param to String to put between the first and last row
193   * @param of String to put between the last row and the total row count
194   * @return String of the format XtoYofZ.
195   */
196  String getDisplayXtoYofZ(String to, String of);
197}