package com.avaje.ebean;

import org.jetbrains.annotations.Nullable;

import javax.persistence.NonUniqueResultException;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Object relational query for finding a List, Set, Map or single entity bean.
 * <p>
 * Example: Create the query using the API.
 * </p>
 * <p>
 * <pre>{@code
 *
 * List<Order> orderList =
 *   ebeanServer.find(Order.class)
 *     .fetch("customer")
 *     .fetch("details")
 *     .where()
 *       .like("customer.name","rob%")
 *       .gt("orderDate",lastWeek)
 *     .orderBy("customer.id, id desc")
 *     .setMaxRows(50)
 *     .findList();
 *
 * ...
 * }</pre>
 * <p>
 * Example: The same query using the query language
 * </p>
 * <pre>{@code
 *
 * String oql =
 *   	+" fetch customer "
 *   	+" fetch details "
 *   	+" where customer.name like :custName and orderDate > :minOrderDate "
 *   	+" order by customer.id, id desc "
 *   	+" limit 50 ";
 *
 * Query<Order> query = ebeanServer.createQuery(Order.class, oql);
 * query.setParameter("custName", "Rob%");
 * query.setParameter("minOrderDate", lastWeek);
 *
 * List<Order> orderList = query.findList();
 * ...
 * }</pre>
 * <p>
 * Example: Using a named query called "with.cust.and.details"
 * </p>
 * <pre>{@code
 *
 * Query<Order> query = ebeanServer.createNamedQuery(Order.class,"with.cust.and.details");
 * query.setParameter("custName", "Rob%");
 * query.setParameter("minOrderDate", lastWeek);
 *
 * List<Order> orderList = query.findList();
 * ...
 * }</pre>
 * <h3>AutoTune</h3>
 * <p>
 * Ebean has built in support for "AutoTune". This is a mechanism where a query
 * can be automatically tuned based on profiling information that is collected.
 * </p>
 * <p>
 * This is effectively the same as automatically using select() and fetch() to
 * build a query that will fetch all the data required by the application and no
 * more.
 * </p>
 * <p>
 * It is expected that AutoTune will be the default approach for many queries
 * in a system. It is possibly not as useful where the result of a query is sent
 * to a remote client or where there is some requirement for "Read Consistency"
 * guarantees.
 * </p>
 * <h3>Query Language</h3>
 * <p>
 * <b>Partial Objects</b>
 * </p>
 * <p>
 * The <em>find</em> and <em>fetch</em> clauses support specifying a list of
 * properties to fetch. This results in objects that are "partially populated".
 * If you try to get a property that was not populated a "lazy loading" query
 * will automatically fire and load the rest of the properties of the bean (This
 * is very similar behaviour as a reference object being "lazy loaded").
 * </p>
 * <p>
 * Partial objects can be saved just like fully populated objects. If you do
 * this you should remember to include the <em>"Version"</em> property in the
 * initial fetch. If you do not include a version property then optimistic
 * concurrency checking will occur but only include the fetched properties.
 * Refer to "ALL Properties/Columns" mode of Optimistic Concurrency checking.
 * </p>
 * <pre>{@code
 * [ select [ ( * | {fetch properties} ) ] ]
 * [ fetch {path} [ ( * | {fetch properties} ) ] ]
 * [ where {predicates} ]
 * [ order by {order by properties} ]
 * [ limit {max rows} [ offset {first row} ] ]
 * }</pre>
 * <p>
 * <b>SELECT</b> [ ( <i>*</i> | <i>{fetch properties}</i> ) ]
 * </p>
 * <p>
 * With the select you can specify a list of properties to fetch.
 * </p>
 * <p>
 * <b>FETCH</b> <b>{path}</b> [ ( <i>*</i> | <i>{fetch properties}</i> ) ]
 * </p>
 * <p>
 * With the fetch you specify the associated property to fetch and populate. The
 * path is a OneToOne, ManyToOne, OneToMany or ManyToMany property.
 * </p>
 * <p>
 * For fetch of a path we can optionally specify a list of properties to fetch.
 * If you do not specify a list of properties ALL the properties for that bean
 * type are fetched.
 * </p>
 * <p>
 * <b>WHERE</b> <b>{list of predicates}</b>
 * </p>
 * <p>
 * The list of predicates which are joined by AND OR NOT ( and ). They can
 * include named (or positioned) bind parameters. These parameters will need to
 * be bound by {@link Query#setParameter(String, Object)}.
 * </p>
 * <p>
 * <b>ORDER BY</b> <b>{order by properties}</b>
 * </p>
 * <p>
 * The list of properties to order the result. You can include ASC (ascending)
 * and DESC (descending) in the order by clause.
 * </p>
 * <p>
 * <b>LIMIT</b> <b>{max rows}</b> [ OFFSET <i>{first row}</i> ]
 * </p>
 * <p>
 * The limit offset specifies the max rows and first row to fetch. The offset is
 * optional.
 * </p>
 * <h4>Examples of Ebean's Query Language</h4>
 * <p>
 * Find orders fetching its id, shipDate and status properties. Note that the id
 * property is always fetched even if it is not included in the list of fetch
 * properties.
 * </p>
 * <pre>{@code
 *
 * select (shipDate, status)
 *
 * }</pre>
 * <p>
 * Find orders with a named bind variable (that will need to be bound via
 * {@link Query#setParameter(String, Object)}).
 * </p>
 * <pre>{@code
 *
 * where customer.name like :custLike
 *
 * }</pre>
 * <p>
 * Find orders and also fetch the customer with a named bind parameter. This
 * will fetch and populate both the order and customer objects.
 * </p>
 * <pre>{@code
 *
 * fetch customer
 * where customer.id = :custId
 *
 * }</pre>
 * <p>
 * Find orders and also fetch the customer, customer shippingAddress, order
 * details and related product. Note that customer and product objects will be
 * "Partial Objects" with only some of their properties populated. The customer
 * objects will have their id, name and shipping address populated. The product
 * objects (associated with each order detail) will have their id, sku and name
 * populated.
 * </p>
 * <pre>{@code
 *
 * fetch customer (name)
 * fetch customer.shippingAddress
 * fetch details
 * fetch details.product (sku, name)
 *
 * }</pre>
 *
 * @param <T> the type of Entity bean this query will fetch.
 */
public interface Query<T> {

  /**
   * Return the RawSql that was set to use for this query.
   */
  RawSql getRawSql();

  /**
   * Set RawSql to use for this query.
   */
  Query<T> setRawSql(RawSql rawSql);

  /**
   * Perform an 'As of' query using history tables to return the object graph
   * as of a time in the past.
   * <p>
   * To perform this query the DB must have underlying history tables.
   * </p>
   *
   * @param asOf the date time in the past at which you want to view the data
   */
  Query<T> asOf(Timestamp asOf);

  /**
   * Execute the query against the draft set of tables.
   */
  Query<T> asDraft();

  /**
   * Cancel the query execution if supported by the underlying database and
   * driver.
   * <p>
   * This must be called from a different thread to the query executor.
   * </p>
   */
  void cancel();

  /**
   * Return a copy of the query.
   * <p>
   * This is so that you can use a Query as a "prototype" for creating other
   * query instances. You could create a Query with various where expressions
   * and use that as a "prototype" - using this copy() method to create a new
   * instance that you can then add other expressions then execute.
   * </p>
   */
  Query<T> copy();

  /**
   * Specify the PersistenceContextScope to use for this query.
   * <p/>
   * When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)}
   * is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}.
   * <p/>
   * Note that the same persistence Context is used for subsequent lazy loading and query join queries.
   * <p/>
   * Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for
   * queries executed as #findIterate, #findEach, #findEachWhile.
   *
   * @param scope The scope to use for this query and subsequent lazy loading.
   */
  Query<T> setPersistenceContextScope(PersistenceContextScope scope);

  /**
   * Return the ExpressionFactory used by this query.
   */
  ExpressionFactory getExpressionFactory();

  /**
   * Returns true if this query was tuned by autoTune.
   */
  boolean isAutoTuned();

  /**
   * Explicitly specify whether to use AutoTune for this query.
   * <p>
   * If you do not call this method on a query the "Implicit AutoTune mode" is
   * used to determine if AutoTune should be used for a given query.
   * </p>
   * <p>
   * AutoTune can add additional fetch paths to the query and specify which
   * properties are included for each path. If you have explicitly defined some
   * fetch paths AutoTune will not remove them.
   * </p>
   */
  Query<T> setAutoTune(boolean autoTune);

  /**
   * Set the default lazy loading batch size to use.
   * <p>
   * When lazy loading is invoked on beans loaded by this query then this sets the
   * batch size used to load those beans.
   *
   * @param lazyLoadBatchSize the number of beans to lazy load in a single batch
   */
  Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);

  /**
   * Execute the query including soft deleted rows.
   * <p>
   * This means that Ebean will not add any predicates to the query for filtering out
   * soft deleted rows. You can still add your own predicates for the deleted properties
   * and effectively you have full control over the query to include or exclude soft deleted
   * rows as needed for a given use case.
   * </p>
   */
  Query<T> setIncludeSoftDeletes();

  /**
   * Disable read auditing for this query.
   * <p>
   * This is intended to be used when the query is not a user initiated query and instead
   * part of the internal processing in an application to load a cache or document store etc.
   * In these cases we don't want the query to be part of read auditing.
   * </p>
   */
  Query<T> setDisableReadAuditing();

  /**
   * Specify the properties to fetch on the root level entity bean in comma delimited format.
   * <p>
   * The Id property is automatically included in the properties to fetch unless setDistinct(true)
   * is set on the query.
   * </p>
   * <p>
   * Use {@link #fetch(String, String)} to specify specific properties to fetch
   * on other non-root level paths of the object graph.
   * </p>
   * <pre>{@code
   *
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     // Only fetch the customer id, name and status.
   *     // This is described as a "Partial Object"
   *     .select("name, status")
   *     .where.ilike("name", "rob%")
   *     .findList();
   *
   * }</pre>
   *
   * @param fetchProperties the properties to fetch for this bean (* = all properties).
   */
  Query<T> select(String fetchProperties);

  /**
   * Specify a path to fetch eagerly including specific properties.
   * <p>
   * Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
   * not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
   * will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
   * </p>
   * <pre>{@code
   *
   * // query orders...
   * List<Order> orders =
   *     ebeanServer.find(Order.class)
   *       // fetch the customer...
   *       // ... getting the customers name and phone number
   *       .fetch("customer", "name, phoneNumber")
   *
   *       // ... also fetch the customers billing address (* = all properties)
   *       .fetch("customer.billingAddress", "*")
   *       .findList();
   * }</pre>
   * <p>
   * If columns is null or "*" then all columns/properties for that path are fetched.
   * </p>
   * <pre>{@code
   *
   * // fetch customers (their id, name and status)
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .select("name, status")
   *     .fetch("contacts", "firstName,lastName,email")
   *     .findList();
   *
   * }</pre>
   *
   * @param path            the property path we wish to fetch eagerly.
   * @param fetchProperties properties of the associated bean that you want to include in the
   *                        fetch (* means all properties, null also means all properties).
   */
  Query<T> fetch(String path, String fetchProperties);

  /**
   * Fetch the path and properties using a "query join" (separate SQL query).
   * <p>
   * This is the same as:
   * </p>
   * <pre>{@code
   *
   *  fetch(path, fetchProperties, new FetchConfig().query())
   *
   * }</pre>
   * <p>
   * This would be used instead of a fetch() when we use a separate SQL query to fetch this
   * part of the object graph rather than a SQL join.
   * </p>
   * <p>
   * We might typically get a performance benefit when the path to fetch is a OneToMany
   * or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
   * is high.
   * </p>
   *
   * @param path            the property path we wish to fetch eagerly.
   * @param fetchProperties properties of the associated bean that you want to include in the
   *                        fetch (* means all properties, null also means all properties).
   */
  Query<T> fetchQuery(String path, String fetchProperties);

  /**
   * Fetch the path and properties lazily (via batch lazy loading).
   * <p>
   * This is the same as:
   * </p>
   * <pre>{@code
   *
   *  fetch(path, fetchProperties, new FetchConfig().lazy())
   *
   * }</pre>
   * <p>
   * The reason for using fetchLazy() is to either:
   * </p>
   * <ul>
   * <li>Control/tune what is fetched as part of lazy loading</li>
   * <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
   * </ul>
   *
   * @param path            the property path we wish to fetch lazily.
   * @param fetchProperties properties of the associated bean that you want to include in the
   *                        fetch (* means all properties, null also means all properties).
   */
  Query<T> fetchLazy(String path, String fetchProperties);

  /**
   * Additionally specify a FetchConfig to use a separate query or lazy loading
   * to load this path.
   * <pre>{@code
   *
   * // fetch customers (their id, name and status)
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .select("name, status")
   *     .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
   *     .findList();
   *
   * }</pre>
   *
   * @param path the property path we wish to fetch eagerly.
   */
  Query<T> fetch(String path, String fetchProperties, FetchConfig fetchConfig);

  /**
   * Specify a path to fetch eagerly including all its properties.
   * <p>
   * Ebean will endeavour to fetch this path using a SQL join. If Ebean determines that it can
   * not use a SQL join (due to maxRows or because it would result in a cartesian product) Ebean
   * will automatically convert this fetch query into a "query join" - i.e. use fetchQuery().
   * </p>
   * <pre>{@code
   *
   * // fetch customers (their id, name and status)
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     // eager fetch the contacts
   *     .fetch("contacts")
   *     .findList();
   *
   * }</pre>
   *
   * @param path the property path we wish to fetch eagerly.
   */
  Query<T> fetch(String path);

  /**
   * Fetch the path eagerly using a "query join" (separate SQL query).
   * <p>
   * This is the same as:
   * </p>
   * <pre>{@code
   *
   *  fetch(path, new FetchConfig().query())
   *
   * }</pre>
   * <p>
   * This would be used instead of a fetch() when we use a separate SQL query to fetch this
   * part of the object graph rather than a SQL join.
   * </p>
   * <p>
   * We might typically get a performance benefit when the path to fetch is a OneToMany
   * or ManyToMany, the 'width' of the 'root bean' is wide and the cardinality of the many
   * is high.
   * </p>
   *
   * @param path the property path we wish to fetch eagerly
   */
  Query<T> fetchQuery(String path);

  /**
   * Fetch the path lazily (via batch lazy loading).
   * <p>
   * This is the same as:
   * </p>
   * <pre>{@code
   *
   *  fetch(path, new FetchConfig().lazy())
   *
   * }</pre>
   * <p>
   * The reason for using fetchLazy() is to either:
   * </p>
   * <ul>
   * <li>Control/tune what is fetched as part of lazy loading</li>
   * <li>Make use of the L2 cache, build this part of the graph from L2 cache</li>
   * </ul>
   *
   * @param path the property path we wish to fetch lazily.
   */
  Query<T> fetchLazy(String path);

  /**
   * Additionally specify a JoinConfig to specify a "query join" and or define
   * the lazy loading query.
   * <pre>{@code
   *
   * // fetch customers (their id, name and status)
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     // lazy fetch contacts with a batch size of 100
   *     .fetch("contacts", new FetchConfig().lazy(100))
   *     .findList();
   *
   * }</pre>
   */
  Query<T> fetch(String path, FetchConfig fetchConfig);

  /**
   * Apply the path properties replacing the select and fetch clauses.
   * <p>
   * This is typically used when the FetchPath is applied to both the query and the JSON output.
   * </p>
   */
  Query<T> apply(FetchPath fetchPath);

  /**
   * Execute the query returning the list of Id's.
   * <p>
   * This query will execute against the EbeanServer that was used to create it.
   * </p>
   *
   * @see EbeanServer#findIds(Query, Transaction)
   */
  <A> List<A> findIds();

  /**
   * Execute the query iterating over the results.
   * <p>
   * Note that findIterate (and findEach and findEachWhile) uses a "per graph"
   * persistence context scope and adjusts jdbc fetch buffer size for large
   * queries. As such it is better to use findList for small queries.
   * </p>
   * <p>
   * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()}
   * when you have finished iterating the results (typically in a finally block).
   * </p>
   * <p>
   * findEach() and findEachWhile() are preferred to findIterate() as they ensure
   * the jdbc statement and resultSet are closed at the end of the iteration.
   * </p>
   * <p>
   * This query will execute against the EbeanServer that was used to create it.
   * </p>
   * <pre>{@code
   *
   *  Query<Customer> query =
   *    ebeanServer.find(Customer.class)
   *     .where().eq("status", Status.NEW)
   *     .order().asc("id");
   *
   *  QueryIterator<Customer> it = query.findIterate();
   *  try {
   *    while (it.hasNext()) {
   *      Customer customer = it.next();
   *      // do something with customer ...
   *    }
   *  } finally {
   *    // close the underlying resources
   *    it.close();
   *  }
   *
   * }</pre>
   */
  QueryIterator<T> findIterate();

  /**
   * Execute the query processing the beans one at a time.
   * <p>
   * This method is appropriate to process very large query results as the
   * beans are consumed one at a time and do not need to be held in memory
   * (unlike #findList #findSet etc)
   * </p>
   * <p>
   * Note that findEach (and findEachWhile and findIterate) uses a "per graph"
   * persistence context scope and adjusts jdbc fetch buffer size for large
   * queries. As such it is better to use findList for small queries.
   * </p>
   * <p>
   * Note that internally Ebean can inform the JDBC driver that it is expecting larger
   * resultSet and specifically for MySQL this hint is required to stop it's JDBC driver
   * from buffering the entire resultSet. As such, for smaller resultSets findList() is
   * generally preferable.
   * </p>
   * <p>
   * Compared with #findEachWhile this will always process all the beans where as
   * #findEachWhile provides a way to stop processing the query result early before
   * all the beans have been read.
   * </p>
   * <p>
   * This method is functionally equivalent to findIterate() but instead of using an
   * iterator uses the QueryEachConsumer (SAM) interface which is better suited to use
   * with Java8 closures.
   * </p>
   * <pre>{@code
   *
   *  ebeanServer.find(Customer.class)
   *     .where().eq("status", Status.NEW)
   *     .order().asc("id")
   *     .findEach((Customer customer) -> {
   *
   *       // do something with customer
   *       System.out.println("-- visit " + customer);
   *     });
   *
   * }</pre>
   *
   * @param consumer the consumer used to process the queried beans.
   */
  void findEach(QueryEachConsumer<T> consumer);

  /**
   * Execute the query using callbacks to a visitor to process the resulting
   * beans one at a time.
   * <p>
   * Note that findEachWhile (and findEach and findIterate) uses a "per graph"
   * persistence context scope and adjusts jdbc fetch buffer size for large
   * queries. As such it is better to use findList for small queries.
   * </p>
   * <p>
   * This method is functionally equivalent to findIterate() but instead of using an
   * iterator uses the QueryEachWhileConsumer (SAM) interface which is better suited to use
   * with Java8 closures.
   * </p>
   * <pre>{@code
   *
   *  ebeanServer.find(Customer.class)
   *     .fetch("contacts", new FetchConfig().query(2))
   *     .where().eq("status", Status.NEW)
   *     .order().asc("id")
   *     .setMaxRows(2000)
   *     .findEachWhile((Customer customer) -> {
   *
   *       // do something with customer
   *       System.out.println("-- visit " + customer);
   *
   *       // return true to continue processing or false to stop
   *       return (customer.getId() < 40);
   *     });
   *
   * }</pre>
   *
   * @param consumer the consumer used to process the queried beans.
   */
  void findEachWhile(QueryEachWhileConsumer<T> consumer);

  /**
   * Execute the query returning the list of objects.
   * <p>
   * This query will execute against the EbeanServer that was used to create it.
   * </p>
   * <pre>{@code
   *
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .where().ilike("name", "rob%")
   *     .findList();
   *
   * }</pre>
   *
   * @see EbeanServer#findList(Query, Transaction)
   */
  List<T> findList();

  /**
   * Execute the query returning the set of objects.
   * <p>
   * This query will execute against the EbeanServer that was used to create it.
   * </p>
   * <pre>{@code
   *
   * Set<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .where().ilike("name", "rob%")
   *     .findSet();
   *
   * }</pre>
   *
   * @see EbeanServer#findSet(Query, Transaction)
   */
  Set<T> findSet();

  /**
   * Execute the query returning a map of the objects.
   * <p>
   * This query will execute against the EbeanServer that was used to create it.
   * </p>
   * <p>
   * You can use setMapKey() so specify the property values to be used as keys
   * on the map. If one is not specified then the id property is used.
   * </p>
   * <pre>{@code
   *
   * Map<String, Product> map =
   *   ebeanServer.find(Product.class)
   *     .setMapKey("sku")
   *     .findMap();
   *
   * }</pre>
   *
   * @see EbeanServer#findMap(Query, Transaction)
   */
  <K> Map<K, T> findMap();

  /**
   * Execute the query returning a list of values for a single property.
   *
   * <h3>Example 1:</h3>
   * <pre>{@code
   *
   *  List<String> names =
   *    Ebean.find(Customer.class)
   *      .select("name")
   *      .orderBy().asc("name")
   *      .findSingleAttributeList();
   *
   * }</pre>
   *
   * <h3>Example 2:</h3>
   * <pre>{@code
   *
   *  List<String> names =
   *    Ebean.find(Customer.class)
   *      .setDistinct(true)
   *      .select("name")
   *      .where().eq("status", Customer.Status.NEW)
   *      .orderBy().asc("name")
   *      .setMaxRows(100)
   *      .findSingleAttributeList();
   *
   * }</pre>
   *
   * @return the list of values for the selected property
   */
  <A> List<A> findSingleAttributeList();

  /**
   * Execute the query returning either a single bean or null (if no matching
   * bean is found).
   * <p>
   * If more than 1 row is found for this query then a NonUniqueResultException is
   * thrown.
   * </p>
   * <p>
   * This is useful when your predicates dictate that your query should only
   * return 0 or 1 results.
   * </p>
   * <pre>{@code
   *
   * // assuming the sku of products is unique...
   * Product product =
   *     ebeanServer.find(Product.class)
   *         .where().eq("sku", "aa113")
   *         .findUnique();
   * ...
   * }</pre>
   * <p>
   * It is also useful with finding objects by their id when you want to specify
   * further join information.
   * </p>
   * <pre>{@code
   *
   * // Fetch order 1 and additionally fetch join its order details...
   * Order order =
   *     ebeanServer.find(Order.class)
   *       .setId(1)
   *       .fetch("details")
   *       .findUnique();
   *
   * // the order details were eagerly loaded
   * List<OrderDetail> details = order.getDetails();
   * ...
   * }</pre>
   *
   * @throws NonUniqueResultException if more than one result was found
   */
  @Nullable
  T findUnique();

  /**
   * Return versions of a @History entity bean.
   * <p>
   * Note that this query will work against view based history implementations
   * but not sql2011 standards based implementations that require a start and
   * end timestamp to be specified.
   * </p>
   * <p>
   * Generally this query is expected to be a find by id or unique predicates query.
   * It will execute the query against the history returning the versions of the bean.
   * </p>
   */
  List<Version<T>> findVersions();

  /**
   * Return versions of a @History entity bean between the 2 timestamps.
   * <p>
   * Generally this query is expected to be a find by id or unique predicates query.
   * It will execute the query against the history returning the versions of the bean.
   * </p>
   */
  List<Version<T>> findVersionsBetween(Timestamp start, Timestamp end);

  /**
   * Execute as a delete query deleting the 'root level' beans that match the predicates
   * in the query.
   * <p>
   * Note that if the query includes joins then the generated delete statement may not be
   * optimal depending on the database platform.
   * </p>
   *
   * @return the number of beans/rows that were deleted.
   */
  int delete();

  /**
   * Execute the UpdateQuery returning the number of rows updated.
   */
  int update();

  /**
   * Return the count of entities this query should return.
   * <p>
   * This is the number of 'top level' or 'root level' entities.
   * </p>
   */
  int findCount();

  /**
   * Execute find row count query in a background thread.
   * <p>
   * This returns a Future object which can be used to cancel, check the
   * execution status (isDone etc) and get the value (with or without a
   * timeout).
   * </p>
   *
   * @return a Future object for the row count query
   */
  FutureRowCount<T> findFutureCount();

  /**
   * Execute find Id's query in a background thread.
   * <p>
   * This returns a Future object which can be used to cancel, check the
   * execution status (isDone etc) and get the value (with or without a
   * timeout).
   * </p>
   *
   * @return a Future object for the list of Id's
   */
  FutureIds<T> findFutureIds();

  /**
   * Execute find list query in a background thread.
   * <p>
   * This query will execute in it's own PersistenceContext and using its own transaction.
   * What that means is that it will not share any bean instances with other queries.
   * </p>
   *
   * @return a Future object for the list result of the query
   */
  FutureList<T> findFutureList();

  /**
   * Return a PagedList for this query using firstRow and maxRows.
   * <p>
   * The benefit of using this over findList() is that it provides functionality to get the
   * total row count etc.
   * </p>
   * <p>
   * If maxRows is not set on the query prior to calling findPagedList() then a
   * PersistenceException is thrown.
   * </p>
   * <pre>{@code
   *
   *  PagedList<Order> pagedList = Ebean.find(Order.class)
   *       .setFirstRow(50)
   *       .setMaxRows(20)
   *       .findPagedList();
   *
   *       // fetch the total row count in the background
   *       pagedList.loadRowCount();
   *
   *       List<Order> orders = pagedList.getList();
   *       int totalRowCount = pagedList.getTotalRowCount();
   *
   * }</pre>
   *
   * @return The PagedList
   */
  PagedList<T> findPagedList();

  /**
   * Set a named bind parameter. Named parameters have a colon to prefix the name.
   * <pre>{@code
   *
   * // a query with a named parameter
   * String oql = "find order where status = :orderStatus";
   *
   * Query<Order> query = ebeanServer.find(Order.class, oql);
   *
   * // bind the named parameter
   * query.bind("orderStatus", OrderStatus.NEW);
   * List<Order> list = query.findList();
   *
   * }</pre>
   *
   * @param name  the parameter name
   * @param value the parameter value
   */
  Query<T> setParameter(String name, Object value);

  /**
   * Set an ordered bind parameter according to its position. Note that the
   * position starts at 1 to be consistent with JDBC PreparedStatement. You need
   * to set a parameter value for each ? you have in the query.
   * <pre>{@code
   *
   * // a query with a positioned parameter
   * String oql = "where status = ? order by id desc";
   *
   * Query<Order> query = ebeanServer.createQuery(Order.class, oql);
   *
   * // bind the parameter
   * query.setParameter(1, OrderStatus.NEW);
   *
   * List<Order> list = query.findList();
   *
   * }</pre>
   *
   * @param position the parameter bind position starting from 1 (not 0)
   * @param value    the parameter bind value.
   */
  Query<T> setParameter(int position, Object value);

  /**
   * Set the Id value to query. This is used with findUnique().
   * <p>
   * You can use this to have further control over the query. For example adding
   * fetch joins.
   * </p>
   * <pre>{@code
   *
   * Order order =
   *     ebeanServer.find(Order.class)
   *     .setId(1)
   *     .fetch("details")
   *     .findUnique();
   *
   * // the order details were eagerly fetched
   * List<OrderDetail> details = order.getDetails();
   *
   * }</pre>
   */
  Query<T> setId(Object id);

  /**
   * Return the Id value.
   */
  Object getId();

  /**
   * Add a single Expression to the where clause returning the query.
   * <pre>{@code
   *
   * List<Order> newOrders =
   *     ebeanServer.find(Order.class)
   * 		.where().eq("status", Order.NEW)
   * 		.findList();
   * ...
   *
   * }</pre>
   */
  Query<T> where(Expression expression);

  /**
   * Add Expressions to the where clause with the ability to chain on the
   * ExpressionList. You can use this for adding multiple expressions to the
   * where clause.
   * <pre>{@code
   *
   * List<Order> orders =
   *     ebeanServer.find(Order.class)
   *     .where()
   *       .eq("status", Order.NEW)
   *       .ilike("customer.name","rob%")
   *     .findList();
   *
   * }</pre>
   *
   * @return The ExpressionList for adding expressions to.
   * @see Expr
   */
  ExpressionList<T> where();

  /**
   * Add Full text search expressions for Document store queries.
   * <p>
   * This is currently ElasticSearch only and provides the full text
   * expressions such as Match and Multi-Match.
   * </p>
   * <p>
   * This automatically makes this query a "Doc Store" query and will execute
   * against the document store (ElasticSearch).
   * </p>
   * <p>
   * Expressions added here are added to the "query" section of an ElasticSearch
   * query rather than the "filter" section.
   * </p>
   * <p>
   * Expressions added to the where() are added to the "filter" section of an
   * ElasticSearch query.
   * </p>
   */
  ExpressionList<T> text();

  /**
   * This applies a filter on the 'many' property list rather than the root
   * level objects.
   * <p>
   * Typically you will use this in a scenario where the cardinality is high on
   * the 'many' property you wish to join to. Say you want to fetch customers
   * and their associated orders... but instead of getting all the orders for
   * each customer you only want to get the new orders they placed since last
   * week. In this case you can use filterMany() to filter the orders.
   * </p>
   * <pre>{@code
   *
   * List<Customer> list =
   *     ebeanServer.find(Customer.class)
   *     // .fetch("orders", new FetchConfig().lazy())
   *     // .fetch("orders", new FetchConfig().query())
   *     .fetch("orders")
   *     .where().ilike("name", "rob%")
   *     .filterMany("orders").eq("status", Order.Status.NEW).gt("orderDate", lastWeek)
   *     .findList();
   *
   * }</pre>
   * <p>
   * Please note you have to be careful that you add expressions to the correct
   * expression list - as there is one for the 'root level' and one for each
   * filterMany that you have.
   * </p>
   *
   * @param propertyName the name of the many property that you want to have a filter on.
   * @return the expression list that you add filter expressions for the many
   * to.
   */
  ExpressionList<T> filterMany(String propertyName);

  /**
   * Add Expressions to the Having clause return the ExpressionList.
   * <p>
   * Currently only beans based on raw sql will use the having clause.
   * </p>
   * <p>
   * Note that this returns the ExpressionList (so you can add multiple
   * expressions to the query in a fluent API way).
   * </p>
   *
   * @return The ExpressionList for adding more expressions to.
   * @see Expr
   */
  ExpressionList<T> having();

  /**
   * Add an expression to the having clause returning the query.
   * <p>
   * Currently only beans based on raw sql will use the having clause.
   * </p>
   * <p>
   * This is similar to {@link #having()} except it returns the query rather
   * than the ExpressionList. This is useful when you want to further specify
   * something on the query.
   * </p>
   *
   * @param addExpressionToHaving the expression to add to the having clause.
   * @return the Query object
   */
  Query<T> having(Expression addExpressionToHaving);

  /**
   * Set the order by clause replacing the existing order by clause if there is
   * one.
   * <p>
   * This follows SQL syntax using commas between each property with the
   * optional asc and desc keywords representing ascending and descending order
   * respectively.
   * </p>
   * <p>
   * This is EXACTLY the same as {@link #order(String)}.
   * </p>
   */
  Query<T> orderBy(String orderByClause);

  /**
   * Set the order by clause replacing the existing order by clause if there is
   * one.
   * <p>
   * This follows SQL syntax using commas between each property with the
   * optional asc and desc keywords representing ascending and descending order
   * respectively.
   * </p>
   * <p>
   * This is EXACTLY the same as {@link #orderBy(String)}.
   * </p>
   */
  Query<T> order(String orderByClause);

  /**
   * Return the OrderBy so that you can append an ascending or descending
   * property to the order by clause.
   * <p>
   * This will never return a null. If no order by clause exists then an 'empty'
   * OrderBy object is returned.
   * </p>
   * <p>
   * This is EXACTLY the same as {@link #orderBy()}.
   * </p>
   */
  OrderBy<T> order();

  /**
   * Return the OrderBy so that you can append an ascending or descending
   * property to the order by clause.
   * <p>
   * This will never return a null. If no order by clause exists then an 'empty'
   * OrderBy object is returned.
   * </p>
   * <p>
   * This is EXACTLY the same as {@link #order()}.
   * </p>
   */
  OrderBy<T> orderBy();

  /**
   * Set an OrderBy object to replace any existing OrderBy clause.
   * <p>
   * This is EXACTLY the same as {@link #setOrderBy(OrderBy)}.
   * </p>
   */
  Query<T> setOrder(OrderBy<T> orderBy);

  /**
   * Set an OrderBy object to replace any existing OrderBy clause.
   * <p>
   * This is EXACTLY the same as {@link #setOrder(OrderBy)}.
   * </p>
   */
  Query<T> setOrderBy(OrderBy<T> orderBy);

  /**
   * Set whether this query uses DISTINCT.
   * <p>
   * The select() clause MUST be specified when setDistinct(true) is set. The reason for this is that
   * generally ORM queries include the "id" property and this doesn't make sense for distinct queries.
   * </p>
   * <pre>{@code
   *
   *   List<Customer> customers =
   *       Ebean.find(Customer.class)
   *          .setDistinct(true)
   *          .select("name")
   *          .findList();
   *
   * }</pre>
   */
  Query<T> setDistinct(boolean isDistinct);

  /**
   * Return the first row value.
   */
  int getFirstRow();

  /**
   * Set the first row to return for this query.
   *
   * @param firstRow the first row to include in the query result.
   */
  Query<T> setFirstRow(int firstRow);

  /**
   * Return the max rows for this query.
   */
  int getMaxRows();

  /**
   * Set the maximum number of rows to return in the query.
   *
   * @param maxRows the maximum number of rows to return in the query.
   */
  Query<T> setMaxRows(int maxRows);

  /**
   * Set the property to use as keys for a map.
   * <p>
   * If no property is set then the id property is used.
   * </p>
   * <pre>{@code
   *
   * // Assuming sku is unique for products...
   *
   * Map<?,Product> productMap =
   *     ebeanServer.find(Product.class)
   *     // use sku for keys...
   *     .setMapKey("sku")
   *     .findMap();
   *
   * }</pre>
   *
   * @param mapKey the property to use as keys for a map.
   */
  Query<T> setMapKey(String mapKey);

  /**
   * Set this to false to not use the bean cache.
   * <p>
   * By default "find by id" and "find by natural key" will use the bean cache
   * when bean caching is enabled. Setting this to false means that the query
   * will not use the bean cache and instead hit the database.
   * </p>
   * <p>
   * In the case of other queries (findList(), findEach() etc) then setting this to
   * false beans that the if lazy loading is invoked that lazy loading will not try
   * to use the bean cache.
   * </p>
   */
  Query<T> setUseCache(boolean useCache);

  /**
   * Set this to true to use the query cache.
   */
  Query<T> setUseQueryCache(boolean useQueryCache);

  /**
   * Set to true if this query should execute against the doc store.
   * <p>
   * When setting this you may also consider disabling lazy loading.
   * </p>
   */
  Query<T> setUseDocStore(boolean useDocStore);

  /**
   * When set to true when you want the returned beans to be read only.
   */
  Query<T> setReadOnly(boolean readOnly);

  /**
   * When set to true all the beans from this query are loaded into the bean
   * cache.
   */
  Query<T> setLoadBeanCache(boolean loadBeanCache);

  /**
   * Set a timeout on this query.
   * <p>
   * This will typically result in a call to setQueryTimeout() on a
   * preparedStatement. If the timeout occurs an exception will be thrown - this
   * will be a SQLException wrapped up in a PersistenceException.
   * </p>
   *
   * @param secs the query timeout limit in seconds. Zero means there is no limit.
   */
  Query<T> setTimeout(int secs);

  /**
   * A hint which for JDBC translates to the Statement.fetchSize().
   * <p>
   * Gives the JDBC driver a hint as to the number of rows that should be
   * fetched from the database when more rows are needed for ResultSet.
   * </p>
   * <p>
   * Note that internally findEach and findEachWhile will set the fetch size
   * if it has not already as these queries expect to process a lot of rows.
   * If we didn't then Postgres and MySql for example would eagerly pull back
   * all the row data and potentially consume a lot of memory in the process.
   * </p>
   * <p>
   * As findEach and findEachWhile automatically set the fetch size we don't have
   * to do so generally but we might still wish to for tuning a specific use case.
   * </p>
   */
  Query<T> setBufferFetchSizeHint(int fetchSize);

  /**
   * Return the sql that was generated for executing this query.
   * <p>
   * This is only available after the query has been executed and provided only
   * for informational purposes.
   * </p>
   */
  String getGeneratedSql();

  /**
   * executed the select with "for update" which should lock the record
   * "on read"
   */
  Query<T> setForUpdate(boolean forUpdate);

  /**
   * Return true if this query has forUpdate set.
   */
  boolean isForUpdate();

  /**
   * Set root table alias.
   */
  Query<T> alias(String alias);

  /**
   * Return the type of beans being queried.
   */
  Class<T> getBeanType();

  /**
   * Set true if you want to disable lazy loading.
   * <p>
   * That is, once the object graph is returned further lazy loading is disabled.
   * </p>
   */
  Query<T> setDisableLazyLoading(boolean disableLazyLoading);

  /**
   * Returns the set of properties or paths that are unknown (do not map to known properties or paths).
   * <p>
   * Validate the query checking the where and orderBy expression paths to confirm if
   * they represent valid properties or paths for the given bean type.
   * </p>
   */
  Set<String> validate();

}
