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}