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}