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