diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java index f860cd11c..3161ef96b 100644 --- a/src/main/java/com/avaje/ebean/Query.java +++ b/src/main/java/com/avaje/ebean/Query.java @@ -371,11 +371,13 @@ public interface Query { Query setDisableReadAuditing(); /** - * 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. + * Specify the properties to fetch on the root level entity bean in comma delimited format. *

- * You use {@link #fetch(String, String)} to specify specific properties to fetch + * 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
@@ -395,21 +397,17 @@ public interface Query {
   Query select(String fetchProperties);
 
   /**
-   * Specify a path to fetch with its specific properties to include
-   * (aka partial object).
+   * Specify a path to fetch eagerly including specific properties.
    * 

- * 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. + * 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 =
-   *     ebeanserver.find(Order.class)
+   *     ebeanServer.find(Order.class)
    *       // fetch the customer...
    *       // ... getting the customers name and phone number
    *       .fetch("customer", "name, phoneNumber")
@@ -419,8 +417,7 @@ public interface Query {
    *       .findList();
    * }
*

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

*
{@code
    *
@@ -433,12 +430,62 @@ public interface Query {
    *
    * }
* - * @param path the path of an associated (1-1,1-M,M-1,M-M) bean. + * @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, new FetchConfig().query())
+   *
+   * }
+ *

+ * 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 lazily (via batch lazy loading). + *

+ * This is the same as: + *

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

+ * 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. @@ -452,13 +499,17 @@ public interface Query { * .findList(); * * }
+ * + * @param path the property path we wish to fetch eagerly. */ - Query fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig); + Query fetch(String path, String fetchProperties, FetchConfig fetchConfig); /** - * Specify a path to load including all its properties. + * Specify a path to fetch eagerly including all its properties. *

- * The same as {@link #fetch(String, String)} with the fetchProperties as "*". + * 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
    *
@@ -471,10 +522,56 @@ public interface Query {
    *
    * }
* - * @param path the property of an associated (1-1,1-M,M-1,M-M) bean. + * @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, new FetchConfig().query())
+   *
+   * }
+ *

+ * 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 lazily (via batch lazy loading). + *

+ * This is the same as: + *

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

+ * 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. @@ -737,6 +834,7 @@ public interface Query { * Deprecated in favor of findCount(). *

* Return the count of entities this query should return. + * * @deprecated */ int findRowCount(); @@ -1199,6 +1297,16 @@ public interface Query { * 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); diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index dedc9f597..24bdea098 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -43,6 +43,10 @@ public class DefaultOrmQuery implements SpiQuery { public static final String DEFAULT_QUERY_NAME = "default"; + private static final FetchConfig FETCH_QUERY = new FetchConfig().query(); + + private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy(); + private final Class beanType; private final BeanDescriptor beanDescriptor; @@ -1013,6 +1017,16 @@ public class DefaultOrmQuery implements SpiQuery { return fetch(property, null, null); } + @Override + public Query fetchQuery(String property) { + return fetch(property, null, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property) { + return fetch(property, null, FETCH_LAZY); + } + @Override public DefaultOrmQuery fetch(String property, FetchConfig joinConfig) { return fetch(property, null, joinConfig); @@ -1023,6 +1037,16 @@ public class DefaultOrmQuery implements SpiQuery { return fetch(property, columns, null); } + @Override + public Query fetchQuery(String property, String columns) { + return fetch(property, columns, FETCH_QUERY); + } + + @Override + public Query fetchLazy(String property, String columns) { + return fetch(property, columns, FETCH_LAZY); + } + @Override public DefaultOrmQuery fetch(String property, String columns, FetchConfig config) { detail.fetch(property, columns, config);