diff --git a/ebean-api/src/main/java/io/ebean/Query.java b/ebean-api/src/main/java/io/ebean/Query.java index c48474ac7..f0d3c7d5a 100644 --- a/ebean-api/src/main/java/io/ebean/Query.java +++ b/ebean-api/src/main/java/io/ebean/Query.java @@ -1,20 +1,6 @@ package io.ebean; import io.avaje.lang.NonNullApi; -import io.avaje.lang.Nullable; - -import jakarta.persistence.NonUniqueResultException; -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.Timestamp; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Stream; /** * Object relational query for finding a List, Set, Map or single entity bean. @@ -181,7 +167,7 @@ import java.util.stream.Stream; * @param the type of Entity bean this query will fetch. */ @NonNullApi -public interface Query extends CancelableQuery { +public interface Query extends CancelableQuery, QueryBuilder, T> { /** * The lock type (strength) to use with query FOR UPDATE row locking. @@ -234,130 +220,6 @@ public interface Query extends CancelableQuery { SKIPLOCKED } - /** - * Set RawSql to use for this query. - */ - Query setRawSql(RawSql rawSql); - - /** - * Perform an 'As of' query using history tables to return the object graph - * as of a time in the past. - *

- * To perform this query the DB must have underlying history tables. - *

- * - * @param asOf the date time in the past at which you want to view the data - */ - Query asOf(Timestamp asOf); - - /** - * Execute the query against the draft set of tables. - */ - Query asDraft(); - - /** - * Convert the query to a DTO bean query. - *

- * We effectively use the underlying ORM query to build the SQL and then execute - * and map it into DTO beans. - */ - DtoQuery asDto(Class dtoClass); - - /** - * Convert the query to a UpdateQuery. - *

- * Typically this is used with query beans to covert a query bean - * query into an UpdateQuery like the examples below. - *

- * - *
{@code
-   *
-   *  int rowsUpdated = new QCustomer()
-   *       .name.startsWith("Rob")
-   *       .asUpdate()
-   *       .set("active", false)
-   *       .update();;
-   *
-   * }
- * - *
{@code
-   *
-   *   int rowsUpdated = new QContact()
-   *       .notes.note.startsWith("Make Inactive")
-   *       .email.endsWith("@foo.com")
-   *       .customer.id.equalTo(42)
-   *       .asUpdate()
-   *       .set("inactive", true)
-   *       .setRaw("email = lower(email)")
-   *       .update();
-   *
-   * }
- */ - UpdateQuery asUpdate(); - - /** - * Return a copy of the query. - *

- * 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. - *

- */ - Query copy(); - - /** - * Specify the PersistenceContextScope to use for this query. - *

- * When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)} - * is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}. - *

- * Note that the same persistence Context is used for subsequent lazy loading and query join queries. - *

- * 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 setPersistenceContextScope(PersistenceContextScope scope); - - /** - * Set the index(es) to search for a document store which uses partitions. - *

- * For example, when executing a query against ElasticSearch with daily indexes we can - * explicitly specify the indexes to search against. - *

- *
{@code
-   *
-   *   // explicitly specify the indexes to search
-   *   query.setDocIndexName("logstash-2016.11.5,logstash-2016.11.6")
-   *
-   *   // search today's index
-   *   query.setDocIndexName("$today")
-   *
-   *   // search the last 3 days
-   *   query.setDocIndexName("$last-3")
-   *
-   * }
- *

- * If the indexName is specified with ${daily} e.g. "logstash-${daily}" ... then we can use - * $today and $last-x as the search docIndexName like the examples below. - *

- *
{@code
-   *
-   *   // search today's index
-   *   query.setDocIndexName("$today")
-   *
-   *   // search the last 3 days
-   *   query.setDocIndexName("$last-3")
-   *
-   * }
- * - * @param indexName The index or indexes to search against - * @return This query - */ - Query setDocIndexName(String indexName); - /** * Return the ExpressionFactory used by this query. */ @@ -368,744 +230,11 @@ public interface Query extends CancelableQuery { */ boolean isAutoTuned(); - /** - * Explicitly specify whether to use AutoTune for this query. - *

- * 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. - *

- *

- * 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. - *

- */ - Query setAutoTune(boolean autoTune); - - /** - * Execute the query allowing properties with invalid JSON to be collected and not fail the query. - *
{@code
-   *
-   *   // fetch a bean with JSON content
-   *   EBasicJsonList bean= DB.find(EBasicJsonList.class)
-   *       .setId(42)
-   *       .setAllowLoadErrors()  // collect errors into bean state if we have invalid JSON
-   *       .findOne();
-   *
-   *
-   *   // get the invalid JSON errors from the bean state
-   *   Map errors = server().getBeanState(bean).getLoadErrors();
-   *
-   *   // If this map is not empty tell we have invalid JSON
-   *   // and should try and fix the JSON content or inform the user
-   *
-   * }
- */ - Query setAllowLoadErrors(); - - /** - * Set the default lazy loading batch size to use. - *

- * 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 setLazyLoadBatchSize(int lazyLoadBatchSize); - - /** - * Execute the query including soft deleted rows. - *

- * 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. - *

- */ - Query setIncludeSoftDeletes(); - - /** - * Disable read auditing for this query. - *

- * 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. - *

- */ - Query setDisableReadAuditing(); - - /** - * Specify the properties to fetch on the root level entity bean in comma delimited format. - *

- * The Id property is automatically included in the properties to fetch unless setDistinct(true) - * is set on the query. - *

- *

- * Use {@link #fetch(String, String)} to specify specific properties to fetch - * on other non-root level paths of the object graph. - *

- *
{@code
-   *
-   * List customers = DB.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();
-   *
-   * }
- * - * @param fetchProperties the properties to fetch for this bean (* = all properties). - */ - Query select(String fetchProperties); - - /** - * Apply the fetchGroup which defines what part of the object graph to load. - */ - Query select(FetchGroup fetchGroup); - - /** - * Specify a path to fetch eagerly including specific properties. - *

- * 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(). - *

- *
{@code
-   *
-   * // query orders...
-   * List orders = DB.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();
-   * }
- *

- * If columns is null or "*" then all columns/properties for that path are fetched. - *

- *
{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers = DB.find(Customer.class)
-   *     .select("name, status")
-   *     .fetch("contacts", "firstName,lastName,email")
-   *     .findList();
-   *
-   * }
- * - * @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 fetch(String path, String fetchProperties); - - /** - * Fetch the path and properties using a "query join" (separate SQL query). - *

- * This is the same as: - *

- *
{@code
-   *
-   *  fetch(path, fetchProperties, FetchConfig.ofQuery())
-   *
-   * }
- *

- * 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. - *

- *

- * 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. - *

- * - * @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 fetchQuery(String path, String fetchProperties); - - /** - * Fetch the path and properties using L2 bean cache. - * - * @param path The path of the beans we are fetching from L2 cache. - * @param fetchProperties The properties that should be loaded. - */ - Query fetchCache(String path, String fetchProperties); - - /** - * Fetch the path and properties lazily (via batch lazy loading). - *

- * This is the same as: - *

- *
{@code
-   *
-   *  fetch(path, fetchProperties, FetchConfig.ofLazy())
-   *
-   * }
- *

- * The reason for using fetchLazy() is to either: - *

- *
    - *
  • Control/tune what is fetched as part of lazy loading
  • - *
  • Make use of the L2 cache, build this part of the graph from L2 cache
  • - *
- * - * @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 fetchLazy(String path, String fetchProperties); - - /** - * Additionally specify a FetchConfig to use a separate query or lazy loading - * to load this path. - *
{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers = DB.find(Customer.class)
-   *     .select("name, status")
-   *     .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
-   *     .findList();
-   *
-   * }
- * - * @param path the property path we wish to fetch eagerly. - */ - Query fetch(String path, String fetchProperties, FetchConfig fetchConfig); - - /** - * Specify a path to fetch eagerly including all its properties. - *

- * 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(). - *

- *
{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers = DB.find(Customer.class)
-   *     // eager fetch the contacts
-   *     .fetch("contacts")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path we wish to fetch eagerly. - */ - Query fetch(String path); - - /** - * Fetch the path eagerly using a "query join" (separate SQL query). - *

- * This is the same as: - *

- *
{@code
-   *
-   *  fetch(path, FetchConfig.ofQuery())
-   *
-   * }
- *

- * 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. - *

- *

- * 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. - *

- * - * @param path the property path we wish to fetch eagerly - */ - Query fetchQuery(String path); - - /** - * Fetch the path eagerly using L2 cache. - */ - Query fetchCache(String path); - - /** - * Fetch the path lazily (via batch lazy loading). - *

- * This is the same as: - *

- *
{@code
-   *
-   *  fetch(path, FetchConfig.ofLazy())
-   *
-   * }
- *

- * The reason for using fetchLazy() is to either: - *

- *
    - *
  • Control/tune what is fetched as part of lazy loading
  • - *
  • Make use of the L2 cache, build this part of the graph from L2 cache
  • - *
- * - * @param path the property path we wish to fetch lazily. - */ - Query fetchLazy(String path); - - /** - * Additionally specify a JoinConfig to specify a "query join" and or define - * the lazy loading query. - *
{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers = DB.find(Customer.class)
-   *     // lazy fetch contacts with a batch size of 100
-   *     .fetch("contacts", FetchConfig.ofLazy(100))
-   *     .findList();
-   *
-   * }
- */ - Query fetch(String path, FetchConfig fetchConfig); - - /** - * Apply the path properties replacing the select and fetch clauses. - *

- * This is typically used when the FetchPath is applied to both the query and the JSON output. - *

- */ - Query apply(FetchPath fetchPath); - - /** - * Apply changes to the query conditional on the supplied predicate. - *

- * Typically, the changes are extra predicates etc. - * - * @param predicate The predicate which when true the changes are applied - * @param apply The changes to apply to the query - */ - Query alsoIf(BooleanSupplier predicate, Consumer> apply); - - /** - * Execute the query using the given transaction. - */ - Query usingTransaction(Transaction transaction); - - /** - * Execute the query using the given connection. - */ - Query usingConnection(Connection connection); - - /** - * Execute the query using the given database. - */ - Query usingDatabase(Database database); - - /** - * Ensure that the master DataSource is used if there is a read only data source - * being used (that is using a read replica database potentially with replication lag). - *

- * When the database is configured with a read-only DataSource via - * say {@link io.ebean.DatabaseBuilder#readOnlyDataSource(DataSource)} then - * by default when a query is run without an active transaction, it uses the read-only data - * source. We use {@code usingMaster()} to instead ensure that the query is executed - * against the master data source. - */ - Query usingMaster(); - - /** - * Execute the query returning the list of Id's. - *

- * This query will execute against the Database that was used to create it. - *

- */ - List findIds(); - - /** - * Execute the query iterating over the results. - *

- * 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. - *

- *

- * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()} - * when you have finished iterating the results (typically in a finally block). - *

- *

- * findEach() and findEachWhile() are preferred to findIterate() as they ensure - * the jdbc statement and resultSet are closed at the end of the iteration. - *

- *

- * This query will execute against the Database that was used to create it. - *

- *
{@code
-   *
-   *  Query query = DB.find(Customer.class)
-   *     .where().eq("status", Status.NEW)
-   *     .order().asc("id");
-   *
-   *  // use try with resources to ensure QueryIterator is closed
-   *
-   *  try (QueryIterator it = query.findIterate()) {
-   *    while (it.hasNext()) {
-   *      Customer customer = it.next();
-   *      // do something with customer ...
-   *    }
-   *  }
-   *
-   * }
- */ - QueryIterator findIterate(); - - /** - * Execute the query returning the result as a Stream. - *

- * Note that this can support very large queries iterating - * any number of results. To do so internally it can use - * multiple persistence contexts. - *

- *
{@code
-   *
-   *  // use try with resources to ensure Stream is closed
-   *
-   *  try (Stream stream = query.findStream()) {
-   *    stream
-   *    .map(...)
-   *    .collect(...);
-   *  }
-   *
-   * }
- */ - Stream findStream(); - - /** - * Execute the query processing the beans one at a time. - *

- * 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) - *

- *

- * 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. - *

- *

- * 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. - *

- *

- * 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. - *

- *

- * This method is functionally equivalent to findIterate() but instead of using an - * iterator uses the Consumer interface which is better suited to use with closures. - *

- *
{@code
-   *
-   *  DB.find(Customer.class)
-   *     .where().eq("status", Status.NEW)
-   *     .order().asc("id")
-   *     .findEach((Customer customer) -> {
-   *
-   *       // do something with customer
-   *       System.out.println("-- visit " + customer);
-   *     });
-   *
-   * }
- * - * @param consumer the consumer used to process the queried beans. - */ - void findEach(Consumer consumer); - - /** - * Execute findEach streaming query batching the results for consuming. - *

- * This query execution will stream the results and is suited to consuming - * large numbers of results from the database. - *

- * Typically we use this batch consumer when we want to do further processing on - * the beans and want to do that processing in batch form, for example - 100 at - * a time. - * - * @param batch The number of beans processed in the batch - * @param consumer Process the batch of beans - */ - void findEach(int batch, Consumer> consumer); - - /** - * Execute the query using callbacks to a visitor to process the resulting - * beans one at a time. - *

- * 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. - *

- *

- * This method is functionally equivalent to findIterate() but instead of using an - * iterator uses the Predicate interface which is better suited to use with closures. - *

- *
{@code
-   *
-   *  DB.find(Customer.class)
-   *     .fetchQuery("contacts")
-   *     .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);
-   *     });
-   *
-   * }
- * - * @param consumer the consumer used to process the queried beans. - */ - void findEachWhile(Predicate consumer); - - /** - * Execute the query returning the list of objects. - *

- * This query will execute against the Database that was used to create it. - *

- *
{@code
-   *
-   * List customers = DB.find(Customer.class)
-   *     .where().ilike("name", "rob%")
-   *     .findList();
-   *
-   * }
- */ - List findList(); - - /** - * Execute the query returning the set of objects. - *

- * This query will execute against the Database that was used to create it. - *

- *
{@code
-   *
-   * Set customers = DB.find(Customer.class)
-   *     .where().ilike("name", "rob%")
-   *     .findSet();
-   *
-   * }
- */ - Set findSet(); - - /** - * Execute the query returning a map of the objects. - *

- * This query will execute against the Database that was used to create it. - *

- *

- * 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. - *

- *
{@code
-   *
-   * Map map = DB.find(Product.class)
-   *     .setMapKey("sku")
-   *     .findMap();
-   *
-   * }
- */ - Map findMap(); - - /** - * Execute the query returning a list of values for a single property. - *

- *

Example 1:

- *
{@code
-   *
-   *  List names =
-   *    DB.find(Customer.class)
-   *      .select("name")
-   *      .order().asc("name")
-   *      .findSingleAttributeList();
-   *
-   * }
- *

- *

Example 2:

- *
{@code
-   *
-   *  List names =
-   *    DB.find(Customer.class)
-   *      .setDistinct(true)
-   *      .select("name")
-   *      .where().eq("status", Customer.Status.NEW)
-   *      .order().asc("name")
-   *      .setMaxRows(100)
-   *      .findSingleAttributeList();
-   *
-   * }
- * - * @return the list of values for the selected property - */ -
List findSingleAttributeList(); - - /** - * Execute the query returning a hashset of values for a single property. - */ - Set findSingleAttributeSet(); - - /** - * Execute a query returning a single value or null for a single property/column. - *

- *

{@code
-   *
-   *  String name =
-   *    DB.find(Customer.class)
-   *      .select("name")
-   *      .where().eq("id", 42)
-   *      .findSingleAttribute();
-   *
-   * }
- */ - @Nullable -
A findSingleAttribute(); - - /** - * Execute the query returning a single optional attribute value. - *

- *

Example

- *
{@code
-   *
-   *  Optional maybeName =
-   *    new QCustomer()
-   *      .select(name)
-   *      .id.eq(42)
-   *      .status.eq(NEW)
-   *      .findSingleAttributeOrEmpty();
-   *
-   * }
- * - * @return an optional value for the selected property - */ -
Optional findSingleAttributeOrEmpty(); - /** * Return true if this is countDistinct query. */ boolean isCountDistinct(); - /** - * Execute the query returning true if a row is found. - *

- * The query is executed using max rows of 1 and will only select the id property. - * This method is really just a convenient way to optimise a query to perform a - * 'does a row exist in the db' check. - *

- * - *

Example using a query bean:

- *
{@code
-   *
-   *   boolean userExists =
-   *     new QContact()
-   *       .email.equalTo("rob@foo.com")
-   *       .exists();
-   *
-   * }
- * - *

Example:

- *
{@code
-   *
-   *   boolean userExists = query()
-   *     .where().eq("email", "rob@foo.com")
-   *     .exists();
-   *
-   * }
- * - * @return True if the query finds a matching row in the database - */ - boolean exists(); - - /** - * Execute the query returning either a single bean or null (if no matching - * bean is found). - *

- * If more than 1 row is found for this query then a NonUniqueResultException is - * thrown. - *

- *

- * This is useful when your predicates dictate that your query should only - * return 0 or 1 results. - *

- *
{@code
-   *
-   * // assuming the sku of products is unique...
-   * Product product = DB.find(Product.class)
-   *         .where().eq("sku", "aa113")
-   *         .findOne();
-   * ...
-   * }
- *

- * It is also useful with finding objects by their id when you want to specify - * further join information. - *

- *
{@code
-   *
-   * // Fetch order 1 and additionally fetch join its order details...
-   * Order order = DB.find(Order.class)
-   *       .setId(1)
-   *       .fetch("details")
-   *       .findOne();
-   *
-   * // the order details were eagerly loaded
-   * List details = order.getDetails();
-   * ...
-   * }
- * - * @throws NonUniqueResultException if more than one result was found - */ - @Nullable - T findOne(); - - /** - * Execute the query returning an optional bean. - */ - Optional findOneOrEmpty(); - - /** - * Return versions of a @History entity bean. - *

- * 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. - *

- *

- * 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. - *

- */ - List> findVersions(); - - /** - * Return versions of a @History entity bean between the 2 timestamps. - *

- * 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. - *

- */ - List> findVersionsBetween(Timestamp start, Timestamp end); - - /** - * Execute as a delete query deleting the 'root level' beans that match the predicates - * in the query. - *

- * Note that if the query includes joins then the generated delete statement may not be - * optimal depending on the database platform. - *

- * - * @return the number of beans/rows that were deleted. - */ - int delete(); - /** * Execute as a delete query returning the number of rows deleted using the given transaction. *

@@ -1117,13 +246,6 @@ public interface Query extends CancelableQuery { */ int delete(Transaction transaction); - /** - * Execute the UpdateQuery returning the number of rows updated. - * - * @return the number of beans/rows updated. - */ - int update(); - /** * Execute the UpdateQuery returning the number of rows updated using the given transaction. * @@ -1132,76 +254,11 @@ public interface Query extends CancelableQuery { int update(Transaction transaction); /** - * Return the count of entities this query should return. - *

- * This is the number of 'top level' or 'root level' entities. - *

+ * Execute the UpdateQuery returning the number of rows updated. + * + * @return the number of beans/rows updated. */ - int findCount(); - - /** - * Execute find row count query in a background thread. - *

- * 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). - *

- * - * @return a Future object for the row count query - */ - FutureRowCount findFutureCount(); - - /** - * Execute find Id's query in a background thread. - *

- * 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). - *

- * - * @return a Future object for the list of Id's - */ - FutureIds findFutureIds(); - - /** - * Execute find list query in a background thread. - *

- * 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. - *

- * - * @return a Future object for the list result of the query - */ - FutureList findFutureList(); - - /** - * Return a PagedList for this query using firstRow and maxRows. - *

- * The benefit of using this over findList() is that it provides functionality to get the - * total row count etc. - *

- *

- * If maxRows is not set on the query prior to calling findPagedList() then a - * PersistenceException is thrown. - *

- *
{@code
-   *
-   *  PagedList pagedList = DB.find(Order.class)
-   *       .setFirstRow(50)
-   *       .setMaxRows(20)
-   *       .findPagedList();
-   *
-   *       // fetch the total row count in the background
-   *       pagedList.loadRowCount();
-   *
-   *       List orders = pagedList.getList();
-   *       int totalRowCount = pagedList.getTotalRowCount();
-   *
-   * }
- * - * @return The PagedList - */ - PagedList findPagedList(); + int update(); /** * Set a named bind parameter. Named parameters have a colon to prefix the name. @@ -1404,16 +461,6 @@ public interface Query extends CancelableQuery { */ Query having(Expression addExpressionToHaving); - /** - * Set the order by clause replacing the existing order by clause if there is - * one. - *

- * This follows SQL syntax using commas between each property with the - * optional asc and desc keywords representing ascending and descending order - * respectively. - */ - Query orderBy(String orderByClause); - /** * @deprecated migrate to {@link #orderBy()}. */ @@ -1422,6 +469,22 @@ public interface Query extends CancelableQuery { return orderBy(orderByClause); } + /** + * @deprecated migrate to {@link #orderBy()}. + */ + @Deprecated(since = "13.19", forRemoval = true) + default OrderBy order() { + return orderBy(); + } + + /** + * @deprecated migrate to {@link #setOrderBy(OrderBy)}. + */ + @Deprecated(since = "13.19", forRemoval = true) + default Query setOrder(OrderBy orderBy) { + return setOrderBy(orderBy); + } + /** * Return the OrderBy so that you can append an ascending or descending * property to the order by clause. @@ -1433,282 +496,16 @@ public interface Query extends CancelableQuery { */ OrderBy orderBy(); - /** - * @deprecated migrate to {@link #orderBy()}. - */ - @Deprecated(since = "13.19", forRemoval = true) - default OrderBy order() { - return orderBy(); - } - - /** - * Set an OrderBy object to replace any existing OrderBy clause. - */ - Query setOrderBy(OrderBy orderBy); - - /** - * @deprecated migrate to {@link #setOrderBy(OrderBy)}. - */ - @Deprecated(since = "13.19", forRemoval = true) - default Query setOrder(OrderBy orderBy) { - return setOrderBy(orderBy); - } - - /** - * Set whether this query uses DISTINCT. - *

- * 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. - *

- *
{@code
-   *
-   *   List customers =
-   *       DB.find(Customer.class)
-   *          .setDistinct(true)
-   *          .select("name")
-   *          .findList();
-   *
-   * }
- */ - Query setDistinct(boolean isDistinct); - - /** - * Extended version for setDistinct in conjunction with "findSingleAttributeList"; - * - *
{@code
-   *
-   *  List> orderStatusCount =
-   *
-   *     DB.find(Order.class)
-   *      .select("status")
-   *      .where()
-   *      .gt("orderDate", LocalDate.now().minusMonths(3))
-   *
-   *      // fetch as single attribute with a COUNT
-   *      .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
-   *      .findSingleAttributeList();
-   *
-   *     for (CountedValue entry : orderStatusCount) {
-   *       System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
-   *     }
-   *
-   *   // produces
-   *
-   *   count:3 orderStatus:NEW
-   *   count:1 orderStatus:SHIPPED
-   *   count:1 orderStatus:COMPLETE
-   *
-   * }
- */ - Query setCountDistinct(CountDistinctOrder orderBy); - /** * 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 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 setMaxRows(int maxRows); - - /** - * Set the property to use as keys for a map. - *

- * If no property is set then the id property is used. - *

- *
{@code
-   *
-   * // Assuming sku is unique for products...
-   *
-   * Map productMap = DB.find(Product.class)
-   *     .setMapKey("sku")  // sku map keys...
-   *     .findMap();
-   *
-   * }
- * - * @param mapKey the property to use as keys for a map. - */ - Query setMapKey(String mapKey); - - /** - * Set this to false to not use the bean cache. - *

- * This method is now superseded by {@link #setBeanCacheMode(CacheMode)} - * which provides more explicit options controlled bean cache use. - *

- *

- * This method is likely to be deprecated in the future with migration - * over to setUseBeanCache(). - *

- */ - default Query setUseCache(boolean useCache) { - return setBeanCacheMode(useCache ? CacheMode.ON : CacheMode.OFF); - } - - /** - * Set the mode to use the bean cache when executing this query. - *

- * 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. - *

- *

- * By default findList() with natural keys will not use the bean cache. In that - * case we need to explicitly use the bean cache. - *

- */ - Query setBeanCacheMode(CacheMode beanCacheMode); - - /** - * Set the {@link CacheMode} to use the query for executing this query. - */ - Query setUseQueryCache(CacheMode queryCacheMode); - - /** - * Calls {@link #setUseQueryCache(CacheMode)} with ON or OFF. - */ - default Query setUseQueryCache(boolean enabled) { - return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF); - } - - /** - * Set the profile location of this query. This is used to relate query execution metrics - * back to a location like a specific line of code. - */ - Query setProfileLocation(ProfileLocation profileLocation); - - /** - * Set a label on the query. - *

- * This label can be used to help identify query performance metrics but we can also use - * profile location enhancement on Finders so for some that would be a better option. - *

- */ - Query setLabel(String label); - - /** - * Set a SQL query hint. - *

- * This results in an inline comment that immediately follows - * after the select keyword in the form: {@code /*+ hint *\/ } - */ - Query setHint(String hint); - - /** - * Set to true if this query should execute against the doc store. - *

- * When setting this you may also consider disabling lazy loading. - *

- */ - Query setUseDocStore(boolean useDocStore); - - /** - * When set to true when you want the returned beans to be read only. - */ - Query setReadOnly(boolean readOnly); - - /** - * Set a timeout on this query. - *

- * 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. - *

- * - * @param secs the query timeout limit in seconds. Zero means there is no limit. - */ - Query setTimeout(int secs); - - /** - * A hint which for JDBC translates to the Statement.fetchSize(). - *

- * 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. - *

- *

- * 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. - *

- *

- * 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. - *

- */ - Query setBufferFetchSizeHint(int fetchSize); - - /** - * Return the sql that was generated for executing this query. - *

- * This is only available after the query has been executed and provided only - * for informational purposes. - *

- */ - String getGeneratedSql(); - - /** - * Execute the query with the given lock type and WAIT. - *

- * Note that forUpdate() is the same as - * withLock(LockType.UPDATE). - *

- * Provides us with the ability to explicitly use Postgres - * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. - */ - Query withLock(LockType lockType); - - /** - * Execute the query with the given lock type and lock wait. - *

- * Note that forUpdateNoWait() is the same as - * withLock(LockType.UPDATE, LockWait.NOWAIT). - *

- * Provides us with the ability to explicitly use Postgres - * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. - */ - Query withLock(LockType lockType, LockWait lockWait); - - /** - * Execute using "for update" clause which results in the DB locking the record. - *

- * The same as withLock(LockType.UPDATE, LockWait.WAIT). - */ - Query forUpdate(); - - /** - * Execute using "for update" clause with "no wait" option. - *

- * This is typically a Postgres and Oracle only option at this stage. - *

- * The same as withLock(LockType.UPDATE, LockWait.NOWAIT). - */ - Query forUpdateNoWait(); - - /** - * Execute using "for update" clause with "skip locked" option. - *

- * This is typically a Postgres and Oracle only option at this stage. - *

- * The same as withLock(LockType.UPDATE, LockWait.SKIPLOCKED). - */ - Query forUpdateSkipLocked(); - /** * Return true if this query has forUpdate set. */ @@ -1724,50 +521,6 @@ public interface Query extends CancelableQuery { */ LockType getForUpdateLockType(); - /** - * Set root table alias. - */ - Query alias(String alias); - - /** - * Set the base table to use for this query. - *

- * Typically this is used when a table has partitioning and we wish to specify a specific - * partition/table to query against. - *

- *
{@code
-   *
-   *   QOrder()
-   *   .setBaseTable("order_2019_05")
-   *   .status.equalTo(Status.NEW)
-   *   .findList();
-   *
-   * }
- */ - Query setBaseTable(String baseTable); - - /** - * Return the type of beans being queried. - */ - Class getBeanType(); - - /** - * Restrict the query to only return subtypes of the given inherit type. - * - *
{@code
-   *
-   *   List animals =
-   *     new QAnimal()
-   *       .name.startsWith("Fluffy")
-   *       .setInheritType(Cat.class)
-   *       .findList();
-   *
-   * }
- * - * @param type An inheritance subtype of the - */ - Query setInheritType(Class type); - /** * Returns the inherit type. This is normally the same as getBeanType() returns as long as no other type is set. */ @@ -1778,23 +531,6 @@ public interface Query extends CancelableQuery { */ QueryType getQueryType(); - /** - * Set true if you want to disable lazy loading. - *

- * That is, once the object graph is returned further lazy loading is disabled. - *

- */ - Query setDisableLazyLoading(boolean disableLazyLoading); - - /** - * Returns the set of properties or paths that are unknown (do not map to known properties or paths). - *

- * Validate the query checking the where and orderBy expression paths to confirm if - * they represent valid properties or paths for the given bean type. - *

- */ - Set validate(); - /** * Controls, if paginated queries should always append an 'order by id' statement at the end to * guarantee a deterministic sort result. This may affect performance. @@ -1803,6 +539,12 @@ public interface Query extends CancelableQuery { */ Query orderById(boolean orderById); + /** + * Set the profile location of this query. This is used to relate query execution metrics + * back to a location like a specific line of code. + */ + Query setProfileLocation(ProfileLocation profileLocation); + /** * Type safe query bean properties and expressions (marker interface). *

diff --git a/ebean-api/src/main/java/io/ebean/QueryBuilder.java b/ebean-api/src/main/java/io/ebean/QueryBuilder.java new file mode 100644 index 000000000..d8471ae6f --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/QueryBuilder.java @@ -0,0 +1,967 @@ +package io.ebean; + +import io.avaje.lang.Nullable; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * Build and execute an ORM query. + * + * @param The type of the builder + * @param The entity bean type + */ +public interface QueryBuilder extends QueryBuilderProjection { + + /** + * Set root table alias. + */ + SELF alias(String alias); + + /** + * Apply changes to the query conditional on the supplied predicate. + *

+ * Typically, the changes are extra predicates etc. + * + * @param predicate The predicate which when true the changes are applied + * @param apply The changes to apply to the query + */ + SELF alsoIf(BooleanSupplier predicate, Consumer apply); + + /** + * Perform an 'As of' query using history tables to return the object graph + * as of a time in the past. + *

+ * To perform this query the DB must have underlying history tables. + * + * @param asOf the date time in the past at which you want to view the data + */ + SELF asOf(Timestamp asOf); + + /** + * Execute the query against the draft set of tables. + */ + SELF asDraft(); + + /** + * Convert the query to a DTO bean query. + *

+ * We effectively use the underlying ORM query to build the SQL and then execute + * and map it into DTO beans. + */ + DtoQuery asDto(Class dtoClass); + + /** + * Convert the query to a UpdateQuery. + *

+ * Typically this is used with query beans to covert a query bean + * query into an UpdateQuery like the examples below. + *

+ * + *
{@code
+   *
+   *  int rowsUpdated = new QCustomer()
+   *       .name.startsWith("Rob")
+   *       .asUpdate()
+   *       .set("active", false)
+   *       .update();;
+   *
+   * }
+ * + *
{@code
+   *
+   *   int rowsUpdated = new QContact()
+   *       .notes.note.startsWith("Make Inactive")
+   *       .email.endsWith("@foo.com")
+   *       .customer.id.equalTo(42)
+   *       .asUpdate()
+   *       .set("inactive", true)
+   *       .setRaw("email = lower(email)")
+   *       .update();
+   *
+   * }
+ */ + UpdateQuery asUpdate(); + + /** + * Return a copy of the query. + *

+ * 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. + *

+ */ + SELF copy(); + + /** + * Execute the query using the given transaction. + */ + SELF usingTransaction(Transaction transaction); + + /** + * Execute the query using the given connection. + */ + SELF usingConnection(Connection connection); + + /** + * Execute the query using the given database. + */ + SELF usingDatabase(Database database); + + /** + * Ensure that the master DataSource is used if there is a read only data source + * being used (that is using a read replica database potentially with replication lag). + *

+ * When the database is configured with a read-only DataSource via + * say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then + * by default when a query is run without an active transaction, it uses the read-only data + * source. We we use {@code usingMaster()} to instead ensure that the query is executed + * against the master data source. + */ + SELF usingMaster(); + + /** + * Set the base table to use for this query. + *

+ * Typically this is used when a table has partitioning and we wish to specify a specific + * partition/table to query against. + *

+ *
{@code
+   *
+   *   QOrder()
+   *   .setBaseTable("order_2019_05")
+   *   .status.equalTo(Status.NEW)
+   *   .findList();
+   *
+   * }
+ */ + SELF setBaseTable(String baseTable); + + /** + * Specify the PersistenceContextScope to use for this query. + *

+ * When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)} + * is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}. + *

+ * Note that the same persistence Context is used for subsequent lazy loading and query join queries. + *

+ * 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. + */ + SELF setPersistenceContextScope(PersistenceContextScope scope); + + /** + * Explicitly specify whether to use AutoTune for this query. + *

+ * 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. + *

+ * 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. + */ + SELF setAutoTune(boolean autoTune); + + /** + * Execute the query allowing properties with invalid JSON to be collected and not fail the query. + *

{@code
+   *
+   *   // fetch a bean with JSON content
+   *   EBasicJsonList bean= DB.find(EBasicJsonList.class)
+   *       .setId(42)
+   *       .setAllowLoadErrors()  // collect errors into bean state if we have invalid JSON
+   *       .findOne();
+   *
+   *
+   *   // get the invalid JSON errors from the bean state
+   *   Map errors = server().getBeanState(bean).getLoadErrors();
+   *
+   *   // If this map is not empty tell we have invalid JSON
+   *   // and should try and fix the JSON content or inform the user
+   *
+   * }
+ */ + SELF setAllowLoadErrors(); + + /** + * Set the default lazy loading batch size to use. + *

+ * 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 + */ + SELF setLazyLoadBatchSize(int lazyLoadBatchSize); + + /** + * Set a label on the query. + *

+ * This label can be used to help identify query performance metrics but we can also use + * profile location enhancement on Finders so for some that would be a better option. + */ + SELF setLabel(String label); + + /** + * Set a SQL query hint. + *

+ * This results in an inline comment that immediately follows + * after the select keyword in the form: {@code /*+ hint *\/ } + */ + SELF setHint(String hint); + + /** + * Set the index(es) to search for a document store which uses partitions. + *

+ * For example, when executing a query against ElasticSearch with daily indexes we can + * explicitly specify the indexes to search against. + *

+ *
{@code
+   *
+   *   // explicitly specify the indexes to search
+   *   query.setDocIndexName("logstash-2016.11.5,logstash-2016.11.6")
+   *
+   *   // search today's index
+   *   query.setDocIndexName("$today")
+   *
+   *   // search the last 3 days
+   *   query.setDocIndexName("$last-3")
+   *
+   * }
+ *

+ * If the indexName is specified with ${daily} e.g. "logstash-${daily}" ... then we can use + * $today and $last-x as the search docIndexName like the examples below. + *

+ *
{@code
+   *
+   *   // search today's index
+   *   query.setDocIndexName("$today")
+   *
+   *   // search the last 3 days
+   *   query.setDocIndexName("$last-3")
+   *
+   * }
+ * + * @param indexName The index or indexes to search against + * @return This query + */ + SELF setDocIndexName(String indexName); + + /** + * Execute the query including soft deleted rows. + *

+ * 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. + */ + SELF setIncludeSoftDeletes(); + + /** + * Disable read auditing for this query. + *

+ * 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. + */ + SELF setDisableReadAuditing(); + + /** + * Set true if you want to disable lazy loading. + *

+ * That is, once the object graph is returned further lazy loading is disabled. + */ + SELF setDisableLazyLoading(boolean disableLazyLoading); + + /** + * Set whether this query uses DISTINCT. + */ + SELF setDistinct(boolean distinct); + + /** + * Restrict the query to only return subtypes of the given inherit type. + *

{@code
+   *
+   *   List animals =
+   *     new QAnimal()
+   *       .name.startsWith("Fluffy")
+   *       .setInheritType(Cat.class)
+   *       .findList();
+   *
+   * }
+ */ + SELF setInheritType(Class type); + + /** + * Set the first row to return for this query. + * + * @param firstRow the first row to include in the query result. + */ + SELF setFirstRow(int firstRow); + + /** + * Set the maximum number of rows to return in the query. + * + * @param maxRows the maximum number of rows to return in the query. + */ + SELF setMaxRows(int maxRows); + + /** + * Set RawSql to use for this query. + */ + SELF setRawSql(RawSql rawSql); + + /** + * Extended version for setDistinct in conjunction with "findSingleAttributeList"; + * + *
{@code
+   *
+   *  List> orderStatusCount =
+   *
+   *     DB.find(Order.class)
+   *      .select("status")
+   *      .where()
+   *      .gt("orderDate", LocalDate.now().minusMonths(3))
+   *
+   *      // fetch as single attribute with a COUNT
+   *      .setCountDistinct(CountDistinctOrder.COUNT_DESC_ATTR_ASC)
+   *      .findSingleAttributeList();
+   *
+   *     for (CountedValue entry : orderStatusCount) {
+   *       System.out.println(" count:" + entry.getCount()+" orderStatus:" + entry.getValue() );
+   *     }
+   *
+   *   // produces
+   *
+   *   count:3 orderStatus:NEW
+   *   count:1 orderStatus:SHIPPED
+   *   count:1 orderStatus:COMPLETE
+   *
+   * }
+ */ + SELF setCountDistinct(CountDistinctOrder orderBy); + + /** + * Set the property to use as keys for a map. + *

+ * If no property is set then the id property is used. + *

+ *
{@code
+   *
+   * // Assuming sku is unique for products...
+   *
+   * Map productMap = DB.find(Product.class)
+   *     .setMapKey("sku")  // sku map keys...
+   *     .findMap();
+   *
+   * }
+ * + * @param mapKey the property to use as keys for a map. + */ + SELF setMapKey(String mapKey); + + /** + * Set to true if this query should execute against the doc store. + *

+ * When setting this you may also consider disabling lazy loading. + */ + SELF setUseDocStore(boolean useDocStore); + + /** + * When set to true when you want the returned beans to be read only. + */ + SELF setReadOnly(boolean readOnly); + + /** + * Set a timeout on this query. + *

+ * 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. + *

+ * + * @param secs the query timeout limit in seconds. Zero means there is no limit. + */ + SELF setTimeout(int secs); + + /** + * A hint which for JDBC translates to the Statement.fetchSize(). + *

+ * 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. + *

+ *

+ * 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. + *

+ *

+ * 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. + *

+ */ + SELF setBufferFetchSizeHint(int fetchSize); + + /** + * Set the mode to use the bean cache when executing this query. + *

+ * 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. + *

+ * By default, findList() with natural keys will not use the bean cache. In that + * case we need to explicitly use the bean cache. + */ + SELF setBeanCacheMode(CacheMode beanCacheMode); + + /** + * Set the {@link CacheMode} to use the query for executing this query. + */ + SELF setUseQueryCache(CacheMode cacheMode); + + /** + * Set this to false to not use the bean cache. + *

+ * This method is now superseded by {@link #setBeanCacheMode(CacheMode)} + * which provides more explicit options controlled bean cache use. + *

+ * This method is likely to be deprecated in the future with migration + * over to setUseBeanCache(). + */ + default SELF setUseCache(boolean useCache) { + return setBeanCacheMode(useCache ? CacheMode.ON : CacheMode.OFF); + } + + /** + * Calls {@link #setUseQueryCache(CacheMode)} with ON or OFF. + */ + default SELF setUseQueryCache(boolean enabled) { + return setUseQueryCache(enabled ? CacheMode.ON : CacheMode.OFF); + } + + /** + * Set the order by clause replacing the existing order by clause if there is + * one. + *

+ * This follows SQL syntax using commas between each property with the + * optional asc and desc keywords representing ascending and descending order + * respectively. + */ + SELF orderBy(String orderByClause); + + /** + * Set an OrderBy object to replace any existing OrderBy clause. + */ + SELF setOrderBy(OrderBy orderBy); + + /** + * Execute the query with the given lock type and WAIT. + *

+ * Note that forUpdate() is the same as + * withLock(LockType.UPDATE). + *

+ * Provides us with the ability to explicitly use Postgres + * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. + */ + SELF withLock(Query.LockType lockType); + + /** + * Execute the query with the given lock type and lock wait. + *

+ * Note that forUpdateNoWait() is the same as + * withLock(LockType.UPDATE, LockWait.NOWAIT). + *

+ * Provides us with the ability to explicitly use Postgres + * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. + */ + SELF withLock(Query.LockType lockType, Query.LockWait lockWait); + + /** + * Execute using "for update" clause which results in the DB locking the record. + *

+ * The same as withLock(LockType.UPDATE, LockWait.WAIT). + */ + SELF forUpdate(); + + /** + * Execute using "for update" clause with "no wait" option. + *

+ * This is typically a Postgres and Oracle only option at this stage. + *

+ * The same as withLock(LockType.UPDATE, LockWait.NOWAIT). + */ + SELF forUpdateNoWait(); + + /** + * Execute using "for update" clause with "skip locked" option. + *

+ * This is typically a Postgres and Oracle only option at this stage. + *

+ * The same as withLock(LockType.UPDATE, LockWait.SKIPLOCKED). + */ + SELF forUpdateSkipLocked(); + + /** + * Returns the set of properties or paths that are unknown (do not map to known properties or paths). + *

+ * Validate the query checking the where and orderBy expression paths to confirm if + * they represent valid properties or paths for the given bean type. + */ + Set validate(); + + /** + * Return the sql that was generated for executing this query. + *

+ * This is only available after the query has been executed and provided only + * for informational purposes. + */ + String getGeneratedSql(); + + /** + * Return the type of beans being queried. + */ + Class getBeanType(); + + /** + * Execute as a delete query deleting the 'root level' beans that match the predicates + * in the query. + *

+ * Note that if the query includes joins then the generated delete statement may not be + * optimal depending on the database platform. + * + * @return the number of beans/rows that were deleted. + */ + int delete(); + + /** + * Execute the query returning true if a row is found. + *

+ * The query is executed using max rows of 1 and will only select the id property. + * This method is really just a convenient way to optimise a query to perform a + * 'does a row exist in the db' check. + * + *

Example using a query bean:

+ *
{@code
+   *
+   *   boolean userExists =
+   *     new QContact()
+   *       .email.equalTo("rob@foo.com")
+   *       .exists();
+   *
+   * }
+ * + *

Example:

+ *
{@code
+   *
+   *   boolean userExists = query()
+   *     .where().eq("email", "rob@foo.com")
+   *     .exists();
+   *
+   * }
+ * + * @return True if the query finds a matching row in the database + */ + boolean exists(); + + /** + * Execute the query returning either a single bean or null (if no matching + * bean is found). + *

+ * If more than 1 row is found for this query then a PersistenceException is + * thrown. + *

+ * This is useful when your predicates dictate that your query should only + * return 0 or 1 results. + *

+ *

{@code
+   *
+   * // assuming the sku of products is unique...
+   * Product product =
+   *     new QProduct()
+   *         .sku.equalTo("aa113")
+   *         .findOne();
+   * ...
+   * }
+ *

+ * It is also useful with finding objects by their id when you want to specify + * further join information to optimise the query. + *

+ *

{@code
+   *
+   * // Fetch order 42 and additionally fetch join its order details...
+   * Order order =
+   *     new QOrder()
+   *         .fetch("details") // eagerly load the order details
+   *         .id.equalTo(42)
+   *         .findOne();
+   *
+   * // the order details were eagerly loaded
+   * List details = order.getDetails();
+   * ...
+   * }
+ */ + @Nullable + T findOne(); + + /** + * Execute the query returning an optional bean. + */ + Optional findOneOrEmpty(); + + /** + * Execute the query returning the list of objects. + *

+ * This query will execute against the EbeanServer that was used to create it. + *

+ *

{@code
+   *
+   * List customers =
+   *     new QCustomer()
+   *       .name.ilike("rob%")
+   *       .findList();
+   *
+   * }
+ * + * @see Query#findList() + */ + List findList(); + + /** + * Execute the query returning the result as a Stream. + *

+ * Note that this can support very large queries iterating + * any number of results. To do so internally it can use + * multiple persistence contexts. + *

+ *
{@code
+   *
+   *  // use try with resources to ensure Stream is closed
+   *
+   *  try (Stream stream = query.findStream()) {
+   *    stream
+   *    .map(...)
+   *    .collect(...);
+   *  }
+   *
+   * }
+ */ + Stream findStream(); + + /** + * Execute the query returning the set of objects. + *

+ * This query will execute against the EbeanServer that was used to create it. + *

+ *

{@code
+   *
+   * Set customers =
+   *     new QCustomer()
+   *       .name.ilike("rob%")
+   *       .findSet();
+   *
+   * }
+ * + * @see Query#findSet() + */ + Set findSet(); + + /** + * Execute the query returning the list of Id's. + *

+ * This query will execute against the EbeanServer that was used to create it. + * + * @see Query#findIds() + */ + List findIds(); + + /** + * Execute the query returning a map of the objects. + *

+ * This query will execute against the EbeanServer that was used to create it. + *

+ * You can use setMapKey() or asMapKey() to specify the property to be used as keys + * on the map. If one is not specified then the id property is used. + *

+ *

{@code
+   *
+   * Map map =
+   *   new QProduct()
+   *     .sku.asMapKey()
+   *     .findMap();
+   *
+   * }
+ * + * @see Query#findMap() + */ + Map findMap(); + + /** + * Execute the query iterating over the results. + *

+ * 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. + *

+ * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()} + * when you have finished iterating the results (typically in a finally block). + *

+ * findEach() and findEachWhile() are preferred to findIterate() as they ensure + * the jdbc statement and resultSet are closed at the end of the iteration. + *

+ * This query will execute against the EbeanServer that was used to create it. + *

+ *
{@code
+   *
+   *  Query query =
+   *    new QCustomer()
+   *     .status.equalTo(Customer.Status.NEW)
+   *     .orderBy()
+   *       id.asc()
+   *     .query();
+   *
+   *  try (QueryIterator it = query.findIterate()) {
+   *    while (it.hasNext()) {
+   *      Customer customer = it.next();
+   *      // do something with customer ...
+   *    }
+   *  }
+   *
+   * }
+ */ + QueryIterator findIterate(); + + /** + * Execute the query returning a list of values for a single property. + *

+ *

Example

+ *
{@code
+   *
+   *  List names =
+   *    new QCustomer()
+   *      .setDistinct(true)
+   *      .select(name)
+   *      .findSingleAttributeList();
+   *
+   * }
+ * + * @return the list of values for the selected property + */ +
List findSingleAttributeList(); + + /** + * Execute the query returning a single value or null for a single property. + *

+ *

Example

+ *
{@code
+   *
+   *  LocalDate maxDate =
+   *    new QCustomer()
+   *      .select("max(startDate)")
+   *      .findSingleAttribute();
+   *
+   * }
+ * + * @return a single value or null for the selected property + */ + @Nullable +
A findSingleAttribute(); + + /** + * Execute the query returning a single optional attribute value. + *

+ *

Example

+ *
{@code
+   *
+   *  Optional maybeName =
+   *    new QCustomer()
+   *      .select(name)
+   *      .id.eq(42)
+   *      .status.eq(NEW)
+   *      .findSingleAttributeOrEmpty();
+   *
+   * }
+ * + * @return an optional value for the selected property + */ +
Optional findSingleAttributeOrEmpty(); + + /** + * Execute the query returning a hashset of values for a single property. + */ + Set findSingleAttributeSet(); + + /** + * Execute the query processing the beans one at a time. + *

+ * 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) + *

+ * 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. + *

+ * 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. + *

+ * This method is functionally equivalent to findIterate() but instead of using an + * iterator uses the Consumer interface which is better suited to use with closures. + * + *

{@code
+   *
+   *  new QCustomer()
+   *     .status.equalTo(Status.NEW)
+   *     .orderBy().id.asc()
+   *     .findEach((Customer customer) -> {
+   *
+   *       // do something with customer
+   *       System.out.println("-- visit " + customer);
+   *     });
+   *
+   * }
+ * + * @param consumer the consumer used to process the queried beans. + */ + void findEach(Consumer consumer); + + /** + * Execute findEach streaming query batching the results for consuming. + *

+ * This query execution will stream the results and is suited to consuming + * large numbers of results from the database. + *

+ * Typically, we use this batch consumer when we want to do further processing on + * the beans and want to do that processing in batch form, for example - 100 at + * a time. + * + * @param batch The number of beans processed in the batch + * @param consumer Process the batch of beans + */ + void findEach(int batch, Consumer> consumer); + + /** + * Execute the query using callbacks to a visitor to process the resulting + * beans one at a time. + *

+ * This method is functionally equivalent to findIterate() but instead of using an + * iterator uses the Predicate interface which is better suited to use with closures. + * + *

{@code
+   *
+   *  new QCustomer()
+   *     .status.equalTo(Status.NEW)
+   *     .orderBy().id.asc()
+   *     .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);
+   *     });
+   *
+   * }
+ * + * @param consumer the consumer used to process the queried beans. + */ + void findEachWhile(Predicate consumer); + + /** + * Return versions of a @History entity bean. + *

+ * 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. + */ + List> findVersions(); + + /** + * Return versions of a @History entity bean between a start and end timestamp. + *

+ * 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. + */ + List> findVersionsBetween(Timestamp start, Timestamp end); + + /** + * Return the count of entities this query should return. + *

+ * This is the number of 'top level' or 'root level' entities. + */ + int findCount(); + + /** + * Execute find row count query in a background thread. + *

+ * 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). + * + * @return a Future object for the row count query + */ + FutureRowCount findFutureCount(); + + /** + * Execute find Id's query in a background thread. + *

+ * 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). + * + * @return a Future object for the list of Id's + */ + FutureIds findFutureIds(); + + /** + * Execute find list query in a background thread. + *

+ * 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. + * + * @return a Future object for the list result of the query + */ + FutureList findFutureList(); + + /** + * Return a PagedList for this query using firstRow and maxRows. + *

+ * The benefit of using this over findList() is that it provides functionality to get the + * total row count etc. + *

+ * If maxRows is not set on the query prior to calling findPagedList() then a + * PersistenceException is thrown. + *

+ *

{@code
+   *
+   *  PagedList pagedList =
+   *    new QOrder()
+   *       .setFirstRow(50)
+   *       .setMaxRows(20)
+   *       .findPagedList();
+   *
+   *       // fetch the total row count in the background
+   *       pagedList.loadRowCount();
+   *
+   *       List orders = pagedList.getList();
+   *       int totalRowCount = pagedList.getTotalRowCount();
+   *
+   * }
+ * + * @return The PagedList + */ + PagedList findPagedList(); + +} diff --git a/ebean-api/src/main/java/io/ebean/QueryBuilderProjection.java b/ebean-api/src/main/java/io/ebean/QueryBuilderProjection.java new file mode 100644 index 000000000..31c3e5021 --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/QueryBuilderProjection.java @@ -0,0 +1,242 @@ +package io.ebean; + +/** + * Builder for ORM Query projection (the select and fetch part). + * + * @param The builder type + * @param The entity bean type + */ +public interface QueryBuilderProjection { + + /** + * Apply the path properties replacing the select and fetch clauses. + *

+ * This is typically used when the FetchPath is applied to both the query and the JSON output. + */ + SELF apply(FetchPath fetchPath); + + /** + * Specify the properties to fetch on the root level entity bean in comma delimited format. + *

+ * The Id property is automatically included in the properties to fetch unless setDistinct(true) + * is set on the query. + *

+ *

+ * Use {@link #fetch(String, String)} to specify specific properties to fetch + * on other non-root level paths of the object graph. + *

+ *
{@code
+   *
+   * List customers = DB.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();
+   *
+   * }
+ * + * @param fetchProperties the properties to fetch for this bean (* = all properties). + */ + SELF select(String fetchProperties); + + /** + * Apply the fetchGroup which defines what part of the object graph to load. + */ + SELF select(FetchGroup fetchGroup); + + /** + * Specify a path to fetch eagerly including specific properties. + *

+ * 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(). + *

+ *
{@code
+   *
+   * // query orders...
+   * List orders = DB.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();
+   * }
+ *

+ * If columns is null or "*" then all columns/properties for that path are fetched. + *

+ *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers = DB.find(Customer.class)
+   *     .select("name, status")
+   *     .fetch("contacts", "firstName,lastName,email")
+   *     .findList();
+   *
+   * }
+ * + * @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). + */ + SELF fetch(String path, String fetchProperties); + + /** + * Fetch the path and properties using a "query join" (separate SQL query). + *

+ * This is the same as: + *

+ *
{@code
+   *
+   *  fetch(path, fetchProperties, FetchConfig.ofQuery())
+   *
+   * }
+ *

+ * 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. + *

+ * 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. + * + * @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). + */ + SELF fetchQuery(String path, String fetchProperties); + + /** + * Fetch the path and properties using L2 bean cache. + * + * @param path The path of the beans we are fetching from L2 cache. + * @param fetchProperties The properties that should be loaded. + */ + SELF fetchCache(String path, String fetchProperties); + + /** + * Fetch the path and properties lazily (via batch lazy loading). + *

+ * This is the same as: + * + *

{@code
+   *
+   *  fetch(path, fetchProperties, FetchConfig.ofLazy())
+   *
+   * }
+ *

+ * The reason for using fetchLazy() is to either: + *

    + *
  • Control/tune what is fetched as part of lazy loading
  • + *
  • Make use of the L2 cache, build this part of the graph from L2 cache
  • + *
+ * + * @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). + */ + SELF fetchLazy(String path, String fetchProperties); + + /** + * Additionally specify a FetchConfig to use a separate query or lazy loading + * to load this path. + *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers = DB.find(Customer.class)
+   *     .select("name, status")
+   *     .fetch("contacts", "firstName,lastName,email", FetchConfig.ofLazy(10))
+   *     .findList();
+   *
+   * }
+ * + * @param path the property path we wish to fetch eagerly. + */ + SELF fetch(String path, String fetchProperties, FetchConfig fetchConfig); + + /** + * Specify a path to fetch eagerly including all its properties. + *

+ * 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(). + *

+ *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers = DB.find(Customer.class)
+   *     // eager fetch the contacts
+   *     .fetch("contacts")
+   *     .findList();
+   *
+   * }
+ * + * @param path the property path we wish to fetch eagerly. + */ + SELF fetch(String path); + + /** + * Fetch the path eagerly using a "query join" (separate SQL query). + *

+ * This is the same as: + *

{@code
+   *
+   *  fetch(path, FetchConfig.ofQuery())
+   *
+   * }
+ *

+ * 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. + *

+ * 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. + * + * @param path the property path we wish to fetch eagerly + */ + SELF fetchQuery(String path); + + /** + * Fetch the path eagerly using L2 cache. + */ + SELF fetchCache(String path); + + /** + * Fetch the path lazily (via batch lazy loading). + *

+ * This is the same as: + *

+ *
{@code
+   *
+   *  fetch(path, FetchConfig.ofLazy())
+   *
+   * }
+ *

+ * The reason for using fetchLazy() is to either: + *

+ *
    + *
  • Control/tune what is fetched as part of lazy loading
  • + *
  • Make use of the L2 cache, build this part of the graph from L2 cache
  • + *
+ * + * @param path the property path we wish to fetch lazily. + */ + SELF fetchLazy(String path); + + /** + * Additionally specify a JoinConfig to specify a "query join" and or define + * the lazy loading query. + *
{@code
+   *
+   * // fetch customers (their id, name and status)
+   * List customers = DB.find(Customer.class)
+   *     // lazy fetch contacts with a batch size of 100
+   *     .fetch("contacts", FetchConfig.ofLazy(100))
+   *     .findList();
+   *
+   * }
+ */ + SELF fetch(String path, FetchConfig fetchConfig); + +} diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/QueryBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/QueryBean.java index 00ad8fadf..a2e0e7454 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/QueryBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/QueryBean.java @@ -53,17 +53,7 @@ import java.util.stream.Stream; * @param the entity bean type (normal entity bean type e.g. Customer) * @param the specific query bean type (e.g. QCustomer) */ -public interface QueryBean { - - /** - * Return the fetch group. - */ - FetchGroup buildFetchGroup(); - - /** - * Return a copy of the query bean. - */ - R copy(); +public interface QueryBean extends QueryBuilder { /** * Return the underlying query. @@ -74,70 +64,9 @@ public interface QueryBean { Query query(); /** - * Explicitly set a comma delimited list of the properties to fetch on the - * 'main' root level entity bean (aka partial object). Note that '*' means all - * properties. - *

- * You use {@link #fetch(String, String)} to specify specific properties to fetch - * on other non-root level paths of the object graph. - *

- *

{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *     // Only fetch the customer id, name and status.
-   *     // This is described as a "Partial Object"
-   *     .select("name, status")
-   *     .name.ilike("rob%")
-   *     .findList();
-   *
-   * }
- * - * @param properties the properties to fetch for this bean (* = all properties). + * Return the fetch group. */ - R select(String properties); - - /** - * Set a FetchGroup to control what part of the object graph is loaded. - *

- * FetchGroup is immutable and threadsafe. We expect to create and store - * FetchGroup to a static final field and reuse the instance. - *

- * FetchGroup is an alternative to using select() and fetch() providing a nice - * clean separation between what a query should load and the query predicates. - * - *

{@code
-   *
-   * // immutable threadsafe
-   *
-   * static final FetchGroup fetchGroup =
-   *   QCustomer.forFetchGroup()
-   *     .shippingAddress.fetch()
-   *     .contacts.fetch()
-   *     .buildFetchGroup();
-   *
-   * List customers = new QCustomer()
-   *   .select(fetchGroup)
-   *   .findList();
-   *
-   * }
- * - * - *
{@code
-   *
-   * static final FetchGroup fetchGroup =
-   *   FetchGroup.of(Customer.class)
-   *     .select("name, status")
-   *     .fetch("contacts", "firstName, lastName, email")
-   *     .build();
-   *
-   * List customers = new QCustomer()
-   *   .select(fetchGroup)
-   *   .findList();
-   *
-   * }
- */ - R select(FetchGroup fetchGroup); + FetchGroup buildFetchGroup(); /** * Specify the properties to be loaded on the 'main' root level entity bean. @@ -180,364 +109,11 @@ public interface QueryBean { */ R select(Query.Property... properties); - /** - * Specify a path to load including all its properties. - * - *
{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *     // eager fetch the contacts
-   *     .fetch("contacts")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path of an associated (OneToOne, OneToMany, ManyToOne or ManyToMany) bean. - */ - R fetch(String path); - - /** - * Specify a path to load including all its properties using a "query join". - * - *
{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *     // eager fetch the contacts using a "query join"
-   *     .fetchQuery("contacts")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path of an associated (OneToOne, OneToMany, ManyToOne or ManyToMany) bean. - */ - R fetchQuery(String path); - - /** - * Specify a path to load from L2 cache including all its properties. - * - *
{@code
-   *
-   * List orders =
-   *     new QOrder()
-   *     // eager fetch the customer using L2 cache
-   *     .fetchCache("customer")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path to load from L2 cache. - */ - R fetchCache(String path); - - /** - * Specify a path and properties to load using a "query join". - * - *
{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *     // eager fetch contacts using a "query join"
-   *     .fetchQuery("contacts", "email, firstName, lastName")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path of an associated (OneToOne, OneToMany, ManyToOne or ManyToMany) bean. - * @param properties the properties to load for this path. - */ - R fetchQuery(String path, String properties); - - /** - * Specify a path and properties to load from L2 cache. - * - *
{@code
-   *
-   * List orders =
-   *     new QOrder()
-   *     // eager fetch the customer using L2 cache
-   *     .fetchCache("customer", "name,status")
-   *     .findList();
-   *
-   * }
- * - * @param path the property path to load from L2 cache. - * @param properties the properties to load for this path. - */ - R fetchCache(String path, String properties); - - /** - * Specify a path to fetch with its specific properties to include - * (aka partial object). - *

- * When you specify a join this means that property (associated bean(s)) will - * be fetched and populated. If you specify "*" then all the properties of the - * associated bean will be fetched and populated. You can specify a comma - * delimited list of the properties of that associated bean which means that - * only those properties are fetched and populated resulting in a - * "Partial Object" - a bean that only has some of its properties populated. - *

- *

{@code
-   *
-   * // query orders...
-   * List orders =
-   *     new QOrder()
-   *       .fetch("customer", "name, phoneNumber")
-   *       .fetch("customer.billingAddress", "*")
-   *       .findList();
-   *
-   * }
- *

- * If columns is null or "*" then all columns/properties for that path are fetched. - * - *

{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *     .select("name, status")
-   *     .fetch("contacts", "firstName,lastName,email")
-   *     .findList();
-   *
-   * }
- * - * @param path the path of an associated (OneToOne, OneToMany, ManyToOne or ManyToMany) bean. - * @param properties properties of the associated bean that you want to include in the - * fetch (* means all properties, null also means all properties). - */ - R fetch(String path, String properties); - - /** - * Additionally specify a FetchConfig to use a separate query or lazy loading - * to load this path. - *

- *

{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers =
-   *     new QCustomer()
-   *     .select("name, status")
-   *     .fetch("contacts", "firstName,lastName,email", new FetchConfig().lazy(10))
-   *     .findList();
-   *
-   * }
- */ - R fetch(String path, String properties, FetchConfig fetchConfig); - - /** - * Additionally specify a FetchConfig to specify a "query join" and or define - * the lazy loading query. - *

- *

{@code
-   *
-   * // fetch customers (their id, name and status)
-   * List customers =
-   *     new QCustomer()
-   *       // lazy fetch contacts with a batch size of 100
-   *       .fetch("contacts", new FetchConfig().lazy(100))
-   *       .findList();
-   *
-   * }
- */ - R fetch(String path, FetchConfig fetchConfig); - - /** - * Apply the path properties replacing the select and fetch clauses. - *

- * This is typically used when the PathProperties is applied to both the query and the JSON output. - */ - R apply(PathProperties pathProperties); - - /** - * Apply changes to the query conditional on the supplied predicate. - *

- * Typically, the changes are extra predicates etc. - * - * @param predicate The predicate which when true the changes are applied - * @param apply The changes to apply to the query - */ - R alsoIf(BooleanSupplier predicate, Consumer apply); - - /** - * Perform an 'As of' query using history tables to return the object graph - * as of a time in the past. - *

- * To perform this query the DB must have underlying history tables. - * - * @param asOf the date time in the past at which you want to view the data - */ - R asOf(Timestamp asOf); - - /** - * Execute the query against the draft set of tables. - */ - R asDraft(); - - /** - * Execute the query including soft deleted rows. - */ - R setIncludeSoftDeletes(); - /** * Add an expression to the WHERE or HAVING clause. */ R add(Expression expression); - /** - * Set root table alias. - */ - R alias(String alias); - - /** - * Set the maximum number of rows to return in the query. - * - * @param maxRows the maximum number of rows to return in the query. - */ - R setMaxRows(int maxRows); - - /** - * Set the first row to return for this query. - * - * @param firstRow the first row to include in the query result. - */ - R setFirstRow(int firstRow); - - /** - * Execute the query allowing properties with invalid JSON to be collected and not fail the query. - *

{@code
-   *
-   *   // fetch a bean with JSON content
-   *   EBasicJsonList bean= new QEBasicJsonList()
-   *       .id.equalTo(42)
-   *       .setAllowLoadErrors()  // collect errors into bean state if we have invalid JSON
-   *       .findOne();
-   *
-   *
-   *   // get the invalid JSON errors from the bean state
-   *   Map errors = server().getBeanState(bean).getLoadErrors();
-   *
-   *   // If this map is not empty tell we have invalid JSON
-   *   // and should try and fix the JSON content or inform the user
-   *
-   * }
- */ - R setAllowLoadErrors(); - - /** - * Explicitly specify whether to use AutoTune for this query. - *

- * 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. - *

- * 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. - */ - R setAutoTune(boolean autoTune); - - /** - * A hint which for JDBC translates to the Statement.fetchSize(). - *

- * 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. - */ - R setBufferFetchSizeHint(int fetchSize); - - /** - * Set whether this query uses DISTINCT. - */ - R setDistinct(boolean distinct); - - /** - * Set the index(es) to search for a document store which uses partitions. - *

- * For example, when executing a query against ElasticSearch with daily indexes we can - * explicitly specify the indexes to search against. - * - *

{@code
-   *
-   *   // explicitly specify the indexes to search
-   *   query.setDocIndexName("logstash-2016.11.5,logstash-2016.11.6")
-   *
-   *   // search today's index
-   *   query.setDocIndexName("$today")
-   *
-   *   // search the last 3 days
-   *   query.setDocIndexName("$last-3")
-   *
-   * }
- *

- * If the indexName is specified with ${daily} e.g. "logstash-${daily}" ... then we can use - * $today and $last-x as the search docIndexName like the examples below. - *

- *
{@code
-   *
-   *   // search today's index
-   *   query.setDocIndexName("$today")
-   *
-   *   // search the last 3 days
-   *   query.setDocIndexName("$last-3")
-   *
-   * }
- * - * @param indexName The index or indexes to search against - * @return This query - */ - R setDocIndexName(String indexName); - - /** - * Restrict the query to only return subtypes of the given inherit type. - *
{@code
-   *
-   *   List animals =
-   *     new QAnimal()
-   *       .name.startsWith("Fluffy")
-   *       .setInheritType(Cat.class)
-   *       .findList();
-   *
-   * }
- */ - R setInheritType(Class type); - - /** - * Set the base table to use for this query. - *

- * Typically this is used when a table has partitioning and we wish to specify a specific - * partition/table to query against. - *

- *
{@code
-   *
-   *   QOrder()
-   *   .setBaseTable("order_2019_05")
-   *   .status.equalTo(Status.NEW)
-   *   .findList();
-   *
-   * }
- */ - R setBaseTable(String baseTable); - - /** - * Execute the query with the given lock type and WAIT. - *

- * Note that forUpdate() is the same as - * withLock(LockType.UPDATE). - *

- * Provides us with the ability to explicitly use Postgres - * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. - */ - R withLock(Query.LockType lockType); - - /** - * Execute the query with the given lock type and lock wait. - *

- * Note that forUpdateNoWait() is the same as - * withLock(LockType.UPDATE, LockWait.NOWAIT). - *

- * Provides us with the ability to explicitly use Postgres - * SHARE, KEY SHARE, NO KEY UPDATE and UPDATE row locks. - */ - R withLock(Query.LockType lockType, Query.LockWait lockWait); - /** * Add EXISTS sub-query predicate. */ @@ -564,52 +140,6 @@ public interface QueryBean { */ R notExists(String sqlSubQuery, Object... bindValues); - /** - * Execute using "for update" clause which results in the DB locking the record. - */ - R forUpdate(); - - /** - * Execute using "for update" clause with "no wait" option. - *

- * This is typically a Postgres and Oracle only option at this stage. - */ - R forUpdateNoWait(); - - /** - * Execute using "for update" clause with "skip locked" option. - *

- * This is typically a Postgres and Oracle only option at this stage. - */ - R forUpdateSkipLocked(); - - /** - * Return this query as an UpdateQuery. - * - *

{@code
-   *
-   *   int rows =
-   *     new QCustomer()
-   *     .name.startsWith("Rob")
-   *     .organisation.id.equalTo(42)
-   *     .asUpdate()
-   *       .set("active", false)
-   *       .update()
-   *
-   * }
- * - * @return This query as an UpdateQuery - */ - UpdateQuery asUpdate(); - - /** - * Convert the query to a DTO bean query. - *

- * We effectively use the underlying ORM query to build the SQL and then execute - * and map it into DTO beans. - */ - DtoQuery asDto(Class dtoClass); - /** * Set the Id value to query. This is used with findOne(). *

@@ -660,158 +190,6 @@ public interface QueryBean { */ R setIdIn(Collection ids); - /** - * Set a label on the query. - *

- * This label can be used to help identify query performance metrics but we can also use - * profile location enhancement on Finders so for some that would be a better option. - */ - R setLabel(String label); - - /** - * Set a SQL query hint. - *

- * This results in an inline comment that immediately follows - * after the select keyword in the form: {@code /*+ hint *\/ } - */ - R setHint(String hint); - - /** - * Set the profile location. - *

- * This is typically set automatically via enhancement when profile location enhancement - * is turned on. It is generally not set by application code. - */ - R setProfileLocation(ProfileLocation profileLocation); - - /** - * Set the default lazy loading batch size to use. - *

- * 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 - */ - R setLazyLoadBatchSize(int lazyLoadBatchSize); - - /** - * Set the property to use as keys for a map. - *

- * If no property is set then the id property is used. - *

- *

{@code
-   *
-   * // Assuming sku is unique for products...
-   *
-   * Map productMap =
-   *     new QProduct()
-   *     // use sku for keys...
-   *     .setMapKey("sku")
-   *     .findMap();
-   *
-   * }
- * - * @param mapKey the property to use as keys for a map. - */ - R setMapKey(String mapKey); - - /** - * Specify the PersistenceContextScope to use for this query. - *

- * When this is not set the 'default' configured on {@link io.ebean.config.DatabaseConfig#setPersistenceContextScope(PersistenceContextScope)} - * is used - this value defaults to {@link PersistenceContextScope#TRANSACTION}. - *

- * Note that the same persistence Context is used for subsequent lazy loading and query join queries. - *

- * 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. - */ - R setPersistenceContextScope(PersistenceContextScope scope); - - /** - * Set RawSql to use for this query. - */ - R setRawSql(RawSql rawSql); - - /** - * When set to true when you want the returned beans to be read only. - */ - R setReadOnly(boolean readOnly); - - /** - * Set this to true to use the bean cache. - *

- * If the query result is in cache then by default this same instance is - * returned. In this sense it should be treated as a read only object graph. - */ - R setUseCache(boolean useCache); - - /** - * Set the mode to use the bean cache when executing this query. - *

- * 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. - *

- * By default, findList() with natural keys will not use the bean cache. In that - * case we need to explicitly use the bean cache. - */ - R setBeanCacheMode(CacheMode beanCacheMode); - - /** - * Set to true if this query should execute against the doc store. - *

- * When setting this you may also consider disabling lazy loading. - */ - R setUseDocStore(boolean useDocStore); - - /** - * Set true if you want to disable lazy loading. - *

- * That is, once the object graph is returned further lazy loading is disabled. - */ - R setDisableLazyLoading(boolean disableLazyLoading); - - /** - * Disable read auditing for this query. - *

- * 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. - */ - R setDisableReadAuditing(); - - /** - * Set this to true to use the query cache. - */ - R setUseQueryCache(boolean useCache); - - /** - * Set the {@link CacheMode} to use the query for executing this query. - */ - R setUseQueryCache(CacheMode cacheMode); - - /** - * Set a timeout on this query. - *

- * 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. - * - * @param secs the query timeout limit in seconds. Zero means there is no limit. - */ - R setTimeout(int secs); - - /** - * Returns the set of properties or paths that are unknown (do not map to known properties or paths). - *

- * Validate the query checking the where and orderBy expression paths to confirm if - * they represent valid properties or paths for the given bean type. - */ - Set validate(); - /** * Add raw expression with no parameters. *

@@ -963,15 +341,6 @@ public interface QueryBean { @Deprecated(since = "13.19", forRemoval = true) R order(); - /** - * Set the full raw order by clause replacing the existing order by clause if there is one. - *

- * This follows SQL syntax using commas between each property with the - * optional asc and desc keywords representing ascending and descending order - * respectively. - */ - R orderBy(String orderByClause); - /** * @deprecated migrate to {@link #orderBy(String)} */ @@ -1166,466 +535,6 @@ public interface QueryBean { */ R textQueryString(String query, TextQueryString options); - /** - * Execute the query using the given transaction. - */ - R usingTransaction(Transaction transaction); - - /** - * Execute the query using the given connection. - */ - R usingConnection(Connection connection); - - /** - * Ensure that the master DataSource is used if there is a read only data source - * being used (that is using a read replica database potentially with replication lag). - *

- * When the database is configured with a read-only DataSource via - * say {@link io.ebean.config.DatabaseConfig#setReadOnlyDataSource(DataSource)} then - * by default when a query is run without an active transaction, it uses the read-only data - * source. We we use {@code usingMaster()} to instead ensure that the query is executed - * against the master data source. - */ - R usingMaster(); - - /** - * Execute the query returning true if a row is found. - *

- * The query is executed using max rows of 1 and will only select the id property. - * This method is really just a convenient way to optimise a query to perform a - * 'does a row exist in the db' check. - * - *

Example using a query bean:

- *
{@code
-   *
-   *   boolean userExists =
-   *     new QContact()
-   *       .email.equalTo("rob@foo.com")
-   *       .exists();
-   *
-   * }
- * - *

Example:

- *
{@code
-   *
-   *   boolean userExists = query()
-   *     .where().eq("email", "rob@foo.com")
-   *     .exists();
-   *
-   * }
- * - * @return True if the query finds a matching row in the database - */ - boolean exists(); - - /** - * Execute the query returning either a single bean or null (if no matching - * bean is found). - *

- * If more than 1 row is found for this query then a PersistenceException is - * thrown. - *

- * This is useful when your predicates dictate that your query should only - * return 0 or 1 results. - *

- *

{@code
-   *
-   * // assuming the sku of products is unique...
-   * Product product =
-   *     new QProduct()
-   *         .sku.equalTo("aa113")
-   *         .findOne();
-   * ...
-   * }
- *

- * It is also useful with finding objects by their id when you want to specify - * further join information to optimise the query. - *

- *

{@code
-   *
-   * // Fetch order 42 and additionally fetch join its order details...
-   * Order order =
-   *     new QOrder()
-   *         .fetch("details") // eagerly load the order details
-   *         .id.equalTo(42)
-   *         .findOne();
-   *
-   * // the order details were eagerly loaded
-   * List details = order.getDetails();
-   * ...
-   * }
- */ - @Nullable - T findOne(); - - /** - * Execute the query returning an optional bean. - */ - Optional findOneOrEmpty(); - - /** - * Execute the query returning the list of objects. - *

- * This query will execute against the EbeanServer that was used to create it. - *

- *

{@code
-   *
-   * List customers =
-   *     new QCustomer()
-   *       .name.ilike("rob%")
-   *       .findList();
-   *
-   * }
- * - * @see Query#findList() - */ - List findList(); - - /** - * Execute the query returning the result as a Stream. - *

- * Note that this can support very large queries iterating - * any number of results. To do so internally it can use - * multiple persistence contexts. - *

- *
{@code
-   *
-   *  // use try with resources to ensure Stream is closed
-   *
-   *  try (Stream stream = query.findStream()) {
-   *    stream
-   *    .map(...)
-   *    .collect(...);
-   *  }
-   *
-   * }
- */ - Stream findStream(); - - /** - * Execute the query returning the set of objects. - *

- * This query will execute against the EbeanServer that was used to create it. - *

- *

{@code
-   *
-   * Set customers =
-   *     new QCustomer()
-   *       .name.ilike("rob%")
-   *       .findSet();
-   *
-   * }
- * - * @see Query#findSet() - */ - Set findSet(); - - /** - * Execute the query returning the list of Id's. - *

- * This query will execute against the EbeanServer that was used to create it. - * - * @see Query#findIds() - */ - List findIds(); - - /** - * Execute the query returning a map of the objects. - *

- * This query will execute against the EbeanServer that was used to create it. - *

- * You can use setMapKey() or asMapKey() to specify the property to be used as keys - * on the map. If one is not specified then the id property is used. - *

- *

{@code
-   *
-   * Map map =
-   *   new QProduct()
-   *     .sku.asMapKey()
-   *     .findMap();
-   *
-   * }
- * - * @see Query#findMap() - */ - Map findMap(); - - /** - * Execute the query iterating over the results. - *

- * 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. - *

- * Remember that with {@link QueryIterator} you must call {@link QueryIterator#close()} - * when you have finished iterating the results (typically in a finally block). - *

- * findEach() and findEachWhile() are preferred to findIterate() as they ensure - * the jdbc statement and resultSet are closed at the end of the iteration. - *

- * This query will execute against the EbeanServer that was used to create it. - *

- *
{@code
-   *
-   *  Query query =
-   *    new QCustomer()
-   *     .status.equalTo(Customer.Status.NEW)
-   *     .orderBy()
-   *       id.asc()
-   *     .query();
-   *
-   *  try (QueryIterator it = query.findIterate()) {
-   *    while (it.hasNext()) {
-   *      Customer customer = it.next();
-   *      // do something with customer ...
-   *    }
-   *  }
-   *
-   * }
- */ - QueryIterator findIterate(); - - /** - * Execute the query returning a list of values for a single property. - *

- *

Example

- *
{@code
-   *
-   *  List names =
-   *    new QCustomer()
-   *      .setDistinct(true)
-   *      .select(name)
-   *      .findSingleAttributeList();
-   *
-   * }
- * - * @return the list of values for the selected property - */ -
List findSingleAttributeList(); - - /** - * Execute the query returning a single value or null for a single property. - *

- *

Example

- *
{@code
-   *
-   *  LocalDate maxDate =
-   *    new QCustomer()
-   *      .select("max(startDate)")
-   *      .findSingleAttribute();
-   *
-   * }
- * - * @return a single value or null for the selected property - */ - @Nullable -
A findSingleAttribute(); - - /** - * Execute the query returning a single optional attribute value. - *

- *

Example

- *
{@code
-   *
-   *  Optional maybeName =
-   *    new QCustomer()
-   *      .select(name)
-   *      .id.eq(42)
-   *      .status.eq(NEW)
-   *      .findSingleAttributeOrEmpty();
-   *
-   * }
- * - * @return an optional value for the selected property - */ -
Optional findSingleAttributeOrEmpty(); - - /** - * Execute the query processing the beans one at a time. - *

- * 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) - *

- * 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. - *

- * 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. - *

- * This method is functionally equivalent to findIterate() but instead of using an - * iterator uses the Consumer interface which is better suited to use with closures. - * - *

{@code
-   *
-   *  new QCustomer()
-   *     .status.equalTo(Status.NEW)
-   *     .orderBy().id.asc()
-   *     .findEach((Customer customer) -> {
-   *
-   *       // do something with customer
-   *       System.out.println("-- visit " + customer);
-   *     });
-   *
-   * }
- * - * @param consumer the consumer used to process the queried beans. - */ - void findEach(Consumer consumer); - - /** - * Execute findEach streaming query batching the results for consuming. - *

- * This query execution will stream the results and is suited to consuming - * large numbers of results from the database. - *

- * Typically, we use this batch consumer when we want to do further processing on - * the beans and want to do that processing in batch form, for example - 100 at - * a time. - * - * @param batch The number of beans processed in the batch - * @param consumer Process the batch of beans - */ - void findEach(int batch, Consumer> consumer); - - /** - * Execute the query using callbacks to a visitor to process the resulting - * beans one at a time. - *

- * This method is functionally equivalent to findIterate() but instead of using an - * iterator uses the Predicate interface which is better suited to use with closures. - * - *

{@code
-   *
-   *  new QCustomer()
-   *     .status.equalTo(Status.NEW)
-   *     .orderBy().id.asc()
-   *     .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);
-   *     });
-   *
-   * }
- * - * @param consumer the consumer used to process the queried beans. - */ - void findEachWhile(Predicate consumer); - - /** - * Return versions of a @History entity bean. - *

- * 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. - */ - List> findVersions(); - - /** - * Return versions of a @History entity bean between a start and end timestamp. - *

- * 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. - */ - List> findVersionsBetween(Timestamp start, Timestamp end); - - /** - * Return the count of entities this query should return. - *

- * This is the number of 'top level' or 'root level' entities. - */ - int findCount(); - - /** - * Execute find row count query in a background thread. - *

- * 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). - * - * @return a Future object for the row count query - */ - FutureRowCount findFutureCount(); - - /** - * Execute find Id's query in a background thread. - *

- * 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). - * - * @return a Future object for the list of Id's - */ - FutureIds findFutureIds(); - - /** - * Execute find list query in a background thread. - *

- * 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. - * - * @return a Future object for the list result of the query - */ - FutureList findFutureList(); - - /** - * Return a PagedList for this query using firstRow and maxRows. - *

- * The benefit of using this over findList() is that it provides functionality to get the - * total row count etc. - *

- * If maxRows is not set on the query prior to calling findPagedList() then a - * PersistenceException is thrown. - *

- *

{@code
-   *
-   *  PagedList pagedList =
-   *    new QOrder()
-   *       .setFirstRow(50)
-   *       .setMaxRows(20)
-   *       .findPagedList();
-   *
-   *       // fetch the total row count in the background
-   *       pagedList.loadRowCount();
-   *
-   *       List orders = pagedList.getList();
-   *       int totalRowCount = pagedList.getTotalRowCount();
-   *
-   * }
- * - * @return The PagedList - */ - PagedList findPagedList(); - - /** - * Execute as a delete query deleting the 'root level' beans that match the predicates - * in the query. - *

- * Note that if the query includes joins then the generated delete statement may not be - * optimal depending on the database platform. - * - * @return the number of beans/rows that were deleted. - */ - int delete(); - - /** - * Return the sql that was generated for executing this query. - *

- * This is only available after the query has been executed and provided only - * for informational purposes. - */ - String getGeneratedSql(); - - /** - * Return the type of beans being queried. - */ - Class getBeanType(); - /** * Return the expression list that has been built for this query. */ @@ -1677,4 +586,10 @@ public interface QueryBean { * } */ ExpressionList havingClause(); + + /** + * Set the profile location of this query. This is used to relate query execution metrics + * back to a location like a specific line of code. + */ + R setProfileLocation(ProfileLocation profileLocation); } diff --git a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java index 73941a6e3..26b02f5d1 100644 --- a/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java +++ b/ebean-querybean/src/main/java/io/ebean/typequery/TQRootBean.java @@ -8,7 +8,6 @@ import io.ebean.search.TextCommonTerms; import io.ebean.search.TextQueryString; import io.ebean.search.TextSimple; import io.ebean.service.SpiFetchGroupQuery; -import io.ebean.text.PathProperties; import io.ebeaninternal.api.SpiQueryFetch; import io.ebeaninternal.server.util.ArrayStack; @@ -235,7 +234,19 @@ public abstract class TQRootBean implements QueryBean { } @Override - public final R apply(PathProperties pathProperties) { + public R fetchLazy(String path, String fetchProperties) { + query.fetchLazy(path, fetchProperties); + return root; + } + + @Override + public R fetchLazy(String path) { + query.fetchLazy(path); + return root; + } + + @Override + public final R apply(FetchPath pathProperties) { query.apply(pathProperties); return root; } @@ -450,6 +461,12 @@ public abstract class TQRootBean implements QueryBean { return root; } + @Override + public R setCountDistinct(CountDistinctOrder orderBy) { + query.setCountDistinct(orderBy); + return root; + } + @Override public final R setRawSql(RawSql rawSql) { query.setRawSql(rawSql); @@ -462,13 +479,6 @@ public abstract class TQRootBean implements QueryBean { return root; } - @Override - public final R setUseCache(boolean useCache) { - query.setUseCache(useCache); - return root; - } - - @Override public final R setBeanCacheMode(CacheMode beanCacheMode) { query.setBeanCacheMode(beanCacheMode); @@ -493,12 +503,6 @@ public abstract class TQRootBean implements QueryBean { return root; } - @Override - public final R setUseQueryCache(boolean useCache) { - query.setUseQueryCache(useCache); - return root; - } - @Override public final R setUseQueryCache(CacheMode cacheMode) { query.setUseQueryCache(cacheMode); @@ -565,6 +569,12 @@ public abstract class TQRootBean implements QueryBean { return root; } + @Override + public R setOrderBy(OrderBy orderBy) { + query.setOrderBy(orderBy); + return root; + } + @Override @Deprecated(since = "13.19", forRemoval = true) public final R order(String orderByClause) { @@ -696,6 +706,12 @@ public abstract class TQRootBean implements QueryBean { return root; } + @Override + public R usingDatabase(Database database) { + query.usingDatabase(database); + return root; + } + @Override public final R usingMaster() { query.usingMaster(); @@ -753,6 +769,11 @@ public abstract class TQRootBean implements QueryBean { return query.findSingleAttributeList(); } + @Override + public final Set findSingleAttributeSet() { + return query.findSingleAttributeSet(); + } + @Override @Nullable public final A findSingleAttribute() {