* // Normal fetch join results in a single SQL query
- * List<Order> list = Ebean.find(Order.class).join("details").findList();
+ * List<Order> list = Ebean.find(Order.class).fetch("details").findList();
*
* // Find Orders join details using a single SQL query
*
@@ -51,8 +51,8 @@ import java.io.Serializable;
* // This will use 3 SQL queries to build this object graph
* List<Order> list =
* Ebean.find(Order.class)
- * .fetch("details", new JoinConfig().query())
- * .fetch("customer", new JoinConfig().query(5))
+ * .fetch("details", new FetchConfig().query())
+ * .fetch("customer", new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
@@ -70,7 +70,7 @@ import java.io.Serializable;
* .select("status, shipDate")
* .fetch("details", "quantity, price", new FetchConfig().query())
* .fetch("details.product", "sku, name")
- * .fetch("customer", "name", new FetchConfig().query(10))
+ * .fetch("customer", "name", new FetchConfig().queryFirst(5))
* .fetch("customer.contacts")
* .fetch("customer.shippingAddress")
* .findList();
@@ -96,13 +96,13 @@ import java.io.Serializable;
*
* List<Order> list =
* Ebean.find(Order.class)
- * .fetch("customer", new FetchConfig().query(3).lazy(10))
+ * .fetch("customer", new FetchConfig().query(10).lazy(5))
* .findList();
*
* // query 1) find order
- * // query 2) find customer where id in (?,?,?) // first 3 customers
+ * // query 2) find customer where id in (?,?,?,?,?,?,?,?,?,?) // first 10 customers
* // .. then if lazy loading of customers is invoked
- * // .. use a batch size of 10 to load the customers
+ * // .. use a batch size of 5 to load the customers
*
*
*
@@ -128,8 +128,8 @@ import java.io.Serializable;
* // .. use a batch size of 5 to load the customers
*
* find customer (name)
- * fetch contact (contactName, phone, email)
- * fetch shippingAddress (*)
+ * fetch customer.contacts (contactName, phone, email)
+ * fetch customer.shippingAddress (*)
* where id in (?,?,?,?,?)
*
*
@@ -159,6 +159,7 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig lazy() {
this.lazyBatchSize = 0;
+ this.queryAll = false;
return this;
}
@@ -170,11 +171,12 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig lazy(int lazyBatchSize) {
this.lazyBatchSize = lazyBatchSize;
+ this.queryAll = false;
return this;
}
/**
- * Specify that this path should be loaded as a separate query (rather than as
+ * Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
*
* This will use the default batch size for separate query which is 100.
@@ -187,14 +189,15 @@ public class FetchConfig implements Serializable {
}
/**
- * Specify that this path should be loaded as a separate query (rather than as
+ * Eagerly fetch the beans in this path as a separate query (rather than as
* part of the main query).
*
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
*
*
- * This will load all beans on this path eagerly.
+ * This will load all beans on this path eagerly unless a {@link #lazy(int)}
+ * is also used.
*
*
* @param queryBatchSize
@@ -202,12 +205,14 @@ public class FetchConfig implements Serializable {
*/
public FetchConfig query(int queryBatchSize) {
this.queryBatchSize = queryBatchSize;
- this.queryAll = true;
+ // queryAll true as long as a lazy batch size has not already been set
+ this.queryAll = (lazyBatchSize == -1);
return this;
}
/**
- * Similar to {@link #query(int)} but only fetches the first batch.
+ * Eagerly fetch the first batch of beans on this path.
+ * This is similar to {@link #query(int)} but only fetches the first batch.
*
* If there are more parent beans than the batch size then they will not be
* loaded eagerly but instead use lazy loading.
@@ -228,7 +233,7 @@ public class FetchConfig implements Serializable {
public int getLazyBatchSize() {
return lazyBatchSize;
}
-
+
/**
* Return the batch size for separate query load.
*/
diff --git a/src/main/java/com/avaje/ebean/OrderBy.java b/src/main/java/com/avaje/ebean/OrderBy.java
index 38da01765..734960217 100644
--- a/src/main/java/com/avaje/ebean/OrderBy.java
+++ b/src/main/java/com/avaje/ebean/OrderBy.java
@@ -32,6 +32,10 @@ public final class OrderBy implements Serializable {
public OrderBy() {
this.list = new ArrayList(2);
}
+
+ private OrderBy(List list) {
+ this.list = list;
+ }
/**
* Create an orderBy parsing the order by clause.
@@ -81,6 +85,17 @@ public final class OrderBy implements Serializable {
return query;
}
+ /**
+ * Return a copy of this OrderBy with the path trimmed.
+ */
+ public OrderBy copyWithTrim(String path) {
+ List newList = new ArrayList(list.size());
+ for (int i = 0; i < list.size(); i++) {
+ newList.add(list.get(i).copyWithTrim(path));
+ }
+ return new OrderBy(newList);
+ }
+
/**
* Return the properties for this OrderBy.
*/
@@ -196,6 +211,13 @@ public final class OrderBy implements Serializable {
this.ascending = ascending;
}
+ /**
+ * Return a copy of this Property with the path trimmed.
+ */
+ public Property copyWithTrim(String path) {
+ return new Property(property.substring(path.length() + 1), ascending);
+ }
+
protected int hash() {
int hc = property.hashCode();
hc = hc * 31 + (ascending ? 0 : 1);
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanBuffer.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanBuffer.java
new file mode 100644
index 000000000..f33c2181b
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanBuffer.java
@@ -0,0 +1,24 @@
+package com.avaje.ebeaninternal.api;
+
+import java.util.List;
+
+import com.avaje.ebean.bean.EntityBeanIntercept;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+
+/**
+ * A buffer of beans for batch lazy loading and secondary query loading.
+ */
+public interface LoadBeanBuffer {
+
+ public List getBatch();
+
+ public BeanDescriptor> getBeanDescriptor();
+
+ public PersistenceContext getPersistenceContext();
+
+ public String getFullPath();
+
+ public void configureQuery(SpiQuery> query, String lazyLoadProperty);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java
index e5d711260..cb99e4ff2 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanContext.java
@@ -1,39 +1,10 @@
package com.avaje.ebeaninternal.api;
-import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Controls the loading of ManyToOne and OneToOne relationships.
- *
- * @author rbygrave
*/
public interface LoadBeanContext extends LoadSecondaryQuery {
- /**
- * Configure the query to load beans for this node/path.
- */
- public void configureQuery(SpiQuery> query, String lazyLoadProperty);
-
- /**
- * Return the full path of this node from the root object.
- */
- public String getFullPath();
-
- /**
- * Return the persistence context used for all queries
- * related to this object graph.
- */
- public PersistenceContext getPersistenceContext();
-
- /**
- * Return the BeanDescriptor for beans for this node.
- */
- public BeanDescriptor> getBeanDescriptor();
-
- /**
- * Return the batchSize used for lazy loading beans.
- */
- public int getBatchSize();
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java
index ab4c94c2b..f5a8006d7 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadBeanRequest.java
@@ -9,55 +9,52 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
* Request for loading ManyToOne and OneToOne relationships.
*/
public class LoadBeanRequest extends LoadRequest {
-
- private final List batch;
- private final LoadBeanContext loadContext;
-
- private final String lazyLoadProperty;
-
- private final boolean loadCache;
-
- public LoadBeanRequest(LoadBeanContext loadContext, List batch,
- Transaction transaction, int batchSize, boolean lazy, String lazyLoadProperty, boolean loadCache) {
-
- super(transaction, batchSize, lazy);
- this.loadContext = loadContext;
- this.batch = batch;
- this.lazyLoadProperty = lazyLoadProperty;
- this.loadCache = loadCache;
- }
-
- public boolean isLoadCache() {
- return loadCache;
- }
+ private final List batch;
- public String getDescription() {
- String fullPath = loadContext.getFullPath();
- String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
- + batch.size();
- return s;
- }
+ private final LoadBeanBuffer LoadBuffer;
- /**
- * Return the batch of beans to actually load.
- */
- public List getBatch() {
- return batch;
- }
+ private final String lazyLoadProperty;
- /**
- * Return the load context.
- */
- public LoadBeanContext getLoadContext() {
- return loadContext;
- }
+ private final boolean loadCache;
+
+ public LoadBeanRequest(LoadBeanBuffer LoadBuffer, Transaction transaction, boolean lazy, String lazyLoadProperty,
+ boolean loadCache) {
+
+ super(transaction, lazy);
+ this.LoadBuffer = LoadBuffer;
+ this.batch = LoadBuffer.getBatch();
+ this.lazyLoadProperty = lazyLoadProperty;
+ this.loadCache = loadCache;
+ }
+
+ public boolean isLoadCache() {
+ return loadCache;
+ }
+
+ public String getDescription() {
+ return "path:" + LoadBuffer.getFullPath() + " batch:" + batch.size();
+ }
+
+ /**
+ * Return the batch of beans to actually load.
+ */
+ public List getBatch() {
+ return batch;
+ }
+
+ /**
+ * Return the load context.
+ */
+ public LoadBeanBuffer getLoadContext() {
+ return LoadBuffer;
+ }
+
+ /**
+ * Return the property that invoked the lazy loading.
+ */
+ public String getLazyLoadProperty() {
+ return lazyLoadProperty;
+ }
- /**
- * Return the property that invoked the lazy loading.
- */
- public String getLazyLoadProperty() {
- return lazyLoadProperty;
- }
-
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyBuffer.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyBuffer.java
new file mode 100644
index 000000000..75f1b9112
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyBuffer.java
@@ -0,0 +1,30 @@
+package com.avaje.ebeaninternal.api;
+
+import java.util.List;
+
+import com.avaje.ebean.bean.BeanCollection;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
+
+/**
+ * A buffer of bean collections for batch lazy loading and secondary query loading.
+ */
+public interface LoadManyBuffer {
+
+ public List> getBatch();
+
+ public BeanPropertyAssocMany> getBeanProperty();
+
+ public ObjectGraphNode getObjectGraphNode();
+
+ public BeanDescriptor> getBeanDescriptor();
+
+ public PersistenceContext getPersistenceContext();
+
+ public String getFullPath();
+
+ public void configureQuery(SpiQuery> query);
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java
index 543bde12c..72ff7b107 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyContext.java
@@ -1,54 +1,9 @@
package com.avaje.ebeaninternal.api;
-import com.avaje.ebean.bean.ObjectGraphNode;
-import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
-import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
/**
* Controls the loading of OneToMany and ManyToMany relationships.
- *
- * @author rbygrave
*/
public interface LoadManyContext extends LoadSecondaryQuery {
-
- /**
- * Configure the query to load beans for this node/path.
- */
- public void configureQuery(SpiQuery> query);
-
- /**
- * Return the full path of this node from the root object.
- */
- public String getFullPath();
-
- /**
- * Return the node location for this node/path.
- */
- public ObjectGraphNode getObjectGraphNode();
-
-
- /**
- * Return the persistence context used for all queries
- * related to this object graph.
- */
- public PersistenceContext getPersistenceContext();
-
- /**
- * Return the batchSize used for lazy loading beans.
- */
- public int getBatchSize();
-
- /**
- * Return the BeanDescriptor for beans for this node.
- */
- public BeanDescriptor> getBeanDescriptor();
-
- /**
- * Return the associated Many bean property.
- */
- public BeanPropertyAssocMany> getBeanProperty();
-
-
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java
index 1bbbaf51b..e053a1311 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadManyRequest.java
@@ -13,28 +13,24 @@ public class LoadManyRequest extends LoadRequest {
private final List> batch;
- private final LoadManyContext loadContext;
+ private final LoadManyBuffer loadContext;
private final boolean onlyIds;
private final boolean loadCache;
- public LoadManyRequest(LoadManyContext loadContext,
- List> batch, Transaction transaction,
- int batchSize, boolean lazy, boolean onlyIds, boolean loadCache) {
+ public LoadManyRequest(LoadManyBuffer loadContext, Transaction transaction, int batchSize, boolean lazy,
+ boolean onlyIds, boolean loadCache) {
- super(transaction, batchSize, lazy);
+ super(transaction, lazy);
this.loadContext = loadContext;
- this.batch = batch;
+ this.batch = loadContext.getBatch();
this.onlyIds = onlyIds;
this.loadCache = loadCache;
}
public String getDescription() {
- String fullPath = loadContext.getFullPath();
- String s = "path:" + fullPath + " batch:" + batchSize + " actual:"
- + batch.size();
- return s;
+ return "path:" + loadContext.getFullPath() + " size:"+ batch.size();
}
/**
@@ -47,7 +43,7 @@ public class LoadManyRequest extends LoadRequest {
/**
* Return the load context.
*/
- public LoadManyContext getLoadContext() {
+ public LoadManyBuffer getLoadContext() {
return loadContext;
}
diff --git a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java
index 23dc372e5..2364ce71e 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/LoadRequest.java
@@ -9,18 +9,14 @@ public abstract class LoadRequest {
protected final boolean lazy;
- protected final int batchSize;
-
protected final Transaction transaction;
- public LoadRequest(Transaction transaction, int batchSize, boolean lazy) {
+ public LoadRequest(Transaction transaction, boolean lazy) {
this.transaction = transaction;
- this.batchSize = batchSize;
this.lazy = lazy;
}
-
/**
* Return true if this is a lazy load and false if it is a secondary query.
*/
@@ -28,13 +24,6 @@ public abstract class LoadRequest {
return lazy;
}
- /**
- * Return the requested batch size.
- */
- public int getBatchSize() {
- return batchSize;
- }
-
/**
* Return the transaction to use if this is a secondary query.
*
diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
index e1a9fb49c..52190c5e0 100644
--- a/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/api/SpiExpressionList.java
@@ -18,10 +18,10 @@ public interface SpiExpressionList extends ExpressionList {
*/
public List getUnderlyingList();
- /**
- * Trim the path for filterMany() expressions.
- */
- public void trimPath(int prefixTrim);
+ /**
+ * Return a copy of the ExpressionList with the path trimmed for filterMany() expressions.
+ */
+ public SpiExpressionList> trimPath(int prefixTrim);
/**
* Restore the ExpressionFactory after deserialisation.
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java
index 0bd05ca18..3a4677646 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultBeanLoader.java
@@ -15,10 +15,10 @@ import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebeaninternal.api.LoadBeanContext;
+import com.avaje.ebeaninternal.api.LoadBeanBuffer;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
-import com.avaje.ebeaninternal.api.LoadManyContext;
import com.avaje.ebeaninternal.api.LoadManyRequest;
+import com.avaje.ebeaninternal.api.LoadManyBuffer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
@@ -27,8 +27,6 @@ import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
/**
* Helper to handle lazy loading and refreshing of beans.
- *
- * @author rbygrave
*/
public class DefaultBeanLoader {
@@ -50,32 +48,29 @@ public class DefaultBeanLoader {
* re-use the query plan cache and get DB statement re-use.
*
*/
- private int getBatchSize(int batchListSize, int requestedBatchSize) {
- if (batchListSize == requestedBatchSize) {
- return batchListSize;
- }
- if (batchListSize == 1) {
+ private int getBatchSize(int batchSize) {
+
+ if (batchSize == 1) {
// there is only one bean/collection to load
return 1;
}
- if (requestedBatchSize <= 5) {
+ if (batchSize <= 5) {
// anything less than 5 becomes 5
return 5;
}
- if (batchListSize <= 10 || requestedBatchSize <= 10) {
- // 10 or less to load
- // ... or we wanted a batch size between 6 and 10
+ if (batchSize <= 10) {
return 10;
}
- if (batchListSize <= 20 || requestedBatchSize <= 20) {
- // 20 or less to load
- // ... or we wanted a batch size between 11 and 20
+ if (batchSize <= 20) {
return 20;
}
- if (batchListSize <= 50) {
+ if (batchSize <= 50) {
return 50;
}
- return requestedBatchSize;
+ if (batchSize <= 100) {
+ return 100;
+ }
+ return batchSize;
}
public void refreshMany(Object parentBean, String propertyName) {
@@ -86,9 +81,9 @@ public class DefaultBeanLoader {
List> batch = loadRequest.getBatch();
- int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
+ int batchSize = getBatchSize(batch.size());
- LoadManyContext ctx = loadRequest.getLoadContext();
+ LoadManyBuffer ctx = loadRequest.getLoadContext();
BeanPropertyAssocMany> many = ctx.getBeanProperty();
PersistenceContext pc = ctx.getPersistenceContext();
@@ -154,14 +149,14 @@ public class DefaultBeanLoader {
}
}
- public void loadMany(BeanCollection> bc, LoadManyContext ctx, boolean onlyIds) {
+ public void loadMany(BeanCollection> bc, boolean onlyIds) {
Object parentBean = bc.getOwnerBean();
String propertyName = bc.getPropertyName();
- ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
+ //ObjectGraphNode node = ctx == null ? null : ctx.getObjectGraphNode();
- loadManyInternal(parentBean, propertyName, null, false, node, onlyIds);
+ loadManyInternal(parentBean, propertyName, null, false, null, onlyIds);
}
public void refreshMany(Object parentBean, String propertyName, Transaction t) {
@@ -170,17 +165,15 @@ public class DefaultBeanLoader {
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
- EntityBeanIntercept ebi = null;
- PersistenceContext pc = null;
- BeanCollection> beanCollection = null;
- ExpressionList> filterMany = null;
-
- ebi = ((EntityBean) parentBean)._ebean_getIntercept();
- pc = ebi.getPersistenceContext();
+ EntityBeanIntercept ebi = ((EntityBean) parentBean)._ebean_getIntercept();
+ PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor> parentDesc = server.getBeanDescriptor(parentBean.getClass());
BeanPropertyAssocMany> many = (BeanPropertyAssocMany>) parentDesc.getBeanProperty(propertyName);
+ BeanCollection> beanCollection = null;
+ ExpressionList> filterMany = null;
+
Object currentValue = many.getValue(parentBean);
if (currentValue instanceof BeanCollection>) {
beanCollection = (BeanCollection>) currentValue;
@@ -270,9 +263,9 @@ public class DefaultBeanLoader {
throw new RuntimeException("Nothing in batch?");
}
- int batchSize = getBatchSize(batch.size(), loadRequest.getBatchSize());
+ int batchSize = getBatchSize(batch.size());
- LoadBeanContext ctx = loadRequest.getLoadContext();
+ LoadBeanBuffer ctx = loadRequest.getLoadContext();
BeanDescriptor> desc = ctx.getBeanDescriptor();
Class> beanType = desc.getBeanType();
@@ -349,7 +342,6 @@ public class DefaultBeanLoader {
ebis[i].setLazyLoadFailure();
}
}
-
}
public void refresh(Object bean) {
@@ -362,7 +354,6 @@ public class DefaultBeanLoader {
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
-
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
PersistenceContext pc = ebi.getPersistenceContext();
diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
index ab9a46d53..d4db15306 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java
@@ -155,7 +155,7 @@ public final class DefaultServer implements SpiEbeanServer {
private final CQueryEngine cqueryEngine;
- @Deprecated
+ //@Deprecated
private DdlGenerator ddlGenerator;
private final ExpressionFactory expressionFactory;
@@ -188,14 +188,16 @@ public final class DefaultServer implements SpiEbeanServer {
*/
private int lazyLoadBatchSize;
- /** The query batch size */
+ /**
+ * The query batch size
+ */
private int queryBatchSize;
+
/**
* JDBC driver specific handling for JDBC batch execution.
*/
private PstmtBatch pstmtBatch;
-
/**
* holds plugins (e.g. ddl generator) detected by the service loader
*/
@@ -502,7 +504,7 @@ public final class DefaultServer implements SpiEbeanServer {
public void loadMany(BeanCollection> bc, boolean onlyIds) {
- beanLoader.loadMany(bc, null, onlyIds);
+ beanLoader.loadMany(bc, onlyIds);
}
public void refresh(Object bean) {
diff --git a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java
index 3ef049285..84c2ba26b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/expression/FilterExprPath.java
@@ -5,38 +5,36 @@ import java.io.Serializable;
/**
* This is the path prefix for filterMany.
*
- * The actual path can change due to FetchConfig query joins that proceed
- * the query that includes the filterMany.
+ * The actual path can change due to FetchConfig query joins that proceed the
+ * query that includes the filterMany.
*
- *
- * @author rbygrave
*/
public class FilterExprPath implements Serializable {
- private static final long serialVersionUID = -6420905565372842018L;
-
- /**
- * The path of the filterMany.
- */
- private String path;
-
- public FilterExprPath(String path){
- this.path = path;
- }
-
- /**
- * Trim off leading part of the path due to a
- * proceeding (earlier) query join etc.
- */
- public void trimPath(int prefixTrim) {
- path = path.substring(prefixTrim);
- }
+ private static final long serialVersionUID = -6420905565372842018L;
- /**
- * Return the path. This is a prefix used in the filterMany expressions.
- */
- public String getPath() {
- return path;
- }
+ /**
+ * The path of the filterMany.
+ */
+ private String path;
+
+ public FilterExprPath(String path) {
+ this.path = path;
+ }
+
+ /**
+ * Return a copy of the FilterExprPath trimming off leading part of the path
+ * due to a proceeding (earlier) query join etc.
+ */
+ public FilterExprPath trimPath(int prefixTrim) {
+ return new FilterExprPath(path.substring(prefixTrim));
+ }
+
+ /**
+ * Return the path. This is a prefix used in the filterMany expressions.
+ */
+ public String getPath() {
+ return path;
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java
new file mode 100644
index 000000000..674339a27
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBaseContext.java
@@ -0,0 +1,98 @@
+package com.avaje.ebeaninternal.server.loadcontext;
+
+import com.avaje.ebean.FetchConfig;
+import com.avaje.ebean.bean.ObjectGraphNode;
+import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
+import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
+
+/**
+ * Base class for Bean and BeanCollection loading (lazy loading and query join loading).
+ */
+public abstract class DLoadBaseContext {
+
+ protected final DLoadContext parent;
+
+ protected final BeanDescriptor> desc;
+
+ protected final String path;
+
+ protected final String fullPath;
+
+ protected final OrmQueryProperties queryProps;
+
+ protected final boolean hitCache;
+
+ protected final String serverName;
+
+ protected final int firstBatchSize;
+
+ protected final int secondaryBatchSize;
+
+ protected final ObjectGraphNode objectGraphNode;
+
+ protected final boolean queryFetch;
+
+
+ public DLoadBaseContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
+
+ this.parent = parent;
+ this.serverName = parent.getEbeanServer().getName();
+ this.desc = desc;
+ this.queryProps = queryProps;
+ this.path = path;
+ this.fullPath = parent.getFullPath(path);
+
+ this.hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching();
+
+ this.objectGraphNode = parent.getObjectGraphNode(path);
+
+ this.queryFetch = queryProps != null && queryProps.isQueryFetch();
+ this.firstBatchSize = initFirstBatchSize(defaultBatchSize, queryProps);
+ this.secondaryBatchSize = initSecondaryBatchSize(defaultBatchSize, firstBatchSize, queryProps);
+ }
+
+ private int initFirstBatchSize(int batchSize, OrmQueryProperties queryProps) {
+ if (queryProps == null) {
+ return batchSize;
+ }
+ FetchConfig fetchConfig = queryProps.getFetchConfig();
+ if (fetchConfig == null) {
+ return batchSize;
+ }
+
+ int queryBatchSize = fetchConfig.getQueryBatchSize();
+ if (queryBatchSize == -1) {
+ // not eager query fetch, just lazy loading
+ return batchSize;
+
+ } else if (queryBatchSize == 0) {
+ // default query fetch batch size is 100
+ return 100;
+
+ } else {
+ return queryBatchSize;
+ }
+ }
+
+ private int initSecondaryBatchSize(int defaultBatchSize, int firstBatchSize, OrmQueryProperties queryProps) {
+ if (queryProps == null) {
+ return defaultBatchSize;
+ }
+ FetchConfig fetchConfig = queryProps.getFetchConfig();
+ if (fetchConfig == null) {
+ return defaultBatchSize;
+ }
+ if (fetchConfig.isQueryAll()) {
+ return firstBatchSize;
+ }
+
+ int lazyBatchSize = fetchConfig.getLazyBatchSize();
+ return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
+ }
+
+ protected PersistenceContext getPersistenceContext() {
+ return parent.getPersistenceContext();
+ }
+
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
index a85c5a0c2..cd6d7264b 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java
@@ -1,69 +1,44 @@
package com.avaje.ebeaninternal.server.loadcontext;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import com.avaje.ebean.bean.BeanLoader;
-import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
-import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
+import com.avaje.ebeaninternal.api.LoadBeanBuffer;
import com.avaje.ebeaninternal.api.LoadBeanContext;
import com.avaje.ebeaninternal.api.LoadBeanRequest;
-import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Default implementation of LoadBeanContext.
- *
*/
-public class DLoadBeanContext implements LoadBeanContext, BeanLoader {
-
- private static final Logger logger = LoggerFactory.getLogger(DLoadBeanContext.class);
+public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext{
- protected final DLoadContext parent;
+ private List bufferList;
+
+ private LoadBuffer currentBuffer;
- protected final BeanDescriptor> desc;
-
- protected final String path;
-
- protected final String fullPath;
-
- private final DLoadList weakList;
-
- private final OrmQueryProperties queryProps;
-
- private int batchSize;
-
- public DLoadBeanContext(DLoadContext parent, BeanDescriptor> desc, String path, int batchSize,
- OrmQueryProperties queryProps, DLoadList weakList) {
+ public DLoadBeanContext(DLoadContext parent, BeanDescriptor> desc, String path, int defaultBatchSize, OrmQueryProperties queryProps) {
- this.parent = parent;
- this.desc = desc;
- this.path = path;
- this.batchSize = batchSize;
- this.queryProps = queryProps;
- this.weakList = weakList;
-
- if (parent.getRelativePath() == null) {
- this.fullPath = path;
- } else {
- this.fullPath = parent.getRelativePath() + "." + path;
- }
+ super(parent, desc, path, defaultBatchSize, queryProps);
+
+ this.currentBuffer = createBuffer(firstBatchSize);
+ this.bufferList = queryFetch ? new ArrayList() : null;
}
-
- public void configureQuery(SpiQuery> query, String lazyLoadProperty) {
+
+ protected void configureQuery(SpiQuery> query, String lazyLoadProperty) {
// propagate the readOnly state
if (parent.isReadOnly() != null) {
query.setReadOnly(parent.isReadOnly());
}
- query.setParentNode(getObjectGraphNode());
+ query.setParentNode(objectGraphNode);
query.setLazyLoadProperty(lazyLoadProperty);
if (queryProps != null) {
@@ -74,181 +49,131 @@ public class DLoadBeanContext implements LoadBeanContext, BeanLoader {
}
}
- public String getFullPath() {
- return fullPath;
- }
-
- public PersistenceContext getPersistenceContext() {
- return parent.getPersistenceContext();
- }
+ protected void register(EntityBeanIntercept ebi){
- public OrmQueryProperties getQueryProps() {
- return queryProps;
- }
-
- public ObjectGraphNode getObjectGraphNode() {
- return parent.getObjectGraphNode(path);
- }
-
- public String getPath() {
- return path;
- }
-
- public String getName() {
- return parent.getEbeanServer().getName();
- }
-
- public int getBatchSize() {
- return batchSize;
- }
-
- public void setBatchSize(int batchSize) {
- this.batchSize = batchSize;
- }
-
- public BeanDescriptor> getBeanDescriptor() {
- return desc;
- }
-
- public LoadContext getGraphContext() {
- return parent;
- }
-
- public void register(EntityBeanIntercept ebi){
- int pos = weakList.add(ebi);
- ebi.setBeanLoader(pos, this, parent.getPersistenceContext());
- }
-
- /**
- * Check if we can load the bean from L2 cache. If so avoid loading from the DB.
- */
- private boolean loadBeanFromCache(EntityBeanIntercept ebi, int position) {
-
- if (!desc.loadFromCache(ebi)) {
- return false;
- }
- // we loaded the bean from cache
- weakList.removeEntry(position);
- if (logger.isTraceEnabled()) {
- logger.trace("Loading path:" + fullPath + " - bean loaded from L2 cache, position[" + position + "]");
+ ebi.setBeanLoader(0, currentBuffer, getPersistenceContext());
+ if (currentBuffer.add(ebi)) {
+ // the currentBuffer is full so create another one
+ currentBuffer = createBuffer(secondaryBatchSize);
}
- return true;
- }
-
- /**
- * Load this bean and potentially a batch of similar beans.
- */
- public void loadBean(EntityBeanIntercept ebi) {
-
- // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
-
- if (desc.lazyLoadMany(ebi)) {
- // lazy load property was a Many
- return;
- }
-
- int position = ebi.getBeanLoaderIndex();
- boolean hitCache = !parent.isExcludeBeanCache() && desc.isBeanCaching();
-
- if (hitCache && loadBeanFromCache(ebi, position)) {
- // successfully hit the L2 cache so don't invoke DB lazy loading
- return;
- }
-
- // Get a batch of beans to lazy load
- List batch = null;
- try {
- batch = weakList.getLoadBatch(position, batchSize);
- } catch (IllegalStateException e) {
- logger.error("type[" + desc.getFullName() + "] fullPath[" + fullPath + "] batchSize[" + batchSize + "]", e);
- }
-
- if (hitCache && batchSize > 1) {
- // Check each of the beans in the batch to see if they are in the L2 cache.
- // Add more as necessary to make up our batch that will be loaded.
- batch = loadBeanCheckBatch(batch);
- }
-
- if (logger.isTraceEnabled()) {
- for (int i = 0; i < batch.size(); i++) {
-
- EntityBeanIntercept entityBeanIntercept = batch.get(i);
- EntityBean owner = entityBeanIntercept.getOwner();
- Object id = desc.getId(owner);
-
- logger.trace("LoadBean type["+owner.getClass().getName()+"] fullPath["+fullPath+"] id["+id+"] batchIndex["+i+"] beanLoaderIndex["+entityBeanIntercept.getBeanLoaderIndex()+"]");
- }
- }
-
- LoadBeanRequest req = new LoadBeanRequest(this, batch, null, batchSize, true, ebi.getLazyLoadProperty(), hitCache);
- parent.getEbeanServer().loadBean(req);
+ }
- }
-
- /**
- * Check each of the beans in the batch to see if they are in the cache.
- * Get more beans out as necessary to get our desired batch size.
- */
- private List loadBeanCheckBatch(List batch) {
-
-
- List actualLoadBatch = new ArrayList(batchSize);
- List batchToCheck = batch;
-
- int loadedFromCache = 0;
-
- while (true) {
- // check each bean (not already checked) to see if it is in the cache
- for (int i = 0; i < batchToCheck.size(); i++) {
- if (!desc.loadFromCache(batchToCheck.get(i))) {
- actualLoadBatch.add(batchToCheck.get(i));
- } else {
- loadedFromCache++;
- if (logger.isTraceEnabled()) {
- logger.trace( "Loading path:" + fullPath + " - bean loaded from L2 cache(batch)");
- }
- }
- }
-
- if (batchToCheck.isEmpty()) {
- // we have exhausted all the beans that need lazy loading
- break;
- }
- int more = batchSize - actualLoadBatch.size();
- if (more <= 0 || loadedFromCache > 500) {
- break;
- }
- // get some more to check as we loaded some from L2 cache
- batchToCheck = weakList.getNextBatch(more);
- }
- return actualLoadBatch;
- }
+ private LoadBuffer createBuffer(int size) {
+ LoadBuffer buffer = new LoadBuffer(this, size);
+ if (bufferList != null) {
+ bufferList.add(buffer);
+ }
+ return buffer;
+ }
public void loadSecondaryQuery(OrmQueryRequest> parentRequest, int requestedBatchSize, boolean all) {
+ if (!queryFetch) {
+ throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
+ }
synchronized (this) {
- do {
- List batch = weakList.getNextBatch(requestedBatchSize);
- if (batch.size() == 0) {
- // there are no beans to load
- if (logger.isTraceEnabled()) {
- logger.trace("Loading path:" + fullPath + " - no more beans to load");
+
+ if (bufferList != null) {
+ for (LoadBuffer loadBuffer : bufferList) {
+ if (!loadBuffer.list.isEmpty()) {
+ boolean loadCache = false;
+ LoadBeanRequest req = new LoadBeanRequest(loadBuffer, parentRequest.getTransaction(), false, null, loadCache);
+
+ parent.getEbeanServer().loadBean(req);
+ if (!queryProps.isQueryFetchAll()) {
+ // Stop - only fetch the first batch ... the rest will be lazy loaded
+ break;
+ }
}
- return;
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
}
- boolean loadCache = false;
- LoadBeanRequest req = new LoadBeanRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, null, loadCache);
+ }
+ }
+ }
+
- if (logger.isTraceEnabled()) {
- logger.trace("Loading path:" + fullPath + " - secondary query batch load [" + batch.size() + "] beans");
+ /**
+ * A buffer for batch loading beans on a given path.
+ */
+ public static class LoadBuffer implements BeanLoader, LoadBeanBuffer {
+
+ private final DLoadBeanContext context;
+ private final int batchSize;
+ private final List list;
+
+ public LoadBuffer(DLoadBeanContext context, int batchSize) {
+ this.context = context;
+ this.batchSize = batchSize;
+ this.list = new ArrayList(batchSize);
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public boolean add(EntityBeanIntercept ebi) {
+ list.add(ebi);
+ return batchSize == list.size();
+ }
+
+ @Override
+ public List getBatch() {
+ return list;
+ }
+
+ @Override
+ public String getName() {
+ return context.serverName;
+ }
+
+ @Override
+ public BeanDescriptor> getBeanDescriptor() {
+ return context.desc;
+ }
+
+ @Override
+ public PersistenceContext getPersistenceContext() {
+ return context.getPersistenceContext();
+ }
+
+ @Override
+ public void configureQuery(SpiQuery> query, String lazyLoadProperty) {
+ context.configureQuery(query, lazyLoadProperty);
+ }
+
+ @Override
+ public void loadBean(EntityBeanIntercept ebi) {
+ // A synchronized (this) is effectively held by EntityBeanIntercept.loadBean()
+
+ if (context.desc.lazyLoadMany(ebi)) {
+ // lazy load property was a Many
+ return;
+ }
+
+ if (context.hitCache && context.desc.loadFromCache(ebi)) {
+ // successfully hit the L2 cache so don't invoke DB lazy loading
+ list.remove(ebi);
+ return;
+ }
+
+ if (context.hitCache) {
+ // Check each of the beans in the batch to see if they are in the L2 cache.
+ Iterator iterator = list.iterator();
+ while (iterator.hasNext()) {
+ EntityBeanIntercept bean = iterator.next();
+ if (context.desc.loadFromCache(bean)) {
+ iterator.remove();
+ }
}
+ }
- parent.getEbeanServer().loadBean(req);
- if (!all) {
- break;
- }
+ LoadBeanRequest req = new LoadBeanRequest(this, null, true, ebi.getLazyLoadProperty(), context.hitCache);
+ context.desc.getEbeanServer().loadBean(req);
+ }
- } while (true);
+ @Override
+ public String getFullPath() {
+ return context.fullPath;
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
index e26e5660e..71a7e2189 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java
@@ -9,7 +9,6 @@ import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.ObjectGraphOrigin;
import com.avaje.ebean.bean.PersistenceContext;
-import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebeaninternal.api.LoadContext;
import com.avaje.ebeaninternal.api.LoadSecondaryQuery;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
@@ -24,8 +23,6 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
/**
* Default implementation of LoadContext.
- *
- * @author rbygrave
*/
public class DLoadContext implements LoadContext {
@@ -48,7 +45,6 @@ public class DLoadContext implements LoadContext {
private final String relativePath;
private final ObjectGraphOrigin origin;
private final boolean useAutofetchManager;
- private final boolean hardRefs;
private final Map nodePathMap = new HashMap();
@@ -66,10 +62,9 @@ public class DLoadContext implements LoadContext {
boolean excludeBeanCache, ObjectGraphNode parentNode, boolean useAutofetchManager) {
this.ebeanServer = ebeanServer;
- this.hardRefs = GlobalProperties.getBoolean("ebean.hardrefs", false);
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
this.rootDescriptor = rootDescriptor;
- this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null, createBeanLoadList());
+ this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
this.readOnly = readOnly;
this.excludeBeanCache = excludeBeanCache;
this.useAutofetchManager = useAutofetchManager;
@@ -210,6 +205,14 @@ public class DLoadContext implements LoadContext {
public String getRelativePath() {
return relativePath;
}
+
+ protected String getFullPath(String path) {
+ if (relativePath == null) {
+ return path;
+ } else {
+ return relativePath + "." + path;
+ }
+ }
protected SpiEbeanServer getEbeanServer() {
return ebeanServer;
@@ -282,24 +285,7 @@ public class DLoadContext implements LoadContext {
BeanPropertyAssocMany> p = (BeanPropertyAssocMany>)getBeanProperty(rootDescriptor, path);
- return new DLoadManyContext(this, p, path, batchSize, queryProps, createBeanCollectionLoadList());
- }
-
-
- private DLoadList createBeanLoadList() {
- if (hardRefs){
- return new DLoadHardList();
- } else {
- return new DLoadWeakList();
- }
- }
-
- private DLoadList> createBeanCollectionLoadList() {
- if (hardRefs){
- return new DLoadHardList>();
- } else {
- return new DLoadWeakList>();
- }
+ return new DLoadManyContext(this, p, path, batchSize, queryProps);
}
private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
@@ -307,11 +293,10 @@ public class DLoadContext implements LoadContext {
BeanPropertyAssoc> p = (BeanPropertyAssoc>)getBeanProperty(rootDescriptor, path);
BeanDescriptor> targetDescriptor = p.getTargetDescriptor();
- return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps, createBeanLoadList());
+ return new DLoadBeanContext(this, targetDescriptor, path, batchSize, queryProps);
}
private BeanProperty getBeanProperty(BeanDescriptor> desc, String path){
-
return desc.getBeanPropertyFromPath(path);
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java
deleted file mode 100644
index 21177ffb2..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadHardList.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DLoadHardList implements DLoadList {
-
- private static final Logger logger = LoggerFactory.getLogger(DLoadHardList.class);
-
- protected final ArrayList list = new ArrayList();
-
- protected int removedFromTop;
-
- protected DLoadHardList() {
-
- }
-
- public int add(T e) {
- synchronized (this) {
- int i = list.size();
- list.add(e);
- return i;
- }
- }
-
- public void removeEntry(int position) {
- synchronized (this) {
- T object = list.get(position);
- if (object == null) {
- logger.warn("removeEntry found no Object for position[" + position + "]");
- } else {
- // just set the entry to null
- list.set(position, null);
- }
- if (position == removedFromTop) {
- removedFromTop++;
- }
- }
- }
-
- public List getNextBatch(int batchSize) {
- if (removedFromTop >= list.size()){
- return new ArrayList(0);
- }
- return getLoadBatch(removedFromTop, batchSize, true);
- }
-
- public List getLoadBatch(int position, int batchSize) {
- return getLoadBatch(position, batchSize, false);
- }
-
- private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) {
-
- synchronized (this) {
- if (batchSize < 1) {
- throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!");
- }
-
- ArrayList batch = new ArrayList();
-
- if (!addObjectToBatchAt(batch, position) && !ignoreMissing) {
- String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?";
- throw new IllegalStateException(msg);
- }
-
- for (int i = position; i < list.size(); i++) {
- addObjectToBatchAt(batch, i);
- if (batch.size() == batchSize) {
- // found enough beans going forward
- return batch;
- }
- }
-
- // search the front of the list to fill our batch
- for (int i = removedFromTop; i < position; i++) {
- addObjectToBatchAt(batch, i);
- if (batch.size() == batchSize) {
- // found enough beans going forward from start of list
- return batch;
- }
- }
-
- return batch;
- }
- }
-
- private boolean addObjectToBatchAt(ArrayList batch, int i) {
-
- boolean found = false;
- T object = list.get(i);
- if (object != null) {
- found = true;
- batch.add(object);
- // set it to null saying we have loaded this one
- list.set(i, null);
- }
-
- if (i == removedFromTop) {
- removedFromTop++;
- }
- return found;
- }
-
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
index 1d20d7e4b..a69603bb4 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.loadcontext;
+import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.bean.BeanCollection;
@@ -8,55 +9,45 @@ import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.LoadManyContext;
import com.avaje.ebeaninternal.api.LoadManyRequest;
+import com.avaje.ebeaninternal.api.LoadManyBuffer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
-public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader {
-
- protected final DLoadContext parent;
-
- protected final String fullPath;
-
- private final BeanDescriptor> desc;
+public class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
- private final BeanPropertyAssocMany> property;
-
- private final String path;
+ protected final BeanPropertyAssocMany> property;
+
+ private List bufferList;
+
+ private LoadBuffer currentBuffer;
+
+ public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany> property,
+ String path, int defaultBatchSize, OrmQueryProperties queryProps) {
- private final int batchSize;
-
- private final OrmQueryProperties queryProps;
-
- private final DLoadList> weakList;
-
- public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany> p,
- String path, int batchSize, OrmQueryProperties queryProps, DLoadList> weakList) {
-
- this.parent = parent;
- this.property = p;
- this.desc = p.getBeanDescriptor();
- this.path = path;
- this.batchSize = batchSize;
- this.queryProps = queryProps;
- this.weakList = weakList;//new DLoadWeakList>();
-
- if (parent.getRelativePath() == null){
- this.fullPath = path;
- } else {
- this.fullPath = parent.getRelativePath()+"."+path;
- }
+ super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
+ this.property = property;
+ this.currentBuffer = createBuffer(firstBatchSize);
+ this.bufferList = queryFetch ? new ArrayList() : null;
}
- public void configureQuery(SpiQuery> query){
+ private LoadBuffer createBuffer(int size) {
+ LoadBuffer buffer = new LoadBuffer(this, size);
+ if (bufferList != null) {
+ bufferList.add(buffer);
+ }
+ return buffer;
+ }
+
+ public void configureQuery(SpiQuery> query){
// propagate the readOnly state
- if (parent.isReadOnly() != null){
- query.setReadOnly(parent.isReadOnly());
- }
+ if (parent.isReadOnly() != null){
+ query.setReadOnly(parent.isReadOnly());
+ }
query.setParentNode(getObjectGraphNode());
if (queryProps != null){
@@ -82,18 +73,6 @@ public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader {
}
}
- public String getFullPath() {
- return fullPath;
- }
-
- public PersistenceContext getPersistenceContext() {
- return parent.getPersistenceContext();
- }
-
- public int getBatchSize() {
- return batchSize;
- }
-
public BeanPropertyAssocMany> getBeanProperty() {
return property;
}
@@ -102,60 +81,130 @@ public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader {
return desc;
}
- public String getPath() {
- return path;
- }
public String getName() {
return parent.getEbeanServer().getName();
}
public void register(BeanCollection> bc){
- int pos = weakList.add(bc);
- bc.setLoader(pos, this);
+
+ bc.setLoader(0, currentBuffer);
+ if (currentBuffer.add(bc)) {
+ // the currentBuffer is full so create another one
+ currentBuffer = createBuffer(secondaryBatchSize);
+ }
}
- public void loadMany(BeanCollection> bc, boolean onlyIds) {
-
- int position = bc.getLoaderIndex();
-
- LoadManyRequest req;
- synchronized (weakList) {
- boolean hitCache = desc.isBeanCaching() && !onlyIds && !parent.isExcludeBeanCache();
- if (hitCache){
- Object ownerBean = bc.getOwnerBean();
- BeanDescriptor extends Object> parentDesc = desc.getBeanDescriptor(ownerBean.getClass());
- Object parentId = parentDesc.getId(ownerBean);
- if (parentDesc.cacheLoadMany(property, bc, parentId, parent.isReadOnly())) {
- // we loaded the bean from cache
- weakList.removeEntry(position);
- return;
- }
- }
-
- List> loadBatch = weakList.getLoadBatch(position, batchSize);
- req = new LoadManyRequest(this, loadBatch, null, batchSize, true, onlyIds, hitCache);
- }
- parent.getEbeanServer().loadMany(req);
- }
public void loadSecondaryQuery(OrmQueryRequest> parentRequest, int requestedBatchSize, boolean all){
- do {
- LoadManyRequest req;
- synchronized (weakList) {
- List> batch = weakList.getNextBatch(requestedBatchSize);
- if (batch.size() == 0){
- return;
- }
- req = new LoadManyRequest(this, batch, parentRequest.getTransaction(), requestedBatchSize, false, false, false);
- }
- parent.getEbeanServer().loadMany(req);
- if (!all){
- // queryFirst(batch)
- break;
- }
- } while (true);
+ if (!queryFetch) {
+ throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
+ }
+ synchronized (this) {
+ if (bufferList != null) {
+ for (LoadBuffer loadBuffer : bufferList) {
+ if (!loadBuffer.list.isEmpty()) {
+ LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest.getTransaction(), requestedBatchSize, false, false, false);
+ parent.getEbeanServer().loadMany(req);
+ if (!queryProps.isQueryFetchAll()) {
+ // Stop - only fetch the first batch ... the rest will be lazy loaded
+ break;
+ }
+ }
+ }
+
+ // this is only run once - secondary query is a one shot deal
+ this.bufferList = null;
+ }
+ }
}
+ /**
+ * A buffer for batch loading bean collections on a given path.
+ * Supports batch lazy loading and secondary query loading.
+ */
+ public static class LoadBuffer implements BeanCollectionLoader, LoadManyBuffer {
+
+ private final DLoadManyContext context;
+ private final int batchSize;
+ private final List> list;
+
+ public LoadBuffer(DLoadManyContext context, int batchSize) {
+ this.context = context;
+ this.batchSize = batchSize;
+ this.list = new ArrayList>(batchSize);
+ }
+
+ /**
+ * Return true if the buffer is full.
+ */
+ public boolean add(BeanCollection> bc) {
+ list.add(bc);
+ return batchSize == list.size();
+ }
+
+ @Override
+ public List> getBatch() {
+ return list;
+ }
+
+ @Override
+ public BeanPropertyAssocMany> getBeanProperty() {
+ return context.property;
+ }
+
+ @Override
+ public ObjectGraphNode getObjectGraphNode() {
+ return context.getObjectGraphNode();
+ }
+
+ @Override
+ public void configureQuery(SpiQuery> query){
+ context.configureQuery(query);
+ }
+
+ @Override
+ public String getName() {
+ return context.serverName;
+ }
+
+ @Override
+ public BeanDescriptor> getBeanDescriptor() {
+ return context.desc;
+ }
+
+ @Override
+ public PersistenceContext getPersistenceContext() {
+ return context.getPersistenceContext();
+ }
+
+ @Override
+ public String getFullPath() {
+ return context.fullPath;
+ }
+
+ public void loadMany(BeanCollection> bc, boolean onlyIds) {
+
+ synchronized (this) {
+ boolean useCache = context.hitCache && !onlyIds;
+ if (useCache) {
+ Object ownerBean = bc.getOwnerBean();
+ BeanDescriptor extends Object> parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());
+ Object parentId = parentDesc.getId(ownerBean);
+ if (parentDesc.cacheLoadMany(context.property, bc, parentId, context.parent.isReadOnly())) {
+ // we loaded the bean from cache
+ list.remove(bc);
+ return;
+ }
+ }
+
+ // Should reduce the list by checking each beanCollection in the L2 first before executing the query
+
+ LoadManyRequest req = new LoadManyRequest(this, null, batchSize, true, onlyIds, useCache);
+ context.parent.getEbeanServer().loadMany(req);
+ }
+ }
+
+ }
}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java
deleted file mode 100644
index 2dadc4283..000000000
--- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadWeakList.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-public class DLoadWeakList implements DLoadList {
-
- private static final Logger logger = LoggerFactory.getLogger(DLoadWeakList.class);
-
- protected final ArrayList> list = new ArrayList>();
-
- protected int removedFromTop;
-
- protected DLoadWeakList() {
-
- }
-
- public int add(T e) {
- synchronized (this) {
- int i = list.size();
- list.add(new WeakReference(e));
- return i;
- }
- }
-
- public void removeEntry(int position) {
- synchronized (this) {
- WeakReference wref = list.get(position);
- if (wref == null) {
- logger.warn("removeEntry found no WeakReference for position[" + position + "]");
- } else {
- // just set the entry to null
- list.set(position, null);
- T object = wref.get();
- if (object == null) {
- logger.warn("removeEntry found no Object held by WeakReference for position[" + position + "]");
- }
- }
- if (position == removedFromTop) {
- removedFromTop++;
- }
- }
- }
-
- public List getNextBatch(int batchSize) {
- if (removedFromTop >= list.size()){
- return new ArrayList(0);
- }
- return getLoadBatch(removedFromTop, batchSize, true);
- }
-
- public List getLoadBatch(int position, int batchSize) {
- return getLoadBatch(position, batchSize, false);
- }
-
- private List getLoadBatch(int position, int batchSize, boolean ignoreMissing) {
-
- synchronized (this) {
- if (batchSize < 1) {
- throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!");
- }
-
- ArrayList batch = new ArrayList();
-
- if (!addObjectToBatchAt(batch, position) && !ignoreMissing) {
- String msg = "getLoadBatch position[" + position + "] didn't find a bean in the list?";
- throw new IllegalStateException(msg);
- }
-
- for (int i = position; i < list.size(); i++) {
- addObjectToBatchAt(batch, i);
- if (batch.size() == batchSize) {
- // found enough beans going forward
- return batch;
- }
- }
-
- // search the front of the list to fill our batch
- for (int i = removedFromTop; i < position; i++) {
- addObjectToBatchAt(batch, i);
- if (batch.size() == batchSize) {
- // found enough beans going forward from start of list
- return batch;
- }
- }
-
- return batch;
- }
- }
-
- private boolean addObjectToBatchAt(ArrayList batch, int i) {
-
- boolean found = false;
- WeakReference wref = list.get(i);
- if (wref != null) {
- T object = wref.get();
- if (object == null) {
- logger.warn("Bean is null from weak reference");
- } else {
- found = true;
- batch.add(object);
- }
- // set it to null saying we have loaded this one
- list.set(i, null);
- }
- if (i == removedFromTop) {
- removedFromTop++;
- }
- return found;
- }
-
-}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
index a16d10741..f813629e0 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java
@@ -12,7 +12,6 @@ import java.util.Set;
import com.avaje.ebean.ExpressionFactory;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebean.OrderBy;
-import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebean.Query;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.SpiExpressionFactory;
@@ -187,8 +186,7 @@ public class OrmQueryProperties implements Serializable {
if (filterMany == null){
return null;
}
- filterMany.trimPath(trimPath);
- return filterMany;
+ return filterMany.trimPath(trimPath);
}
/**
@@ -248,11 +246,7 @@ public class OrmQueryProperties implements Serializable {
}
if (orderBy != null){
- List orderByProps = orderBy.getProperties();
- for (int i = 0; i < orderByProps.size(); i++) {
- orderByProps.get(i).trim(path);
- }
- query.setOrder(orderBy);
+ query.setOrder(orderBy.copyWithTrim(path));
}
}
diff --git a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
index 6f8eea7d3..8c18dd40a 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/DefaultExpressionList.java
@@ -33,13 +33,13 @@ public class DefaultExpressionList implements SpiExpressionList {
private static final long serialVersionUID = -6992345500247035947L;
- private final ArrayList list = new ArrayList();
+ protected final ArrayList list = new ArrayList();
- private final Query query;
+ protected final Query query;
- private final ExpressionList parentExprList;
+ protected final ExpressionList parentExprList;
- private transient ExpressionFactory expr;
+ protected transient ExpressionFactory expr;
private final String exprLang;
private final String listAndStart;
@@ -69,7 +69,7 @@ public class DefaultExpressionList implements SpiExpressionList {
}
}
- public void trimPath(int prefixTrim) {
+ public SpiExpressionList> trimPath(int prefixTrim) {
throw new RuntimeException("Only allowed on FilterExpressionList");
}
diff --git a/src/main/java/com/avaje/ebeaninternal/util/FilterExpressionList.java b/src/main/java/com/avaje/ebeaninternal/util/FilterExpressionList.java
index b14513660..28d08981a 100644
--- a/src/main/java/com/avaje/ebeaninternal/util/FilterExpressionList.java
+++ b/src/main/java/com/avaje/ebeaninternal/util/FilterExpressionList.java
@@ -15,6 +15,7 @@ import com.avaje.ebean.OrderBy;
import com.avaje.ebean.PagingList;
import com.avaje.ebean.Query;
import com.avaje.ebean.QueryListener;
+import com.avaje.ebeaninternal.api.SpiExpressionList;
import com.avaje.ebeaninternal.server.expression.FilterExprPath;
public class FilterExpressionList extends DefaultExpressionList {
@@ -31,8 +32,8 @@ public class FilterExpressionList extends DefaultExpressionList {
this.rootQuery = rootQuery;
}
- public void trimPath(int prefixTrim) {
- pathPrefix.trimPath(prefixTrim);
+ public SpiExpressionList> trimPath(int prefixTrim) {
+ return new FilterExpressionList(pathPrefix.trimPath(prefixTrim), expr, rootQuery);
}
public FilterExprPath getPathPrefix() {
diff --git a/src/test/java/com/avaje/ebeaninternal/server/loadcontext/TestDLoadWeakList.java b/src/test/java/com/avaje/ebeaninternal/server/loadcontext/TestDLoadWeakList.java
deleted file mode 100644
index 01a749bb5..000000000
--- a/src/test/java/com/avaje/ebeaninternal/server/loadcontext/TestDLoadWeakList.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.avaje.ebeaninternal.server.loadcontext;
-
-import java.util.List;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import com.avaje.ebean.BaseTestCase;
-
-public class TestDLoadWeakList extends BaseTestCase {
-
- @Test
- public void test() {
-
- String s0 = new String("zero");
- String s1 = new String("one");
- String s2 = new String("two");
- String s3 = new String("three");
- String s4 = new String("four");
- String s5 = new String("five");
- String s6 = new String("six");
- String s7 = new String("seven");
- String s8 = new String("eight");
- String s9 = new String("nine");
- String s10 = new String("ten");
-
- DLoadWeakList