package com.avaje.ebean;

import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import org.jetbrains.annotations.Nullable;

import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Provides the API for fetching and saving beans to a particular DataSource.
 * <p>
 * <b>Registration with the Ebean Singleton:</b><br/>
 * When a EbeanServer is constructed it can be registered with the Ebean
 * singleton (see {@link ServerConfig#setRegister(boolean)}). The Ebean
 * singleton is essentially a map of EbeanServer's that have been registered
 * with it. The EbeanServer can then be retrieved later via
 * {@link Ebean#getServer(String)}.
 * </p>
 * <p>
 * <b>The 'default' EbeanServer</b><br/>
 * One EbeanServer can be designated as the 'default' or 'primary' EbeanServer
 * (see {@link ServerConfig#setDefaultServer(boolean)}. Many methods on Ebean
 * such as {@link Ebean#find(Class)} etc are actually just a convenient way to
 * call methods on the 'default/primary' EbeanServer. This is handy for
 * applications that use a single DataSource.
 * <p>
 * There is one EbeanServer per Database (javax.sql.DataSource). One EbeanServer
 * is referred to as the <em>'default'</em> server and that is the one that
 * Ebean methods such as {@link Ebean#find(Class)} use.
 * </p>
 * <p>
 * <b>Constructing a EbeanServer</b><br/>
 * EbeanServer's are constructed by the EbeanServerFactory. They can be created
 * programmatically via {@link EbeanServerFactory#create(ServerConfig)} or they
 * can be automatically constructed on demand using configuration information in
 * the ebean.properties file.
 * </p>
 * <p>
 * Example: Get a EbeanServer
 * </p>
 * <p>
 * <pre>{@code
 * // Get access to the Human Resources EbeanServer/Database
 * EbeanServer hrServer = Ebean.getServer("HR");
 *
 *
 * // fetch contact 3 from the HR database Contact contact =
 * hrServer.find(Contact.class, new Integer(3));
 *
 * contact.setStatus("INACTIVE"); ...
 *
 * // save the contact back to the HR database hrServer.save(contact);
 * }</pre>
 * <p>
 * <p>
 * <b>EbeanServer has more API than Ebean</b><br/>
 * EbeanServer provides additional API compared with Ebean. For example it
 * provides more control over the use of Transactions that is not available in
 * the Ebean API.
 * </p>
 * <p>
 * <em>External Transactions:</em> If you wanted to use transactions created
 * externally to eBean then EbeanServer provides additional methods where you
 * can explicitly pass a transaction (that can be created externally).
 * </p>
 * <p>
 * <em>Bypass ThreadLocal Mechanism:</em> If you want to bypass the built in
 * ThreadLocal transaction management you can use the createTransaction()
 * method. Example: a single thread requires more than one transaction.
 * </p>
 *
 * @see Ebean
 * @see EbeanServerFactory
 * @see ServerConfig
 */
public interface EbeanServer {

  /**
   * Shutdown the EbeanServer programmatically.
   * <p>
   * This method is not normally required. Ebean registers a shutdown hook and shuts down cleanly.
   * </p>
   * <p>
   * If the under underlying DataSource is the Ebean implementation then you
   * also have the option of shutting down the DataSource and deregistering the
   * JDBC driver.
   * </p>
   *
   * @param shutdownDataSource if true then shutdown the underlying DataSource if it is the EbeanORM
   *                           DataSource implementation.
   * @param deregisterDriver   if true then deregister the JDBC driver if it is the EbeanORM
   *                           DataSource implementation.
   */
  void shutdown(boolean shutdownDataSource, boolean deregisterDriver);

  /**
   * Return AutoTune which is used to control the AutoTune service at runtime.
   */
  AutoTune getAutoTune();

  /**
   * Return the name. This is used with {@link Ebean#getServer(String)} to get a
   * EbeanServer that was registered with the Ebean singleton.
   */
  String getName();

  /**
   * Return the ExpressionFactory for this server.
   */
  ExpressionFactory getExpressionFactory();

  /**
   * Return the MetaInfoManager which is used to get meta data from the EbeanServer
   * such as query execution statistics.
   */
  MetaInfoManager getMetaInfoManager();

  /**
   * Return the extended API intended for use by plugins.
   */
  SpiServer getPluginApi();

  /**
   * Return the BeanState for a given entity bean.
   * <p>
   * This will return null if the bean is not an enhanced entity bean.
   * </p>
   */
  BeanState getBeanState(Object bean);

  /**
   * Return the value of the Id property for a given bean.
   */
  Object getBeanId(Object bean);

  /**
   * Set the Id value onto the bean converting the type of the id value if necessary.
   * <p>
   * For example, if the id value passed in is a String but ought to be a Long or UUID etc
   * then it will automatically be converted.
   * </p>
   *
   * @param bean The entity bean to set the id value on.
   * @param id   The id value to set.
   */
  Object setBeanId(Object bean, Object id);

  /**
   * Return a map of the differences between two objects of the same type.
   * <p>
   * When null is passed in for b, then the 'OldValues' of a is used for the
   * difference comparison.
   * </p>
   */
  Map<String, ValuePair> diff(Object newBean, Object oldBean);

  /**
   * Create a new instance of T that is an EntityBean.
   * <p>
   * Useful if you use BeanPostConstructListeners or &#64;PostConstruct Annotations.
   * In this case you should not use "new Bean...()". Making all bean construtors protected
   * could be a good idea here. 
   * </p>
   */
  <T> T createEntityBean(Class<T> type);

  /**
   * Create a CsvReader for a given beanType.
   */
  <T> CsvReader<T> createCsvReader(Class<T> beanType);

  /**
   * Create an Update query to perform a bulk update.
   * <p>
   * <pre>{@code
   *
   *  int rows = ebeanServer
   *      .update(Customer.class)
   *      .set("status", Customer.Status.ACTIVE)
   *      .set("updtime", new Timestamp(System.currentTimeMillis()))
   *      .where()
   *        .gt("id", 1000)
   *        .update();
   *
   * }</pre>
   *
   * @param beanType The type of entity bean to update
   * @param <T>      The type of entity bean
   * @return The update query to use
   */
  <T> UpdateQuery<T> update(Class<T> beanType);

  /**
   * Create a named query.
   * <p>
   * For RawSql the named query is expected to be in ebean.xml.
   * </p>
   *
   * @param beanType   The type of entity bean
   * @param namedQuery The name of the query
   * @param <T>        The type of entity bean
   * @return The query
   */
  <T> Query<T> createNamedQuery(Class<T> beanType, String namedQuery);

  /**
   * Create a query for an entity bean and synonym for {@link #find(Class)}.
   *
   * @see #find(Class)
   */
  <T> Query<T> createQuery(Class<T> beanType);

  /**
   * Parse the Ebean query language statement returning the query which can then
   * be modified (add expressions, change order by clause, change maxRows, change
   * fetch and select paths etc).
   *
   * <h3>Example</h3>
   *
   * <pre>{@code
   *
   *   // Find order additionally fetching the customer, details and details.product name.
   *
   *   String eql = "fetch customer fetch details fetch details.product (name) where id = :orderId ";
   *
   *   Query<Order> query = Ebean.createQuery(Order.class, eql);
   *   query.setParameter("orderId", 2);
   *
   *   Order order = query.findUnique();
   *
   *   // This is the same as:
   *
   *   Order order = Ebean.find(Order.class)
   *     .fetch("customer")
   *     .fetch("details")
   *     .fetch("detail.product", "name")
   *     .setId(2)
   *     .findUnique();
   *
   * }</pre>
   *
   * @param beanType The type of bean to fetch
   * @param eql The Ebean query
   * @param <T> The type of the entity bean
   *
   * @return The query with expressions defined as per the parsed query statement
   */
  <T> Query<T> createQuery(Class<T> beanType, String eql);

  /**
   * Create a query for a type of entity bean.
   * <p>
   * You can use the methods on the Query object to specify fetch paths,
   * predicates, order by, limits etc.
   * </p>
   * <p>
   * You then use findList(), findSet(), findMap() and findUnique() to execute
   * the query and return the collection or bean.
   * </p>
   * <p>
   * Note that a query executed by {@link Query#findList()}
   * {@link Query#findSet()} etc will execute against the same EbeanServer from
   * which is was created.
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // Find order 2 specifying explicitly the parts of the object graph to
   *   // eagerly fetch. In this case eagerly fetch the associated customer,
   *   // details and details.product.name
   *
   *   Order order = ebeanServer.find(Order.class)
   *     .fetch("customer")
   *     .fetch("details")
   *     .fetch("detail.product", "name")
   *     .setId(2)
   *     .findUnique();
   *
   *   // find some new orders ... with firstRow/maxRows
   *   List<Order> orders =
   *     ebeanServer.find(Order.class)
   *       .where().eq("status", Order.Status.NEW)
   *       .setFirstRow(20)
   *       .setMaxRows(10)
   *       .findList();
   *
   * }</pre>
   */
  <T> Query<T> find(Class<T> beanType);

  /**
   * Return the next unique identity value for a given bean type.
   * <p>
   * This will only work when a IdGenerator is on the bean such as for beans
   * that use a DB sequence or UUID.
   * </p>
   * <p>
   * For DB's supporting getGeneratedKeys and sequences such as Oracle10 you do
   * not need to use this method generally. It is made available for more
   * complex cases where it is useful to get an ID prior to some processing.
   * </p>
   */
  Object nextId(Class<?> beanType);

  /**
   * Create a filter for sorting and filtering lists of entities locally without
   * going back to the database.
   * <p>
   * This produces and returns a new list with the sort and filters applied.
   * </p>
   * <p>
   * Refer to {@link Filter} for an example of its use.
   * </p>
   */
  <T> Filter<T> filter(Class<T> beanType);

  /**
   * Sort the list in memory using the sortByClause which can contain a comma delimited
   * list of property names and keywords asc, desc, nullsHigh and nullsLow.
   * <ul>
   * <li>asc - ascending order (which is the default)</li>
   * <li>desc - Descending order</li>
   * <li>nullsHigh - Treat null values as high/large values (which is the
   * default)</li>
   * <li>nullsLow- Treat null values as low/very small values</li>
   * </ul>
   * <p>
   * If you leave off any keywords the defaults are ascending order and treating
   * nulls as high values.
   * </p>
   * <p>
   * Note that the sorting uses a Comparator and Collections.sort(); and does
   * not invoke a DB query.
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // find orders and their customers
   *   List<Order> list = ebeanServer.find(Order.class)
   *     .fetch("customer")
   *     .orderBy("id")
   *     .findList();
   *
   *   // sort by customer name ascending, then by order shipDate
   *   // ... then by the order status descending
   *   ebeanServer.sort(list, "customer.name, shipDate, status desc");
   *
   *   // sort by customer name descending (with nulls low)
   *   // ... then by the order id
   *   ebeanServer.sort(list, "customer.name desc nullsLow, id");
   *
   * }</pre>
   *
   * @param list         the list of entity beans
   * @param sortByClause the properties to sort the list by
   */
  <T> void sort(List<T> list, String sortByClause);

  /**
   * Create a orm update where you will supply the insert/update or delete
   * statement (rather than using a named one that is already defined using the
   * &#064;NamedUpdates annotation).
   * <p>
   * The orm update differs from the sql update in that it you can use the bean
   * name and bean property names rather than table and column names.
   * </p>
   * <p>
   * An example:
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // The bean name and properties - "topic","postCount" and "id"
   *
   *   // will be converted into their associated table and column names
   *   String updStatement = "update topic set postCount = :pc where id = :id";
   *
   *   Update<Topic> update = ebeanServer.createUpdate(Topic.class, updStatement);
   *
   *   update.set("pc", 9);
   *   update.set("id", 3);
   *
   *   int rows = update.execute();
   *   System.out.println("rows updated:" + rows);
   *
   * }</pre>
   */
  <T> Update<T> createUpdate(Class<T> beanType, String ormUpdate);

  /**
   * Create a SqlQuery for executing native sql
   * query statements.
   * <p>
   * Note that you can use raw SQL with entity beans, refer to the SqlSelect
   * annotation for examples.
   * </p>
   */
  SqlQuery createSqlQuery(String sql);

  /**
   * Create a sql update for executing native dml statements.
   * <p>
   * Use this to execute a Insert Update or Delete statement. The statement will
   * be native to the database and contain database table and column names.
   * </p>
   * <p>
   * See {@link SqlUpdate} for example usage.
   * </p>
   */
  SqlUpdate createSqlUpdate(String sql);

  /**
   * Create a CallableSql to execute a given stored procedure.
   */
  CallableSql createCallableSql(String callableSql);

  /**
   * Register a TransactionCallback on the currently active transaction.
   * <p/>
   * If there is no currently active transaction then a PersistenceException is thrown.
   *
   * @param transactionCallback The transaction callback to be registered with the current transaction.
   * @throws PersistenceException If there is no currently active transaction
   */
  void register(TransactionCallback transactionCallback) throws PersistenceException;

  /**
   * Create a new transaction that is not held in TransactionThreadLocal.
   * <p>
   * You will want to do this if you want multiple Transactions in a single
   * thread or generally use transactions outside of the TransactionThreadLocal
   * management.
   * </p>
   */
  Transaction createTransaction();

  /**
   * Create a new transaction additionally specifying the isolation level.
   * <p>
   * Note that this transaction is NOT stored in a thread local.
   * </p>
   */
  Transaction createTransaction(TxIsolation isolation);

  /**
   * Start a transaction with 'REQUIRED' semantics.
   * <p>
   * With REQUIRED semantics if an active transaction already exists that transaction will be used.
   * </p>
   * <p>
   * The transaction is stored in a ThreadLocal variable and typically you only
   * need to use the returned Transaction <em>IF</em> you wish to do things like
   * use batch mode, change the transaction isolation level, use savepoints or
   * log comments to the transaction log.
   * </p>
   * <p>
   * Example of using a transaction to span multiple calls to find(), save()
   * etc.
   * </p>
   * <p>
   * <pre>{@code
   *
   *    // start a transaction (stored in a ThreadLocal)
   *    ebeanServer.beginTransaction();
   *    try {
   * 	    Order order = ebeanServer.find(Order.class,10);
   *
   * 	    ebeanServer.save(order);
   *
   * 	    ebeanServer.commitTransaction();
   *
   *    } finally {
   * 	    // rollback if we didn't commit
   * 	    // i.e. an exception occurred before commitTransaction().
   * 	    ebeanServer.endTransaction();
   *    }
   *
   * }</pre>
   * <p>
   * <h3>Transaction options:</h3>
   * <pre>{@code
   *
   *     Transaction txn = ebeanServer.beginTransaction();
   *     try {
   *       // explicitly turn on/off JDBC batch use
   *       txn.setBatchMode(true);
   *       txn.setBatchSize(50);
   *
   *       // control flushing when mixing save and queries
   *       txn.setBatchFlushOnQuery(false);
   *
   *       // turn off persist cascade if needed
   *       txn.setPersistCascade(false);
   *
   *       // for large batch insert processing when we do not
   *       // ... need the generatedKeys, don't get them
   *       txn.setBatchGetGeneratedKeys(false);
   *
   *       // explicitly flush the JDBC batch buffer
   *       txn.flushBatch();
   *
   *       ...
   *
   *       txn.commit();
   *
   *    } finally {
   *       // rollback if necessary
   *       txn.end();
   *    }
   *
   * }</pre>
   * <p>
   * <p>
   * If you want to externalise the transaction management then you use
   * createTransaction() and pass the transaction around to the various methods on
   * EbeanServer yourself.
   * </p>
   */
  Transaction beginTransaction();

  /**
   * Start a transaction additionally specifying the isolation level.
   */
  Transaction beginTransaction(TxIsolation isolation);

  /**
   * Start a transaction typically specifying REQUIRES_NEW or REQUIRED semantics.
   * <p>
   * <p>
   * Note that this provides an try finally alternative to using {@link #execute(TxScope, TxCallable)} or
   * {@link #execute(TxScope, TxRunnable)}.
   * </p>
   * <p>
   * <h3>REQUIRES_NEW example:</h3>
   * <pre>{@code
   * // Start a new transaction. If there is a current transaction
   * // suspend it until this transaction ends
   * Transaction txn = server.beginTransaction(TxScope.requiresNew());
   * try {
   *
   *   ...
   *
   *   // commit the transaction
   *   txn.commit();
   *
   * } finally {
   *   // end this transaction which:
   *   //  A) will rollback transaction if it has not been committed already
   *   //  B) will restore a previously suspended transaction
   *   txn.end();
   * }
   *
   * }</pre>
   * <p>
   * <h3>REQUIRED example:</h3>
   * <pre>{@code
   *
   * // start a new transaction if there is not a current transaction
   * Transaction txn = server.beginTransaction(TxScope.required());
   * try {
   *
   *   ...
   *
   *   // commit the transaction if it was created or
   *   // do nothing if there was already a current transaction
   *   txn.commit();
   *
   * } finally {
   *   // end this transaction which will rollback the transaction
   *   // if it was created for this try finally scope and has not
   *   // already been committed
   *   txn.end();
   * }
   *
   * }</pre>
   */
  Transaction beginTransaction(TxScope scope);

  /**
   * Returns the current transaction or null if there is no current transaction in scope.
   */
  Transaction currentTransaction();

  /**
   * Commit the current transaction.
   */
  void commitTransaction();

  /**
   * Rollback the current transaction.
   */
  void rollbackTransaction();

  /**
   * If the current transaction has already been committed do nothing otherwise
   * rollback the transaction.
   * <p>
   * Useful to put in a finally block to ensure the transaction is ended, rather
   * than a rollbackTransaction() in each catch block.
   * </p>
   * <p>
   * Code example:
   * <p>
   * <pre>{@code
   *
   *   ebeanServer.beginTransaction();
   *   try {
   *     // do some fetching and or persisting ...
   *
   *     // commit at the end
   *     ebeanServer.commitTransaction();
   *
   *   } finally {
   *     // if commit didn't occur then rollback the transaction
   *     ebeanServer.endTransaction();
   *   }
   *
   * }</pre>
   * <p>
   * </p>
   */
  void endTransaction();

  /**
   * Refresh the values of a bean.
   * <p>
   * Note that this resets OneToMany and ManyToMany properties so that if they
   * are accessed a lazy load will refresh the many property.
   * </p>
   */
  void refresh(Object bean);

  /**
   * Refresh a many property of an entity bean.
   *
   * @param bean         the entity bean containing the 'many' property
   * @param propertyName the 'many' property to be refreshed
   */
  void refreshMany(Object bean, String propertyName);

  /**
   * Find a bean using its unique id.
   * <p>
   * <pre>{@code
   *   // Fetch order 1
   *   Order order = ebeanServer.find(Order.class, 1);
   * }</pre>
   * <p>
   * <p>
   * If you want more control over the query then you can use createQuery() and
   * Query.findUnique();
   * </p>
   * <p>
   * <pre>{@code
   *   // ... additionally fetching customer, customer shipping address,
   *   // order details, and the product associated with each order detail.
   *   // note: only product id and name is fetch (its a "partial object").
   *   // note: all other objects use "*" and have all their properties fetched.
   *
   *   Query<Order> query = ebeanServer.find(Order.class)
   *     .setId(1)
   *     .fetch("customer")
   *     .fetch("customer.shippingAddress")
   *     .fetch("details")
   *     .query();
   *
   *   // fetch associated products but only fetch their product id and name
   *   query.fetch("details.product", "name");
   *
   *
   *   Order order = query.findUnique();
   *
   *   // traverse the object graph...
   *
   *   Customer customer = order.getCustomer();
   *   Address shippingAddress = customer.getShippingAddress();
   *   List<OrderDetail> details = order.getDetails();
   *   OrderDetail detail0 = details.get(0);
   *   Product product = detail0.getProduct();
   *   String productName = product.getName();
   *
   * }</pre>
   *
   * @param beanType the type of entity bean to fetch
   * @param id       the id value
   */
  <T> T find(Class<T> beanType, Object id);

  /**
   * Get a reference object.
   * <p>
   * This will not perform a query against the database unless some property other
   * that the id property is accessed.
   * </p>
   * <p>
   * It is most commonly used to set a 'foreign key' on another bean like:
   * </p>
   * <pre>{@code
   *
   *   Product product = ebeanServer.getReference(Product.class, 1);
   *
   *   OrderDetail orderDetail = new OrderDetail();
   *   // set the product 'foreign key'
   *   orderDetail.setProduct(product);
   *   orderDetail.setQuantity(42);
   *   ...
   *
   *   ebeanServer.save(orderDetail);
   *
   *
   * }</pre>
   * <p>
   * <h3>Lazy loading characteristics</h3>
   * <pre>{@code
   *
   *   Product product = ebeanServer.getReference(Product.class, 1);
   *
   *   // You can get the id without causing a fetch/lazy load
   *   Long productId = product.getId();
   *
   *   // If you try to get any other property a fetch/lazy loading will occur
   *   // This will cause a query to execute...
   *   String name = product.getName();
   *
   * }</pre>
   *
   * @param beanType the type of entity bean
   * @param id       the id value
   */
  <T> T getReference(Class<T> beanType, Object id);

  /**
   * Return the number of 'top level' or 'root' entities this query should return.
   *
   * @see Query#findCount()
   * @see Query#findFutureCount()
   */
  <T> int findCount(Query<T> query, Transaction transaction);

  /**
   * Return the Id values of the query as a List.
   *
   * @see Query#findIds()
   */
  <A> List<A> findIds(Query<?> query, Transaction transaction);

  /**
   * Return a QueryIterator for the query.
   * <p>
   * Generally using {@link #findEach(Query, QueryEachConsumer, Transaction)} or
   * {@link #findEachWhile(Query, QueryEachWhileConsumer, Transaction)} is preferred
   * to findIterate(). The reason is that those methods automatically take care of
   * closing the queryIterator (and the underlying jdbc statement and resultSet).
   * </p>
   * <p>
   * This is similar to findEach in that not all the result beans need to be held
   * in memory at the same time and as such is good for processing large queries.
   * </p>
   *
   * @see Query#findIterate()
   * @see Query#findEach(QueryEachConsumer)
   * @see Query#findEachWhile(QueryEachWhileConsumer)
   */
  <T> QueryIterator<T> findIterate(Query<T> query, Transaction transaction);

  /**
   * Execute the query visiting the each bean one at a time.
   * <p>
   * Unlike findList() this is suitable for processing a query that will return
   * a very large resultSet. The reason is that not all the result beans need to be
   * held in memory at the same time and instead processed one at a time.
   * </p>
   * <p>
   * Internally this query using a PersistenceContext scoped to each bean (and the
   * beans associated object graph).
   * </p>
   * <p>
   * <pre>{@code
   *
   *     ebeanServer.find(Order.class)
   *       .where().eq("status", Order.Status.NEW)
   *       .order().asc("id")
   *       .findEach((Order order) -> {
   *
   *         // do something with the order bean
   *         System.out.println(" -- processing order ... " + order);
   *       });
   *
   * }</pre>
   *
   * @see Query#findEach(QueryEachConsumer)
   * @see Query#findEachWhile(QueryEachWhileConsumer)
   */
  <T> void findEach(Query<T> query, QueryEachConsumer<T> consumer, Transaction transaction);

  /**
   * Execute the query visiting the each bean one at a time.
   * <p>
   * Compared to findEach() this provides the ability to stop processing the query
   * results early by returning false for the QueryEachWhileConsumer.
   * </p>
   * <p>
   * Unlike findList() this is suitable for processing a query that will return
   * a very large resultSet. The reason is that not all the result beans need to be
   * held in memory at the same time and instead processed one at a time.
   * </p>
   * <p>
   * Internally this query using a PersistenceContext scoped to each bean (and the
   * beans associated object graph).
   * </p>
   * <p>
   * <pre>{@code
   *
   *     ebeanServer.find(Order.class)
   *       .where().eq("status", Order.Status.NEW)
   *       .order().asc("id")
   *       .findEachWhile((Order order) -> {
   *
   *         // do something with the order bean
   *         System.out.println(" -- processing order ... " + order);
   *
   *         boolean carryOnProcessing = ...
   *         return carryOnProcessing;
   *       });
   *
   * }</pre>
   *
   * @see Query#findEach(QueryEachConsumer)
   * @see Query#findEachWhile(QueryEachWhileConsumer)
   */
  <T> void findEachWhile(Query<T> query, QueryEachWhileConsumer<T> consumer, Transaction transaction);

  /**
   * Return versions of a @History entity bean.
   * <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>
   */
  <T> List<Version<T>> findVersions(Query<T> query, Transaction transaction);

  /**
   * Execute a query returning a list of beans.
   * <p>
   * Generally you are able to use {@link Query#findList()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   * <p>
   * <pre>{@code
   *
   * List<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .where().ilike("name", "rob%")
   *     .findList();
   *
   * }</pre>
   *
   * @param <T>         the type of entity bean to fetch.
   * @param query       the query to execute.
   * @param transaction the transaction to use (can be null).
   * @return the list of fetched beans.
   * @see Query#findList()
   */
  <T> List<T> findList(Query<T> query, Transaction transaction);

  /**
   * 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>
   *
   * @param query       the query to execute the row count on
   * @param transaction the transaction (can be null).
   * @return a Future object for the row count query
   * @see com.avaje.ebean.Query#findFutureCount()
   */
  <T> FutureRowCount<T> findFutureCount(Query<T> query, Transaction transaction);

  /**
   * 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>
   *
   * @param query       the query to execute the fetch Id's on
   * @param transaction the transaction (can be null).
   * @return a Future object for the list of Id's
   * @see com.avaje.ebean.Query#findFutureIds()
   */
  <T> FutureIds<T> findFutureIds(Query<T> query, Transaction transaction);

  /**
   * Execute find list query in a background thread returning a FutureList object.
   * <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>
   * 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.
   *
   * @param query       the query to execute in the background
   * @param transaction the transaction (can be null).
   * @return a Future object for the list result of the query
   * @see Query#findFutureList()
   */
  <T> FutureList<T> findFutureList(Query<T> query, Transaction transaction);

  /**
   * 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>
   * <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
   * @see Query#findPagedList()
   */
  <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction);

  /**
   * Execute the query returning a set of entity beans.
   * <p>
   * Generally you are able to use {@link Query#findSet()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   * <p>
   * <pre>{@code
   *
   * Set<Customer> customers =
   *     ebeanServer.find(Customer.class)
   *     .where().ilike("name", "rob%")
   *     .findSet();
   *
   * }</pre>
   *
   * @param <T>         the type of entity bean to fetch.
   * @param query       the query to execute
   * @param transaction the transaction to use (can be null).
   * @return the set of fetched beans.
   * @see Query#findSet()
   */
  <T> Set<T> findSet(Query<T> query, Transaction transaction);

  /**
   * Execute the query returning the entity beans in a Map.
   * <p>
   * Generally you are able to use {@link Query#findMap()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   *
   * @param <T>         the type of entity bean to fetch.
   * @param query       the query to execute.
   * @param transaction the transaction to use (can be null).
   * @return the map of fetched beans.
   * @see Query#findMap()
   */
  <K, T> Map<K, T> findMap(Query<T> query, Transaction transaction);

  /**
   * 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
   *
   * @see Query#findSingleAttributeList()
   */
  <A> List<A> findSingleAttributeList(Query<?> query, Transaction transaction);

  /**
   * Execute the query returning at most one entity bean or null (if no matching
   * bean is found).
   * <p>
   * This will throw a NonUniqueResultException if the query finds more than one result.
   * </p>
   * <p>
   * Generally you are able to use {@link Query#findUnique()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   *
   * @param <T>         the type of entity bean to fetch.
   * @param query       the query to execute.
   * @param transaction the transaction to use (can be null).
   * @return the list of fetched beans.
   * @throws NonUniqueResultException if more than one result was found
   * @see Query#findUnique()
   */
  @Nullable
  <T> T findUnique(Query<T> query, Transaction transaction);

  /**
   * 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>
   *
   * @param query       the query used for the delete
   * @param transaction the transaction to use (can be null)
   * @param <T>         the type of entity bean to fetch.
   * @return the number of beans/rows that were deleted
   */
  <T> int delete(Query<T> query, Transaction transaction);

  /**
   * Execute the update query returning the number of rows updated.
   * <p>
   * The update query must be created using {@link #update(Class)}.
   * </p>
   *
   * @param query       the update query to execute
   * @param transaction the optional transaction to use for the update (can be null)
   * @param <T>         the type of entity bean
   * @return The number of rows updated
   */
  <T> int update(Query<T> query, Transaction transaction);

  /**
   * Execute the sql query returning a list of MapBean.
   * <p>
   * Generally you are able to use {@link SqlQuery#findList()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   *
   * @param query       the query to execute.
   * @param transaction the transaction to use (can be null).
   * @return the list of fetched MapBean.
   * @see SqlQuery#findList()
   */
  List<SqlRow> findList(SqlQuery query, Transaction transaction);

  /**
   * Execute the SqlQuery iterating a row at a time.
   * <p>
   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
   * </p>
   */
  void findEach(SqlQuery query, QueryEachConsumer<SqlRow> consumer, Transaction transaction);

  /**
   * Execute the SqlQuery iterating a row at a time with the ability to stop consuming part way through.
   * <p>
   * Returning false after processing a row stops the iteration through the query results.
   * </p>
   * <p>
   * This streaming type query is useful for large query execution as only 1 row needs to be held in memory.
   * </p>
   */
  void findEachWhile(SqlQuery query, QueryEachWhileConsumer<SqlRow> consumer, Transaction transaction);

  /**
   * Execute the sql query returning a single MapBean or null.
   * <p>
   * This will throw a PersistenceException if the query found more than one
   * result.
   * </p>
   * <p>
   * Generally you are able to use {@link SqlQuery#findUnique()} rather than
   * explicitly calling this method. You could use this method if you wish to
   * explicitly control the transaction used for the query.
   * </p>
   *
   * @param query       the query to execute.
   * @param transaction the transaction to use (can be null).
   * @return the fetched MapBean or null if none was found.
   * @see SqlQuery#findUnique()
   */
  @Nullable
  SqlRow findUnique(SqlQuery query, Transaction transaction);

  /**
   * Either Insert or Update the bean depending on its state.
   * <p>
   * If there is no current transaction one will be created and committed for
   * you automatically.
   * </p>
   * <p>
   * Save can cascade along relationships. For this to happen you need to
   * specify a cascade of CascadeType.ALL or CascadeType.PERSIST on the
   * OneToMany, OneToOne or ManyToMany annotation.
   * </p>
   * <p>
   * In this example below the details property has a CascadeType.ALL set so
   * saving an order will also save all its details.
   * </p>
   * <p>
   * <pre>{@code
   *   public class Order { ...
   *
   * 	   @OneToMany(cascade=CascadeType.ALL, mappedBy="order")
   * 	   List<OrderDetail> details;
   * 	   ...
   *   }
   * }</pre>
   * <p>
   * <p>
   * When a save cascades via a OneToMany or ManyToMany Ebean will automatically
   * set the 'parent' object to the 'detail' object. In the example below in
   * saving the order and cascade saving the order details the 'parent' order
   * will be set against each order detail when it is saved.
   * </p>
   */
  void save(Object bean) throws OptimisticLockException;

  /**
   * Save all the beans in the collection.
   */
  int saveAll(Collection<?> beans) throws OptimisticLockException;

  /**
   * Delete the bean.
   * <p>
   * This will return true if the bean was deleted successfully or JDBC batch is being used.
   * </p>
   * <p>
   * If there is no current transaction one will be created and committed for
   * you automatically.
   * </p>
   * <p>
   * If the Bean does not have a version property (or loaded version property) and
   * the bean does not exist then this returns false indicating that nothing was
   * deleted. Note that, if JDBC batch mode is used then this always returns true.
   * </p>
   */
  boolean delete(Object bean) throws OptimisticLockException;

  /**
   * Delete the bean with an explicit transaction.
   * <p>
   * This will return true if the bean was deleted successfully or JDBC batch is being used.
   * </p>
   * <p>
   * If the Bean does not have a version property (or loaded version property) and
   * the bean does not exist then this returns false indicating that nothing was
   * deleted. However, if JDBC batch mode is used then this always returns true.
   * </p>
   */
  boolean delete(Object bean, Transaction transaction) throws OptimisticLockException;

  /**
   * Delete a bean permanently without soft delete.
   */
  boolean deletePermanent(Object bean) throws OptimisticLockException;

  /**
   * Delete a bean permanently without soft delete using an explicit transaction.
   */
  boolean deletePermanent(Object bean, Transaction transaction) throws OptimisticLockException;

  /**
   * Delete all the beans in the collection permanently without soft delete.
   */
  int deleteAllPermanent(Collection<?> beans) throws OptimisticLockException;

  /**
   * Delete all the beans in the collection permanently without soft delete using an explicit transaction.
   */
  int deleteAllPermanent(Collection<?> beans, Transaction transaction) throws OptimisticLockException;

  /**
   * Delete the bean given its type and id.
   */
  int delete(Class<?> beanType, Object id);

  /**
   * Delete the bean given its type and id with an explicit transaction.
   */
  int delete(Class<?> beanType, Object id, Transaction transaction);

  /**
   * Delete permanent given the bean type and id.
   */
  int deletePermanent(Class<?> beanType, Object id);

  /**
   * Delete permanent given the bean type and id with an explicit transaction.
   */
  int deletePermanent(Class<?> beanType, Object id, Transaction transaction);

  /**
   * Delete all the beans in the collection.
   */
  int deleteAll(Collection<?> beans) throws OptimisticLockException;

  /**
   * Delete all the beans in the collection using an explicit transaction.
   */
  int deleteAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;

  /**
   * Delete several beans given their type and id values.
   */
  int deleteAll(Class<?> beanType, Collection<?> ids);

  /**
   * Delete several beans given their type and id values with an explicit transaction.
   */
  int deleteAll(Class<?> beanType, Collection<?> ids, Transaction transaction);

  /**
   * Delete permanent for several beans given their type and id values.
   */
  int deleteAllPermanent(Class<?> beanType, Collection<?> ids);

  /**
   * Delete permanent for several beans given their type and id values with an explicit transaction.
   */
  int deleteAllPermanent(Class<?> beanType, Collection<?> ids, Transaction transaction);

  /**
   * Execute a Sql Update Delete or Insert statement. This returns the number of
   * rows that where updated, deleted or inserted. If is executed in batch then
   * this returns -1. You can get the actual rowCount after commit() from
   * updateSql.getRowCount().
   * <p>
   * If you wish to execute a Sql Select natively then you should use the
   * FindByNativeSql object.
   * </p>
   * <p>
   * Note that the table modification information is automatically deduced and
   * you do not need to call the Ebean.externalModification() method when you
   * use this method.
   * </p>
   * <p>
   * Example:
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // example that uses 'named' parameters
   *   String s = "UPDATE f_topic set post_count = :count where id = :id"
   *
   *   SqlUpdate update = ebeanServer.createSqlUpdate(s);
   *
   *   update.setParameter("id", 1);
   *   update.setParameter("count", 50);
   *
   *   int modifiedCount = ebeanServer.execute(update);
   *
   *   String msg = "There where " + modifiedCount + "rows updated";
   *
   * }</pre>
   *
   * @param sqlUpdate the update sql potentially with bind values
   * @return the number of rows updated or deleted. -1 if executed in batch.
   * @see CallableSql
   */
  int execute(SqlUpdate sqlUpdate);

  /**
   * Execute a ORM insert update or delete statement using the current
   * transaction.
   * <p>
   * This returns the number of rows that where inserted, updated or deleted.
   * </p>
   */
  int execute(Update<?> update);

  /**
   * Execute a ORM insert update or delete statement with an explicit
   * transaction.
   */
  int execute(Update<?> update, Transaction transaction);

  /**
   * For making calls to stored procedures.
   * <p>
   * Example:
   * </p>
   * <p>
   * <pre>{@code
   *
   *   String sql = "{call sp_order_modify(?,?,?)}";
   *
   *   CallableSql cs = ebeanServer.createCallableSql(sql);
   *   cs.setParameter(1, 27);
   *   cs.setParameter(2, "SHIPPED");
   *   cs.registerOut(3, Types.INTEGER);
   *
   *   ebeanServer.execute(cs);
   *
   *   // read the out parameter
   *   Integer returnValue = (Integer) cs.getObject(3);
   *
   * }</pre>
   *
   * @see CallableSql
   * @see Ebean#execute(SqlUpdate)
   */
  int execute(CallableSql callableSql);

  /**
   * Inform Ebean that tables have been modified externally. These could be the
   * result of from calling a stored procedure, other JDBC calls or external
   * programs including other frameworks.
   * <p>
   * If you use ebeanServer.execute(UpdateSql) then the table modification information
   * is automatically deduced and you do not need to call this method yourself.
   * </p>
   * <p>
   * This information is used to invalidate objects out of the cache and
   * potentially text indexes. This information is also automatically broadcast
   * across the cluster.
   * </p>
   * <p>
   * If there is a transaction then this information is placed into the current
   * transactions event information. When the transaction is committed this
   * information is registered (with the transaction manager). If this
   * transaction is rolled back then none of the transaction event information
   * registers including the information you put in via this method.
   * </p>
   * <p>
   * If there is NO current transaction when you call this method then this
   * information is registered immediately (with the transaction manager).
   * </p>
   *
   * @param tableName the name of the table that was modified
   * @param inserted  true if rows where inserted into the table
   * @param updated   true if rows on the table where updated
   * @param deleted   true if rows on the table where deleted
   */
  void externalModification(String tableName, boolean inserted, boolean updated, boolean deleted);

  /**
   * Find a entity bean with an explicit transaction.
   *
   * @param <T>         the type of entity bean to find
   * @param beanType    the type of entity bean to find
   * @param id          the bean id value
   * @param transaction the transaction to use (can be null)
   */
  <T> T find(Class<T> beanType, Object id, Transaction transaction);

  /**
   * Insert or update a bean with an explicit transaction.
   */
  void save(Object bean, Transaction transaction) throws OptimisticLockException;

  /**
   * Save all the beans in the collection with an explicit transaction.
   */
  int saveAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;

  /**
   * Marks the entity bean as dirty.
   * <p>
   * This is used so that when a bean that is otherwise unmodified is updated the version
   * property is updated.
   * <p>
   * An unmodified bean that is saved or updated is normally skipped and this marks the bean as
   * dirty so that it is not skipped.
   * <p>
   * <pre>{@code
   *
   * Customer customer = ebeanServer.find(Customer, id);
   *
   * // mark the bean as dirty so that a save() or update() will
   * // increment the version property
   * ebeanServer.markAsDirty(customer);
   * ebeanServer.save(customer);
   *
   * }</pre>
   */
  void markAsDirty(Object bean);

  /**
   * Saves the bean using an update. If you know you are updating a bean then it is preferable to
   * use this update() method rather than save().
   * <p>
   * <b>Stateless updates:</b> Note that the bean does not have to be previously fetched to call
   * update().You can create a new instance and set some of its properties programmatically for via
   * JSON/XML marshalling etc. This is described as a 'stateless update'.
   * </p>
   * <p>
   * <b>Optimistic Locking: </b> Note that if the version property is not set when update() is
   * called then no optimistic locking is performed (internally ConcurrencyMode.NONE is used).
   * </p>
   * <p>
   * <b>{@link ServerConfig#setUpdatesDeleteMissingChildren(boolean)}: </b> When cascade saving to a
   * OneToMany or ManyToMany the updatesDeleteMissingChildren setting controls if any other children
   * that are in the database but are not in the collection are deleted.
   * </p>
   * <p>
   * <b>{@link ServerConfig#setUpdateChangesOnly(boolean)}: </b> The updateChangesOnly setting
   * controls if only the changed properties are included in the update or if all the loaded
   * properties are included instead.
   * </p>
   * <p>
   * <pre>{@code
   *
   * // A 'stateless update' example
   * Customer customer = new Customer();
   * customer.setId(7);
   * customer.setName("ModifiedNameNoOCC");
   * ebeanServer.update(customer);
   *
   * }</pre>
   *
   * @see ServerConfig#setUpdatesDeleteMissingChildren(boolean)
   * @see ServerConfig#setUpdateChangesOnly(boolean)
   */
  void update(Object bean) throws OptimisticLockException;

  /**
   * Update a bean additionally specifying a transaction.
   */
  void update(Object bean, Transaction transaction) throws OptimisticLockException;

  /**
   * Update a bean additionally specifying a transaction and the deleteMissingChildren setting.
   *
   * @param bean                  the bean to update
   * @param transaction           the transaction to use (can be null).
   * @param deleteMissingChildren specify false if you do not want 'missing children' of a OneToMany
   *                              or ManyToMany to be automatically deleted.
   */
  void update(Object bean, Transaction transaction, boolean deleteMissingChildren) throws OptimisticLockException;

  /**
   * Update a collection of beans. If there is no current transaction one is created and used to
   * update all the beans in the collection.
   */
  void updateAll(Collection<?> beans) throws OptimisticLockException;

  /**
   * Update a collection of beans with an explicit transaction.
   */
  void updateAll(Collection<?> beans, Transaction transaction) throws OptimisticLockException;

  /**
   * Insert the bean.
   * <p>
   * Compared to save() this forces bean to perform an insert rather than trying to decide
   * based on the bean state. As such this is useful when you fetch beans from one database
   * and want to insert them into another database (and you want to explicitly insert them).
   * </p>
   */
  void insert(Object bean);

  /**
   * Insert the bean with a transaction.
   */
  void insert(Object bean, Transaction transaction);

  /**
   * Insert a collection of beans. If there is no current transaction one is created and used to
   * insert all the beans in the collection.
   */
  void insertAll(Collection<?> beans);

  /**
   * Insert a collection of beans with an explicit transaction.
   */
  void insertAll(Collection<?> beans, Transaction transaction);

  /**
   * Execute explicitly passing a transaction.
   */
  int execute(SqlUpdate updSql, Transaction transaction);

  /**
   * Execute explicitly passing a transaction.
   */
  int execute(CallableSql callableSql, Transaction transaction);

  /**
   * Execute a TxRunnable in a Transaction with an explicit scope.
   * <p>
   * The scope can control the transaction type, isolation and rollback
   * semantics.
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // set specific transactional scope settings
   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
   *
   *   ebeanServer.execute(scope, new TxRunnable() {
   * 	   public void run() {
   * 		   User u1 = Ebean.find(User.class, 1);
   * 		   ...
   * 	   }
   *   });
   *
   * }</pre>
   */
  void execute(TxScope scope, TxRunnable runnable);

  /**
   * Execute a TxRunnable in a Transaction with the default scope.
   * <p>
   * The default scope runs with REQUIRED and by default will rollback on any
   * exception (checked or runtime).
   * </p>
   * <p>
   * <pre>{@code
   *
   *    ebeanServer.execute(new TxRunnable() {
   *      public void run() {
   *        User u1 = ebeanServer.find(User.class, 1);
   *        User u2 = ebeanServer.find(User.class, 2);
   *
   *        u1.setName("u1 mod");
   *        u2.setName("u2 mod");
   *
   *        ebeanServer.save(u1);
   *        ebeanServer.save(u2);
   *      }
   *    });
   *
   * }</pre>
   */
  void execute(TxRunnable runnable);

  /**
   * Execute a TxCallable in a Transaction with an explicit scope.
   * <p>
   * The scope can control the transaction type, isolation and rollback
   * semantics.
   * </p>
   * <p>
   * <pre>{@code
   *
   *   // set specific transactional scope settings
   *   TxScope scope = TxScope.requiresNew().setIsolation(TxIsolation.SERIALIZABLE);
   *
   *   ebeanServer.execute(scope, new TxCallable<String>() {
   * 	   public String call() {
   * 		   User u1 = ebeanServer.find(User.class, 1);
   * 		   ...
   * 		   return u1.getEmail();
   * 	   }
   *   });
   *
   * }</pre>
   */
  <T> T execute(TxScope scope, TxCallable<T> callable);

  /**
   * Execute a TxCallable in a Transaction with the default scope.
   * <p>
   * The default scope runs with REQUIRED and by default will rollback on any
   * exception (checked or runtime).
   * </p>
   * <p>
   * This is basically the same as TxRunnable except that it returns an Object
   * (and you specify the return type via generics).
   * </p>
   * <p>
   * <pre>{@code
   *
   *   ebeanServer.execute(new TxCallable<String>() {
   *     public String call() {
   *       User u1 = ebeanServer.find(User.class, 1);
   *       User u2 = ebeanServer.find(User.class, 2);
   *
   *       u1.setName("u1 mod");
   *       u2.setName("u2 mod");
   *
   *       ebeanServer.save(u1);
   *       ebeanServer.save(u2);
   *
   *       return u1.getEmail();
   *     }
   *   });
   *
   * }</pre>
   */
  <T> T execute(TxCallable<T> callable);

  /**
   * Return the manager of the server cache ("L2" cache).
   */
  ServerCacheManager getServerCacheManager();

  /**
   * Return the BackgroundExecutor service for asynchronous processing of
   * queries.
   */
  BackgroundExecutor getBackgroundExecutor();

  /**
   * Return the JsonContext for reading/writing JSON.
   * <p>
   * This instance is safe to be used concurrently by multiple threads and this
   * method is cheap to call.
   * </p>
   * <p>
   * <h3>Simple example:</h3>
   * <pre>{@code
   *
   *     JsonContext json = ebeanServer.json();
   *     String jsonOutput = json.toJson(list);
   *     System.out.println(jsonOutput);
   *
   * }</pre>
   * <p>
   * <h3>Using PathProperties:</h3>
   * <pre>{@code
   *
   *     // specify just the properties we want
   *     PathProperties paths = PathProperties.parse("name, status, anniversary");
   *
   *     List<Customer> customers =
   *       ebeanServer.find(Customer.class)
   *         // apply those paths to the query (only fetch what we need)
   *         .apply(paths)
   *         .where().ilike("name", "rob%")
   *         .findList();
   *
   *     // ... get the json
   *     JsonContext jsonContext = ebeanServer.json();
   *     String json = jsonContext.toJson(customers, paths);
   *
   * }</pre>
   *
   * @see FetchPath
   * @see Query#apply(FetchPath)
   */
  JsonContext json();

  /**
   * Return the Document store.
   */
  DocumentStore docStore();

  /**
   * Publish a single bean given its type and id returning the resulting live bean.
   * <p>
   * The values are published from the draft to the live bean.
   * </p>
   *
   * @param <T>         the type of the entity bean
   * @param beanType    the type of the entity bean
   * @param id          the id of the entity bean
   * @param transaction the transaction the publish process should use (can be null)
   */
  <T> T publish(Class<T> beanType, Object id, Transaction transaction);

  /**
   * Publish a single bean given its type and id returning the resulting live bean.
   * This will use the current transaction or create one if required.
   * <p>
   * The values are published from the draft to the live bean.
   * </p>
   *
   * @param <T>      the type of the entity bean
   * @param beanType the type of the entity bean
   * @param id       the id of the entity bean
   */
  <T> T publish(Class<T> beanType, Object id);

  /**
   * Publish the beans that match the query returning the resulting published beans.
   * <p>
   * The values are published from the draft beans to the live beans.
   * </p>
   *
   * @param <T>         the type of the entity bean
   * @param query       the query used to select the draft beans to publish
   * @param transaction the transaction the publish process should use (can be null)
   */
  <T> List<T> publish(Query<T> query, Transaction transaction);

  /**
   * Publish the beans that match the query returning the resulting published beans.
   * This will use the current transaction or create one if required.
   * <p>
   * The values are published from the draft beans to the live beans.
   * </p>
   *
   * @param <T>   the type of the entity bean
   * @param query the query used to select the draft beans to publish
   */
  <T> List<T> publish(Query<T> query);

  /**
   * Restore the draft bean back to the live state.
   * <p>
   * The values from the live beans are set back to the draft bean and the
   * <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
   * </p>
   *
   * @param <T>         the type of the entity bean
   * @param beanType    the type of the entity bean
   * @param id          the id of the entity bean to restore
   * @param transaction the transaction the restore process should use (can be null)
   */
  <T> T draftRestore(Class<T> beanType, Object id, Transaction transaction);

  /**
   * Restore the draft bean back to the live state.
   * <p>
   * The values from the live beans are set back to the draft bean and the
   * <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
   * </p>
   *
   * @param <T>      the type of the entity bean
   * @param beanType the type of the entity bean
   * @param id       the id of the entity bean to restore
   */
  <T> T draftRestore(Class<T> beanType, Object id);

  /**
   * Restore the draft beans matching the query back to the live state.
   * <p>
   * The values from the live beans are set back to the draft bean and the
   * <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
   * </p>
   *
   * @param <T>         the type of the entity bean
   * @param query       the query used to select the draft beans to restore
   * @param transaction the transaction the restore process should use (can be null)
   */
  <T> List<T> draftRestore(Query<T> query, Transaction transaction);

  /**
   * Restore the draft beans matching the query back to the live state.
   * <p>
   * The values from the live beans are set back to the draft bean and the
   * <code>@DraftDirty</code> and <code>@DraftReset</code> properties are reset.
   * </p>
   *
   * @param <T>   the type of the entity bean
   * @param query the query used to select the draft beans to restore
   */
  <T> List<T> draftRestore(Query<T> query);

  /**
   * Returns the set of properties/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/path for the given bean type.
   * </p>
   */
  <T> Set<String> validateQuery(Query<T> query);
}
