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", "rob@foo.com").exists();
058   *
059   * }</pre>
060   *
061   * <h2>Example using a query bean:</h2>
062   * <pre>{@code
063   *
064   *   boolean userExists = new QContact().email.equalTo("rob@foo.com").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 findEach with batch consumer.
164   *
165   * @see Query#findEach(int, Consumer)
166   */
167  <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t);
168
169  /**
170   * Execute the query visiting the each bean one at a time.
171   * <p>
172   * Compared to findEach() this provides the ability to stop processing the query
173   * results early by returning false for the Predicate.
174   * </p>
175   * <p>
176   * Unlike findList() this is suitable for processing a query that will return
177   * a very large resultSet. The reason is that not all the result beans need to be
178   * held in memory at the same time and instead processed one at a time.
179   * </p>
180   * <p>
181   * Internally this query using a PersistenceContext scoped to each bean (and the
182   * beans associated object graph).
183   * </p>
184   * <p>
185   * <pre>{@code
186   *
187   *     DB.find(Order.class)
188   *       .where().eq("status", Order.Status.NEW)
189   *       .order().asc("id")
190   *       .findEachWhile((Order order) -> {
191   *
192   *         // do something with the order bean
193   *         System.out.println(" -- processing order ... " + order);
194   *
195   *         boolean carryOnProcessing = ...
196   *         return carryOnProcessing;
197   *       });
198   *
199   * }</pre>
200   *
201   * @see Query#findEach(Consumer)
202   * @see Query#findEachWhile(Predicate)
203   */
204  <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
205
206  /**
207   * Return versions of a @History entity bean.
208   * <p>
209   * Generally this query is expected to be a find by id or unique predicates query.
210   * It will execute the query against the history returning the versions of the bean.
211   * </p>
212   */
213  @Nonnull
214  <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
215
216  /**
217   * Execute a query returning a list of beans.
218   * <p>
219   * Generally you are able to use {@link Query#findList()} rather than
220   * explicitly calling this method. You could use this method if you wish to
221   * explicitly control the transaction used for the query.
222   * </p>
223   * <p>
224   * <pre>{@code
225   *
226   * List<Customer> customers = DB.find(Customer.class)
227   *     .where().ilike("name", "rob%")
228   *     .findList();
229   *
230   * }</pre>
231   *
232   * @param <T>         the type of entity bean to fetch.
233   * @param query       the query to execute.
234   * @param transaction the transaction to use (can be null).
235   * @return the list of fetched beans.
236   * @see Query#findList()
237   */
238  @Nonnull
239  <T> List<T> findList(Query<T> query, Transaction transaction);
240
241  /**
242   * Execute find row count query in a background thread.
243   * <p>
244   * This returns a Future object which can be used to cancel, check the
245   * execution status (isDone etc) and get the value (with or without a
246   * timeout).
247   * </p>
248   *
249   * @param query       the query to execute the row count on
250   * @param transaction the transaction (can be null).
251   * @return a Future object for the row count query
252   * @see Query#findFutureCount()
253   */
254  @Nonnull
255  <T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);
256
257  /**
258   * Execute find Id's query in a background thread.
259   * <p>
260   * This returns a Future object which can be used to cancel, check the
261   * execution status (isDone etc) and get the value (with or without a
262   * timeout).
263   * </p>
264   *
265   * @param query       the query to execute the fetch Id's on
266   * @param transaction the transaction (can be null).
267   * @return a Future object for the list of Id's
268   * @see Query#findFutureIds()
269   */
270  @Nonnull
271  <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
272
273  /**
274   * Execute find list query in a background thread returning a FutureList object.
275   * <p>
276   * This returns a Future object which can be used to cancel, check the
277   * execution status (isDone etc) and get the value (with or without a timeout).
278   * <p>
279   * This query will execute in it's own PersistenceContext and using its own transaction.
280   * What that means is that it will not share any bean instances with other queries.
281   *
282   * @param query       the query to execute in the background
283   * @param transaction the transaction (can be null).
284   * @return a Future object for the list result of the query
285   * @see Query#findFutureList()
286   */
287  @Nonnull
288  <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
289
290  /**
291   * Return a PagedList for this query using firstRow and maxRows.
292   * <p>
293   * The benefit of using this over findList() is that it provides functionality to get the
294   * total row count etc.
295   * </p>
296   * <p>
297   * If maxRows is not set on the query prior to calling findPagedList() then a
298   * PersistenceException is thrown.
299   * </p>
300   * <p>
301   * <pre>{@code
302   *
303   *  PagedList<Order> pagedList = DB.find(Order.class)
304   *       .setFirstRow(50)
305   *       .setMaxRows(20)
306   *       .findPagedList();
307   *
308   *       // fetch the total row count in the background
309   *       pagedList.loadRowCount();
310   *
311   *       List<Order> orders = pagedList.getList();
312   *       int totalRowCount = pagedList.getTotalRowCount();
313   *
314   * }</pre>
315   *
316   * @return The PagedList
317   * @see Query#findPagedList()
318   */
319  @Nonnull
320  <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
321
322  /**
323   * Execute the query returning a set of entity beans.
324   * <p>
325   * Generally you are able to use {@link Query#findSet()} rather than
326   * explicitly calling this method. You could use this method if you wish to
327   * explicitly control the transaction used for the query.
328   * </p>
329   * <p>
330   * <pre>{@code
331   *
332   * Set<Customer> customers = DB.find(Customer.class)
333   *     .where().ilike("name", "rob%")
334   *     .findSet();
335   *
336   * }</pre>
337   *
338   * @param <T>         the type of entity bean to fetch.
339   * @param query       the query to execute
340   * @param transaction the transaction to use (can be null).
341   * @return the set of fetched beans.
342   * @see Query#findSet()
343   */
344  @Nonnull
345  <T> Set<T> findSet(Query<T> query, Transaction transaction);
346
347  /**
348   * Execute the query returning the entity beans in a Map.
349   * <p>
350   * Generally you are able to use {@link Query#findMap()} rather than
351   * explicitly calling this method. You could use this method if you wish to
352   * explicitly control the transaction used for the query.
353   * </p>
354   *
355   * @param <T>         the type of entity bean to fetch.
356   * @param query       the query to execute.
357   * @param transaction the transaction to use (can be null).
358   * @return the map of fetched beans.
359   * @see Query#findMap()
360   */
361  @Nonnull
362  <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
363
364  /**
365   * Execute the query returning a list of values for a single property.
366   * <p>
367   * <h3>Example 1:</h3>
368   * <pre>{@code
369   *
370   *  List<String> names =
371   *    DB.find(Customer.class)
372   *      .select("name")
373   *      .order().asc("name")
374   *      .findSingleAttributeList();
375   *
376   * }</pre>
377   * <h3>Example 2:</h3>
378   * <pre>{@code
379   *
380   *  List<String> names =
381   *    DB.find(Customer.class)
382   *      .setDistinct(true)
383   *      .select("name")
384   *      .where().eq("status", Customer.Status.NEW)
385   *      .order().asc("name")
386   *      .setMaxRows(100)
387   *      .findSingleAttributeList();
388   *
389   * }</pre>
390   *
391   * @return the list of values for the selected property
392   * @see Query#findSingleAttributeList()
393   */
394  @Nonnull
395  <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
396
397  /**
398   * Execute the query returning at most one entity bean or null (if no matching
399   * bean is found).
400   * <p>
401   * This will throw a NonUniqueResultException if the query finds more than one result.
402   * </p>
403   * <p>
404   * Generally you are able to use {@link Query#findOne()} rather than
405   * explicitly calling this method. You could use this method if you wish to
406   * explicitly control the transaction used for the query.
407   * </p>
408   *
409   * @param <T>         the type of entity bean to fetch.
410   * @param query       the query to execute.
411   * @param transaction the transaction to use (can be null).
412   * @return the list of fetched beans.
413   * @throws NonUniqueResultException if more than one result was found
414   * @see Query#findOne()
415   */
416  @Nullable
417  <T> T findOne(Query<T> query, Transaction transaction);
418
419  /**
420   * Similar to findOne() but returns an Optional (rather than nullable).
421   */
422  @Nonnull
423  <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
424
425  /**
426   * Execute as a delete query deleting the 'root level' beans that match the predicates
427   * in the query.
428   * <p>
429   * Note that if the query includes joins then the generated delete statement may not be
430   * optimal depending on the database platform.
431   * </p>
432   *
433   * @param query       the query used for the delete
434   * @param transaction the transaction to use (can be null)
435   * @param <T>         the type of entity bean to fetch.
436   * @return the number of beans/rows that were deleted
437   */
438  <T> int delete(Query<T> query, Transaction transaction);
439
440  /**
441   * Execute the update query returning the number of rows updated.
442   * <p>
443   * The update query must be created using {@link Database#update(Class)}.
444   * </p>
445   *
446   * @param query       the update query to execute
447   * @param transaction the optional transaction to use for the update (can be null)
448   * @param <T>         the type of entity bean
449   * @return The number of rows updated
450   */
451  <T> int update(Query<T> query, Transaction transaction);
452
453  /**
454   * Execute the sql query returning a list of MapBean.
455   * <p>
456   * Generally you are able to use {@link SqlQuery#findList()} rather than
457   * explicitly calling this method. You could use this method if you wish to
458   * explicitly control the transaction used for the query.
459   * </p>
460   *
461   * @param query       the query to execute.
462   * @param transaction the transaction to use (can be null).
463   * @return the list of fetched MapBean.
464   * @see SqlQuery#findList()
465   */
466  @Nonnull
467  List<SqlRow> findList(SqlQuery query, Transaction transaction);
468
469  /**
470   * Execute the SqlQuery iterating a row at a time.
471   * <p>
472   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
473   * </p>
474   */
475  void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
476
477  /**
478   * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
479   * <p>
480   * Returning false after processing a row stops the iteration through the query results.
481   * </p>
482   * <p>
483   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
484   * </p>
485   */
486  void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
487
488  /**
489   * Execute the sql query returning a single MapBean or null.
490   * <p>
491   * This will throw a PersistenceException if the query found more than one
492   * result.
493   * </p>
494   * <p>
495   * Generally you are able to use {@link SqlQuery#findOne()} rather than
496   * explicitly calling this method. You could use this method if you wish to
497   * explicitly control the transaction used for the query.
498   * </p>
499   *
500   * @param query       the query to execute.
501   * @param transaction the transaction to use (can be null).
502   * @return the fetched MapBean or null if none was found.
503   * @see SqlQuery#findOne()
504   */
505  @Nullable
506  SqlRow findOne(SqlQuery query, Transaction transaction);
507
508}