001package io.ebean;
002
003import io.avaje.lang.Nullable;
004
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<T> 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  <A, T> List<A> findIds(Query<T> query, Transaction transaction);
086
087  /**
088   * Return a QueryIterator for the query.
089   * <p>
090   * Generally using {@link #findEach(Query, Consumer, Transaction)} or
091   * {@link #findEachWhile(Query, Predicate, Transaction)} is preferred
092   * to findIterate(). The reason is that those methods automatically take care of
093   * closing the queryIterator (and the underlying jdbc statement and resultSet).
094   * <p>
095   * This is similar to findEach in that not all the result beans need to be held
096   * in memory at the same time and as such is good for processing large queries.
097   *
098   * @see Query#findIterate()
099   * @see Query#findEach(Consumer)
100   * @see Query#findEachWhile(Predicate)
101   */
102  <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);
103
104  /**
105   * Execute the query returning the result as a Stream.
106   * <p>
107   * Note that this can support very large queries iterating any number of results.
108   * To do so internally it can use multiple persistence contexts.
109   * <p>
110   * Note that the stream needs to be closed so use with try with resources.
111   * </p>
112   */
113  <T> Stream<T> findStream(Query<T> query, Transaction transaction);
114
115  /**
116   * Deprecated - migrate to findStream().
117   * <p>
118   * Execute the query returning the result as a Stream.
119   * <p>
120   * Note that this can support very large queries iterating any number of results.
121   * To do so internally it can use multiple persistence contexts.
122   * <p>
123   * Note that the stream needs to be closed so use with try with resources.
124   */
125  @Deprecated
126  <T> Stream<T> findLargeStream(Query<T> query, Transaction transaction);
127
128  /**
129   * Execute the query visiting the each bean one at a time.
130   * <p>
131   * Unlike findList() this is suitable for processing a query that will return
132   * a very large resultSet. The reason is that not all the result beans need to be
133   * held in memory at the same time and instead processed one at a time.
134   * </p>
135   * <p>
136   * Internally this query using a PersistenceContext scoped to each bean (and the
137   * beans associated object graph).
138   * </p>
139   * <p>
140   * <pre>{@code
141   *
142   *     DB.find(Order.class)
143   *       .where().eq("status", Order.Status.NEW)
144   *       .order().asc("id")
145   *       .findEach((Order order) -> {
146   *
147   *         // do something with the order bean
148   *         System.out.println(" -- processing order ... " + order);
149   *       });
150   *
151   * }</pre>
152   *
153   * @see Query#findEach(Consumer)
154   * @see Query#findEachWhile(Predicate)
155   */
156  <T> void findEach(Query<T> query, Consumer<T> consumer, Transaction transaction);
157
158  /**
159   * Execute findEach with batch consumer.
160   *
161   * @see Query#findEach(int, Consumer)
162   */
163  <T> void findEach(Query<T> query, int batch, Consumer<List<T>> consumer, Transaction t);
164
165  /**
166   * Execute the query visiting the each bean one at a time.
167   * <p>
168   * Compared to findEach() this provides the ability to stop processing the query
169   * results early by returning false for the Predicate.
170   * </p>
171   * <p>
172   * Unlike findList() this is suitable for processing a query that will return
173   * a very large resultSet. The reason is that not all the result beans need to be
174   * held in memory at the same time and instead processed one at a time.
175   * </p>
176   * <p>
177   * Internally this query using a PersistenceContext scoped to each bean (and the
178   * beans associated object graph).
179   * </p>
180   * <p>
181   * <pre>{@code
182   *
183   *     DB.find(Order.class)
184   *       .where().eq("status", Order.Status.NEW)
185   *       .order().asc("id")
186   *       .findEachWhile((Order order) -> {
187   *
188   *         // do something with the order bean
189   *         System.out.println(" -- processing order ... " + order);
190   *
191   *         boolean carryOnProcessing = ...
192   *         return carryOnProcessing;
193   *       });
194   *
195   * }</pre>
196   *
197   * @see Query#findEach(Consumer)
198   * @see Query#findEachWhile(Predicate)
199   */
200  <T> void findEachWhile(Query<T> query, Predicate<T> consumer, Transaction transaction);
201
202  /**
203   * Return versions of a @History entity bean.
204   * <p>
205   * Generally this query is expected to be a find by id or unique predicates query.
206   * It will execute the query against the history returning the versions of the bean.
207   * </p>
208   */
209  <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);
210
211  /**
212   * Execute a query returning a list of beans.
213   * <p>
214   * Generally you are able to use {@link Query#findList()} rather than
215   * explicitly calling this method. You could use this method if you wish to
216   * explicitly control the transaction used for the query.
217   * </p>
218   * <p>
219   * <pre>{@code
220   *
221   * List<Customer> customers = DB.find(Customer.class)
222   *     .where().ilike("name", "rob%")
223   *     .findList();
224   *
225   * }</pre>
226   *
227   * @param <T>         the type of entity bean to fetch.
228   * @param query       the query to execute.
229   * @param transaction the transaction to use (can be null).
230   * @return the list of fetched beans.
231   * @see Query#findList()
232   */
233  <T> List<T> findList(Query<T> query, Transaction transaction);
234
235  /**
236   * Execute find row count query in a background thread.
237   * <p>
238   * This returns a Future object which can be used to cancel, check the
239   * execution status (isDone etc) and get the value (with or without a
240   * timeout).
241   * </p>
242   *
243   * @param query       the query to execute the row count on
244   * @param transaction the transaction (can be null).
245   * @return a Future object for the row count query
246   * @see Query#findFutureCount()
247   */
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  <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);
264
265  /**
266   * Execute find list query in a background thread returning a FutureList object.
267   * <p>
268   * This returns a Future object which can be used to cancel, check the
269   * execution status (isDone etc) and get the value (with or without a timeout).
270   * <p>
271   * This query will execute in it's own PersistenceContext and using its own transaction.
272   * What that means is that it will not share any bean instances with other queries.
273   *
274   * @param query       the query to execute in the background
275   * @param transaction the transaction (can be null).
276   * @return a Future object for the list result of the query
277   * @see Query#findFutureList()
278   */
279  <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);
280
281  /**
282   * Return a PagedList for this query using firstRow and maxRows.
283   * <p>
284   * The benefit of using this over findList() is that it provides functionality to get the
285   * total row count etc.
286   * </p>
287   * <p>
288   * If maxRows is not set on the query prior to calling findPagedList() then a
289   * PersistenceException is thrown.
290   * </p>
291   * <p>
292   * <pre>{@code
293   *
294   *  PagedList<Order> pagedList = DB.find(Order.class)
295   *       .setFirstRow(50)
296   *       .setMaxRows(20)
297   *       .findPagedList();
298   *
299   *       // fetch the total row count in the background
300   *       pagedList.loadRowCount();
301   *
302   *       List<Order> orders = pagedList.getList();
303   *       int totalRowCount = pagedList.getTotalRowCount();
304   *
305   * }</pre>
306   *
307   * @return The PagedList
308   * @see Query#findPagedList()
309   */
310  <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);
311
312  /**
313   * Execute the query returning a set of entity beans.
314   * <p>
315   * Generally you are able to use {@link Query#findSet()} rather than
316   * explicitly calling this method. You could use this method if you wish to
317   * explicitly control the transaction used for the query.
318   * </p>
319   * <p>
320   * <pre>{@code
321   *
322   * Set<Customer> customers = DB.find(Customer.class)
323   *     .where().ilike("name", "rob%")
324   *     .findSet();
325   *
326   * }</pre>
327   *
328   * @param <T>         the type of entity bean to fetch.
329   * @param query       the query to execute
330   * @param transaction the transaction to use (can be null).
331   * @return the set of fetched beans.
332   * @see Query#findSet()
333   */
334  <T> Set<T> findSet(Query<T> query, Transaction transaction);
335
336  /**
337   * Execute the query returning the entity beans in a Map.
338   * <p>
339   * Generally you are able to use {@link Query#findMap()} rather than
340   * explicitly calling this method. You could use this method if you wish to
341   * explicitly control the transaction used for the query.
342   * </p>
343   *
344   * @param <T>         the type of entity bean to fetch.
345   * @param query       the query to execute.
346   * @param transaction the transaction to use (can be null).
347   * @return the map of fetched beans.
348   * @see Query#findMap()
349   */
350  <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);
351
352  /**
353   * Execute the query returning a list of values for a single property.
354   * <p>
355   * <h3>Example 1:</h3>
356   * <pre>{@code
357   *
358   *  List<String> names =
359   *    DB.find(Customer.class)
360   *      .select("name")
361   *      .order().asc("name")
362   *      .findSingleAttributeList();
363   *
364   * }</pre>
365   * <h3>Example 2:</h3>
366   * <pre>{@code
367   *
368   *  List<String> names =
369   *    DB.find(Customer.class)
370   *      .setDistinct(true)
371   *      .select("name")
372   *      .where().eq("status", Customer.Status.NEW)
373   *      .order().asc("name")
374   *      .setMaxRows(100)
375   *      .findSingleAttributeList();
376   *
377   * }</pre>
378   *
379   * @return the list of values for the selected property
380   * @see Query#findSingleAttributeList()
381   */
382  <A, T> List<A> findSingleAttributeList(Query<T> query, Transaction transaction);
383
384  /**
385   * Execute the query returning at most one entity bean or null (if no matching
386   * bean is found).
387   * <p>
388   * This will throw a NonUniqueResultException if the query finds more than one result.
389   * </p>
390   * <p>
391   * Generally you are able to use {@link Query#findOne()} rather than
392   * explicitly calling this method. You could use this method if you wish to
393   * explicitly control the transaction used for the query.
394   * </p>
395   *
396   * @param <T>         the type of entity bean to fetch.
397   * @param query       the query to execute.
398   * @param transaction the transaction to use (can be null).
399   * @return the list of fetched beans.
400   * @throws NonUniqueResultException if more than one result was found
401   * @see Query#findOne()
402   */
403  @Nullable
404  <T> T findOne(Query<T> query, Transaction transaction);
405
406  /**
407   * Similar to findOne() but returns an Optional (rather than nullable).
408   */
409  <T> Optional<T> findOneOrEmpty(Query<T> query, Transaction transaction);
410
411  /**
412   * Execute as a delete query deleting the 'root level' beans that match the predicates
413   * in the query.
414   * <p>
415   * Note that if the query includes joins then the generated delete statement may not be
416   * optimal depending on the database platform.
417   * </p>
418   *
419   * @param query       the query used for the delete
420   * @param transaction the transaction to use (can be null)
421   * @param <T>         the type of entity bean to fetch.
422   * @return the number of beans/rows that were deleted
423   */
424  <T> int delete(Query<T> query, Transaction transaction);
425
426  /**
427   * Execute the update query returning the number of rows updated.
428   * <p>
429   * The update query must be created using {@link Database#update(Class)}.
430   * </p>
431   *
432   * @param query       the update query to execute
433   * @param transaction the optional transaction to use for the update (can be null)
434   * @param <T>         the type of entity bean
435   * @return The number of rows updated
436   */
437  <T> int update(Query<T> query, Transaction transaction);
438
439  /**
440   * Execute the sql query returning a list of MapBean.
441   * <p>
442   * Generally you are able to use {@link SqlQuery#findList()} rather than
443   * explicitly calling this method. You could use this method if you wish to
444   * explicitly control the transaction used for the query.
445   * </p>
446   *
447   * @param query       the query to execute.
448   * @param transaction the transaction to use (can be null).
449   * @return the list of fetched MapBean.
450   * @see SqlQuery#findList()
451   */
452  List<SqlRow> findList(SqlQuery query, Transaction transaction);
453
454  /**
455   * Execute the SqlQuery iterating a row at a time.
456   * <p>
457   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
458   * </p>
459   */
460  void findEach(SqlQuery query, Consumer<SqlRow> consumer, Transaction transaction);
461
462  /**
463   * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
464   * <p>
465   * Returning false after processing a row stops the iteration through the query results.
466   * </p>
467   * <p>
468   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
469   * </p>
470   */
471  void findEachWhile(SqlQuery query, Predicate<SqlRow> consumer, Transaction transaction);
472
473  /**
474   * Execute the sql query returning a single MapBean or null.
475   * <p>
476   * This will throw a PersistenceException if the query found more than one
477   * result.
478   * </p>
479   * <p>
480   * Generally you are able to use {@link SqlQuery#findOne()} rather than
481   * explicitly calling this method. You could use this method if you wish to
482   * explicitly control the transaction used for the query.
483   * </p>
484   *
485   * @param query       the query to execute.
486   * @param transaction the transaction to use (can be null).
487   * @return the fetched MapBean or null if none was found.
488   * @see SqlQuery#findOne()
489   */
490  @Nullable
491  SqlRow findOne(SqlQuery query, Transaction transaction);
492
493}