001package io.ebean; 002 003import io.ebeaninternal.api.SpiQuery; 004import io.ebeaninternal.api.SpiTransaction; 005 006import javax.annotation.Nonnull; 007import javax.annotation.Nullable; 008import javax.persistence.NonUniqueResultException; 009import java.time.Clock; 010import java.util.List; 011import java.util.Map; 012import java.util.Optional; 013import java.util.Set; 014import java.util.function.Consumer; 015import java.util.function.Predicate; 016 017/** 018 * The extended API for EbeanServer. 019 * <p> 020 * This provides the finder methods that take an explicit transaction rather than obtaining 021 * the transaction from the usual mechanism (which is ThreadLocal based). 022 * </p> 023 * <p> 024 * In general we only want to use this ExtendedServer API when we want to avoid / bypass 025 * the use of the mechanism to get the current transaction and instead explicitly supply 026 * the transaction to use. 027 * </p> 028 * <p> 029 * Note that in all cases the transaction supplied can be null and in this case the EbeanServer 030 * will use the normal mechanism to obtain the transaction to use. 031 * </p> 032 */ 033public interface ExtendedServer { 034 035 /** 036 * Return the NOW time from the Clock. 037 */ 038 long clockNow(); 039 040 /** 041 * Set the Clock to use for <code>@WhenCreated</code> and <code>@WhenModified</code>. 042 * <p> 043 * Note that we only expect to change the Clock for testing purposes. 044 * </p> 045 */ 046 void setClock(Clock clock); 047 048 /** 049 * Execute the query returning true if a row is found. 050 * <p> 051 * The query is executed using max rows of 1 and will only select the id property. 052 * This method is really just a convenient way to optimise a query to perform a 053 * 'does a row exist in the db' check. 054 * </p> 055 * 056 * <h2>Example:</h2> 057 * <pre>{@code 058 * 059 * boolean userExists = query().where().eq("email", "rob@foo.com").exists(); 060 * 061 * }</pre> 062 * 063 * <h2>Example using a query bean:</h2> 064 * <pre>{@code 065 * 066 * boolean userExists = new QContact().email.equalTo("rob@foo.com").exists(); 067 * 068 * }</pre> 069 * 070 * @return True if the query finds a matching row in the database 071 */ 072 <T> boolean exists(SpiQuery<?> ormQuery, SpiTransaction transaction); 073 074 /** 075 * Return the number of 'top level' or 'root' entities this query should return. 076 * 077 * @see Query#findCount() 078 * @see Query#findFutureCount() 079 */ 080 <T> int findCount(Query<T> query, Transaction transaction); 081 082 /** 083 * Return the Id values of the query as a List. 084 * 085 * @see Query#findIds() 086 */ 087 @Nonnull 088 <A, T> List<A> findIds(Query<T> query, Transaction transaction); 089 090 /** 091 * Return a QueryIterator for the query. 092 * <p> 093 * Generally using {@link #findEach(Query, Consumer, Transaction)} or 094 * {@link #findEachWhile(Query, Predicate, Transaction)} is preferred 095 * to findIterate(). The reason is that those methods automatically take care of 096 * closing the queryIterator (and the underlying jdbc statement and resultSet). 097 * </p> 098 * <p> 099 * This is similar to findEach in that not all the result beans need to be held 100 * in memory at the same time and as such is good for processing large queries. 101 * </p> 102 * 103 * @see Query#findIterate() 104 * @see Query#findEach(Consumer) 105 * @see Query#findEachWhile(Predicate) 106 */ 107 @Nonnull 108 <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction); 109 110 /** 111 * Execute the query visiting the each bean one at a time. 112 * <p> 113 * Unlike findList() this is suitable for processing a query that will return 114 * a very large resultSet. The reason is that not all the result beans need to be 115 * held in memory at the same time and instead processed one at a time. 116 * </p> 117 * <p> 118 * Internally this query using a PersistenceContext scoped to each bean (and the 119 * beans associated object graph). 120 * </p> 121 * <p> 122 * <pre>{@code 123 * 124 * DB.find(Order.class) 125 * .where().eq("status", Order.Status.NEW) 126 * .order().asc("id") 127 * .findEach((Order order) -> { 128 * 129 * // do something with the order bean 130 * System.out.println(" -- processing order ... " + order); 131 * }); 132 * 133 * }</pre> 134 * 135 * @see Query#findEach(Consumer) 136 * @see Query#findEachWhile(Predicate) 137 */ 138 <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction); 139 140 /** 141 * Execute the query visiting the each bean one at a time. 142 * <p> 143 * Compared to findEach() this provides the ability to stop processing the query 144 * results early by returning false for the Predicate. 145 * </p> 146 * <p> 147 * Unlike findList() this is suitable for processing a query that will return 148 * a very large resultSet. The reason is that not all the result beans need to be 149 * held in memory at the same time and instead processed one at a time. 150 * </p> 151 * <p> 152 * Internally this query using a PersistenceContext scoped to each bean (and the 153 * beans associated object graph). 154 * </p> 155 * <p> 156 * <pre>{@code 157 * 158 * DB.find(Order.class) 159 * .where().eq("status", Order.Status.NEW) 160 * .order().asc("id") 161 * .findEachWhile((Order order) -> { 162 * 163 * // do something with the order bean 164 * System.out.println(" -- processing order ... " + order); 165 * 166 * boolean carryOnProcessing = ... 167 * return carryOnProcessing; 168 * }); 169 * 170 * }</pre> 171 * 172 * @see Query#findEach(Consumer) 173 * @see Query#findEachWhile(Predicate) 174 */ 175 <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction); 176 177 /** 178 * Return versions of a @History entity bean. 179 * <p> 180 * Generally this query is expected to be a find by id or unique predicates query. 181 * It will execute the query against the history returning the versions of the bean. 182 * </p> 183 */ 184 @Nonnull 185 <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction); 186 187 /** 188 * Execute a query returning a list of beans. 189 * <p> 190 * Generally you are able to use {@link Query#findList()} rather than 191 * explicitly calling this method. You could use this method if you wish to 192 * explicitly control the transaction used for the query. 193 * </p> 194 * <p> 195 * <pre>{@code 196 * 197 * List<Customer> customers = DB.find(Customer.class) 198 * .where().ilike("name", "rob%") 199 * .findList(); 200 * 201 * }</pre> 202 * 203 * @param <T> the type of entity bean to fetch. 204 * @param query the query to execute. 205 * @param transaction the transaction to use (can be null). 206 * @return the list of fetched beans. 207 * @see Query#findList() 208 */ 209 @Nonnull 210 <T> List<T> findList(Query<T> query, Transaction transaction); 211 212 /** 213 * Execute find row count query in a background thread. 214 * <p> 215 * This returns a Future object which can be used to cancel, check the 216 * execution status (isDone etc) and get the value (with or without a 217 * timeout). 218 * </p> 219 * 220 * @param query the query to execute the row count on 221 * @param transaction the transaction (can be null). 222 * @return a Future object for the row count query 223 * @see Query#findFutureCount() 224 */ 225 @Nonnull 226 <T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction); 227 228 /** 229 * Execute find Id's query in a background thread. 230 * <p> 231 * This returns a Future object which can be used to cancel, check the 232 * execution status (isDone etc) and get the value (with or without a 233 * timeout). 234 * </p> 235 * 236 * @param query the query to execute the fetch Id's on 237 * @param transaction the transaction (can be null). 238 * @return a Future object for the list of Id's 239 * @see Query#findFutureIds() 240 */ 241 @Nonnull 242 <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction); 243 244 /** 245 * Execute find list query in a background thread returning a FutureList object. 246 * <p> 247 * This returns a Future object which can be used to cancel, check the 248 * execution status (isDone etc) and get the value (with or without a timeout). 249 * <p> 250 * This query will execute in it's own PersistenceContext and using its own transaction. 251 * What that means is that it will not share any bean instances with other queries. 252 * 253 * @param query the query to execute in the background 254 * @param transaction the transaction (can be null). 255 * @return a Future object for the list result of the query 256 * @see Query#findFutureList() 257 */ 258 @Nonnull 259 <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction); 260 261 /** 262 * Return a PagedList for this query using firstRow and maxRows. 263 * <p> 264 * The benefit of using this over findList() is that it provides functionality to get the 265 * total row count etc. 266 * </p> 267 * <p> 268 * If maxRows is not set on the query prior to calling findPagedList() then a 269 * PersistenceException is thrown. 270 * </p> 271 * <p> 272 * <pre>{@code 273 * 274 * PagedList<Order> pagedList = DB.find(Order.class) 275 * .setFirstRow(50) 276 * .setMaxRows(20) 277 * .findPagedList(); 278 * 279 * // fetch the total row count in the background 280 * pagedList.loadRowCount(); 281 * 282 * List<Order> orders = pagedList.getList(); 283 * int totalRowCount = pagedList.getTotalRowCount(); 284 * 285 * }</pre> 286 * 287 * @return The PagedList 288 * @see Query#findPagedList() 289 */ 290 @Nonnull 291 <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction); 292 293 /** 294 * Execute the query returning a set of entity beans. 295 * <p> 296 * Generally you are able to use {@link Query#findSet()} rather than 297 * explicitly calling this method. You could use this method if you wish to 298 * explicitly control the transaction used for the query. 299 * </p> 300 * <p> 301 * <pre>{@code 302 * 303 * Set<Customer> customers = DB.find(Customer.class) 304 * .where().ilike("name", "rob%") 305 * .findSet(); 306 * 307 * }</pre> 308 * 309 * @param <T> the type of entity bean to fetch. 310 * @param query the query to execute 311 * @param transaction the transaction to use (can be null). 312 * @return the set of fetched beans. 313 * @see Query#findSet() 314 */ 315 @Nonnull 316 <T> Set<T> findSet(Query<T> query, Transaction transaction); 317 318 /** 319 * Execute the query returning the entity beans in a Map. 320 * <p> 321 * Generally you are able to use {@link Query#findMap()} rather than 322 * explicitly calling this method. You could use this method if you wish to 323 * explicitly control the transaction used for the query. 324 * </p> 325 * 326 * @param <T> the type of entity bean to fetch. 327 * @param query the query to execute. 328 * @param transaction the transaction to use (can be null). 329 * @return the map of fetched beans. 330 * @see Query#findMap() 331 */ 332 @Nonnull 333 <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction); 334 335 /** 336 * Execute the query returning a list of values for a single property. 337 * <p> 338 * <h3>Example 1:</h3> 339 * <pre>{@code 340 * 341 * List<String> names = 342 * DB.find(Customer.class) 343 * .select("name") 344 * .orderBy().asc("name") 345 * .findSingleAttributeList(); 346 * 347 * }</pre> 348 * <h3>Example 2:</h3> 349 * <pre>{@code 350 * 351 * List<String> names = 352 * DB.find(Customer.class) 353 * .setDistinct(true) 354 * .select("name") 355 * .where().eq("status", Customer.Status.NEW) 356 * .orderBy().asc("name") 357 * .setMaxRows(100) 358 * .findSingleAttributeList(); 359 * 360 * }</pre> 361 * 362 * @return the list of values for the selected property 363 * @see Query#findSingleAttributeList() 364 */ 365 @Nonnull 366 <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction); 367 368 /** 369 * Execute the query returning at most one entity bean or null (if no matching 370 * bean is found). 371 * <p> 372 * This will throw a NonUniqueResultException if the query finds more than one result. 373 * </p> 374 * <p> 375 * Generally you are able to use {@link Query#findOne()} rather than 376 * explicitly calling this method. You could use this method if you wish to 377 * explicitly control the transaction used for the query. 378 * </p> 379 * 380 * @param <T> the type of entity bean to fetch. 381 * @param query the query to execute. 382 * @param transaction the transaction to use (can be null). 383 * @return the list of fetched beans. 384 * @throws NonUniqueResultException if more than one result was found 385 * @see Query#findOne() 386 */ 387 @Nullable 388 <T> T findOne(Query<T> query, Transaction transaction); 389 390 /** 391 * Similar to findOne() but returns an Optional (rather than nullable). 392 */ 393 @Nonnull 394 <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction); 395 396 /** 397 * Execute as a delete query deleting the 'root level' beans that match the predicates 398 * in the query. 399 * <p> 400 * Note that if the query includes joins then the generated delete statement may not be 401 * optimal depending on the database platform. 402 * </p> 403 * 404 * @param query the query used for the delete 405 * @param transaction the transaction to use (can be null) 406 * @param <T> the type of entity bean to fetch. 407 * @return the number of beans/rows that were deleted 408 */ 409 <T> int delete(Query<T> query, Transaction transaction); 410 411 /** 412 * Execute the update query returning the number of rows updated. 413 * <p> 414 * The update query must be created using {@link EbeanServer#update(Class)}. 415 * </p> 416 * 417 * @param query the update query to execute 418 * @param transaction the optional transaction to use for the update (can be null) 419 * @param <T> the type of entity bean 420 * @return The number of rows updated 421 */ 422 <T> int update(Query<T> query, Transaction transaction); 423 424 /** 425 * Execute the sql query returning a list of MapBean. 426 * <p> 427 * Generally you are able to use {@link SqlQuery#findList()} rather than 428 * explicitly calling this method. You could use this method if you wish to 429 * explicitly control the transaction used for the query. 430 * </p> 431 * 432 * @param query the query to execute. 433 * @param transaction the transaction to use (can be null). 434 * @return the list of fetched MapBean. 435 * @see SqlQuery#findList() 436 */ 437 @Nonnull 438 List<SqlRow> findList(SqlQuery query, Transaction transaction); 439 440 /** 441 * Execute the SqlQuery iterating a row at a time. 442 * <p> 443 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 444 * </p> 445 */ 446 void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction); 447 448 /** 449 * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through. 450 * <p> 451 * Returning false after processing a row stops the iteration through the query results. 452 * </p> 453 * <p> 454 * This streaming type query is useful for large query execution as only 1 row needs to be held in memory. 455 * </p> 456 */ 457 void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction); 458 459 /** 460 * Execute the sql query returning a single MapBean or null. 461 * <p> 462 * This will throw a PersistenceException if the query found more than one 463 * result. 464 * </p> 465 * <p> 466 * Generally you are able to use {@link SqlQuery#findOne()} rather than 467 * explicitly calling this method. You could use this method if you wish to 468 * explicitly control the transaction used for the query. 469 * </p> 470 * 471 * @param query the query to execute. 472 * @param transaction the transaction to use (can be null). 473 * @return the fetched MapBean or null if none was found. 474 * @see SqlQuery#findOne() 475 */ 476 @Nullable 477 SqlRow findOne(SqlQuery query, Transaction transaction); 478 479}