Refactor internals of batch lazy loading and query fetches

This commit is contained in:
Rob Bygrave
2014-01-18 23:35:42 +13:00
parent e685e2f4ac
commit 05dcdf162b
28 changed files with 661 additions and 981 deletions
+20 -15
View File
@@ -24,7 +24,7 @@ import java.io.Serializable;
*
* <pre class="code">
* // Normal fetch join results in a single SQL query
* List&lt;Order&gt; list = Ebean.find(Order.class).join(&quot;details&quot;).findList();
* List&lt;Order&gt; list = Ebean.find(Order.class).fetch(&quot;details&quot;).findList();
*
* // Find Orders join details using a single SQL query
* </pre>
@@ -51,8 +51,8 @@ import java.io.Serializable;
* // This will use 3 SQL queries to build this object graph
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;details&quot;, new JoinConfig().query())
* .fetch(&quot;customer&quot;, new JoinConfig().query(5))
* .fetch(&quot;details&quot;, new FetchConfig().query())
* .fetch(&quot;customer&quot;, new FetchConfig().queryFirst(5))
* .findList();
*
* // query 1) find order
@@ -70,7 +70,7 @@ import java.io.Serializable;
* .select(&quot;status, shipDate&quot;)
* .fetch(&quot;details&quot;, &quot;quantity, price&quot;, new FetchConfig().query())
* .fetch(&quot;details.product&quot;, &quot;sku, name&quot;)
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().query(10))
* .fetch(&quot;customer&quot;, &quot;name&quot;, new FetchConfig().queryFirst(5))
* .fetch(&quot;customer.contacts&quot;)
* .fetch(&quot;customer.shippingAddress&quot;)
* .findList();
@@ -96,13 +96,13 @@ import java.io.Serializable;
* <pre class="code">
* List&lt;Order&gt; list =
* Ebean.find(Order.class)
* .fetch(&quot;customer&quot;, new FetchConfig().query(3).lazy(10))
* .fetch(&quot;customer&quot;, 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
*
* </pre>
*
@@ -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 (?,?,?,?,?)
*
* </pre>
@@ -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).
* <p>
* 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).
* <p>
* The queryBatchSize is the number of parent id's that this separate query
* will load per batch.
* </p>
* <p>
* 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.
* </p>
*
* @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.
* <p>
* 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.
*/
@@ -32,6 +32,10 @@ public final class OrderBy<T> implements Serializable {
public OrderBy() {
this.list = new ArrayList<Property>(2);
}
private OrderBy(List<Property> list) {
this.list = list;
}
/**
* Create an orderBy parsing the order by clause.
@@ -81,6 +85,17 @@ public final class OrderBy<T> implements Serializable {
return query;
}
/**
* Return a copy of this OrderBy with the path trimmed.
*/
public OrderBy<T> copyWithTrim(String path) {
List<Property> newList = new ArrayList<Property>(list.size());
for (int i = 0; i < list.size(); i++) {
newList.add(list.get(i).copyWithTrim(path));
}
return new OrderBy<T>(newList);
}
/**
* Return the properties for this OrderBy.
*/
@@ -196,6 +211,13 @@ public final class OrderBy<T> 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);
@@ -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<EntityBeanIntercept> getBatch();
public BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
public String getFullPath();
public void configureQuery(SpiQuery<?> query, String lazyLoadProperty);
}
@@ -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();
}
@@ -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<EntityBeanIntercept> batch;
private final LoadBeanContext loadContext;
private final String lazyLoadProperty;
private final boolean loadCache;
public LoadBeanRequest(LoadBeanContext loadContext, List<EntityBeanIntercept> 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<EntityBeanIntercept> 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<EntityBeanIntercept> 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<EntityBeanIntercept> 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;
}
}
@@ -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<BeanCollection<?>> getBatch();
public BeanPropertyAssocMany<?> getBeanProperty();
public ObjectGraphNode getObjectGraphNode();
public BeanDescriptor<?> getBeanDescriptor();
public PersistenceContext getPersistenceContext();
public String getFullPath();
public void configureQuery(SpiQuery<?> query);
}
@@ -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();
}
@@ -13,28 +13,24 @@ public class LoadManyRequest extends LoadRequest {
private final List<BeanCollection<?>> batch;
private final LoadManyContext loadContext;
private final LoadManyBuffer loadContext;
private final boolean onlyIds;
private final boolean loadCache;
public LoadManyRequest(LoadManyContext loadContext,
List<BeanCollection<?>> 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;
}
@@ -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.
* <p>
@@ -18,10 +18,10 @@ public interface SpiExpressionList<T> extends ExpressionList<T> {
*/
public List<SpiExpression> 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.
@@ -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.
* </p>
*/
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<BeanCollection<?>> 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();
@@ -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) {
@@ -5,38 +5,36 @@ import java.io.Serializable;
/**
* This is the path prefix for filterMany.
* <p>
* 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.
* </p>
*
* @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;
}
}
@@ -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();
}
}
@@ -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<LoadBuffer> bufferList;
private LoadBuffer currentBuffer;
protected final BeanDescriptor<?> desc;
protected final String path;
protected final String fullPath;
private final DLoadList<EntityBeanIntercept> weakList;
private final OrmQueryProperties queryProps;
private int batchSize;
public DLoadBeanContext(DLoadContext parent, BeanDescriptor<?> desc, String path, int batchSize,
OrmQueryProperties queryProps, DLoadList<EntityBeanIntercept> 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<DLoadBeanContext.LoadBuffer>() : 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<EntityBeanIntercept> 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<EntityBeanIntercept> loadBeanCheckBatch(List<EntityBeanIntercept> batch) {
List<EntityBeanIntercept> actualLoadBatch = new ArrayList<EntityBeanIntercept>(batchSize);
List<EntityBeanIntercept> 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<EntityBeanIntercept> 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<EntityBeanIntercept> list;
public LoadBuffer(DLoadBeanContext context, int batchSize) {
this.context = context;
this.batchSize = batchSize;
this.list = new ArrayList<EntityBeanIntercept>(batchSize);
}
/**
* Return true if the buffer is full.
*/
public boolean add(EntityBeanIntercept ebi) {
list.add(ebi);
return batchSize == list.size();
}
@Override
public List<EntityBeanIntercept> 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<EntityBeanIntercept> 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;
}
}
@@ -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<String,ObjectGraphNode> nodePathMap = new HashMap<String, ObjectGraphNode>();
@@ -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<EntityBeanIntercept> createBeanLoadList() {
if (hardRefs){
return new DLoadHardList<EntityBeanIntercept>();
} else {
return new DLoadWeakList<EntityBeanIntercept>();
}
}
private DLoadList<BeanCollection<?>> createBeanCollectionLoadList() {
if (hardRefs){
return new DLoadHardList<BeanCollection<?>>();
} else {
return new DLoadWeakList<BeanCollection<?>>();
}
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);
}
@@ -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<T> implements DLoadList<T> {
private static final Logger logger = LoggerFactory.getLogger(DLoadHardList.class);
protected final ArrayList<T> list = new ArrayList<T>();
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<T> getNextBatch(int batchSize) {
if (removedFromTop >= list.size()){
return new ArrayList<T>(0);
}
return getLoadBatch(removedFromTop, batchSize, true);
}
public List<T> getLoadBatch(int position, int batchSize) {
return getLoadBatch(position, batchSize, false);
}
private List<T> getLoadBatch(int position, int batchSize, boolean ignoreMissing) {
synchronized (this) {
if (batchSize < 1) {
throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!");
}
ArrayList<T> batch = new ArrayList<T>();
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<T> 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;
}
}
@@ -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<LoadBuffer> 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<BeanCollection<?>> weakList;
public DLoadManyContext(DLoadContext parent, BeanPropertyAssocMany<?> p,
String path, int batchSize, OrmQueryProperties queryProps, DLoadList<BeanCollection<?>> 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<BeanCollection<?>>();
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<DLoadManyContext.LoadBuffer>() : 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<BeanCollection<?>> 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<BeanCollection<?>> 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<BeanCollection<?>> list;
public LoadBuffer(DLoadManyContext context, int batchSize) {
this.context = context;
this.batchSize = batchSize;
this.list = new ArrayList<BeanCollection<?>>(batchSize);
}
/**
* Return true if the buffer is full.
*/
public boolean add(BeanCollection<?> bc) {
list.add(bc);
return batchSize == list.size();
}
@Override
public List<BeanCollection<?>> 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);
}
}
}
}
@@ -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<T> implements DLoadList<T> {
private static final Logger logger = LoggerFactory.getLogger(DLoadWeakList.class);
protected final ArrayList<WeakReference<T>> list = new ArrayList<WeakReference<T>>();
protected int removedFromTop;
protected DLoadWeakList() {
}
public int add(T e) {
synchronized (this) {
int i = list.size();
list.add(new WeakReference<T>(e));
return i;
}
}
public void removeEntry(int position) {
synchronized (this) {
WeakReference<T> 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<T> getNextBatch(int batchSize) {
if (removedFromTop >= list.size()){
return new ArrayList<T>(0);
}
return getLoadBatch(removedFromTop, batchSize, true);
}
public List<T> getLoadBatch(int position, int batchSize) {
return getLoadBatch(position, batchSize, false);
}
private List<T> getLoadBatch(int position, int batchSize, boolean ignoreMissing) {
synchronized (this) {
if (batchSize < 1) {
throw new RuntimeException("batchSize " + batchSize + " < 1 ??!!");
}
ArrayList<T> batch = new ArrayList<T>();
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<T> batch, int i) {
boolean found = false;
WeakReference<T> 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;
}
}
@@ -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<Property> orderByProps = orderBy.getProperties();
for (int i = 0; i < orderByProps.size(); i++) {
orderByProps.get(i).trim(path);
}
query.setOrder(orderBy);
query.setOrder(orderBy.copyWithTrim(path));
}
}
@@ -33,13 +33,13 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
private static final long serialVersionUID = -6992345500247035947L;
private final ArrayList<SpiExpression> list = new ArrayList<SpiExpression>();
protected final ArrayList<SpiExpression> list = new ArrayList<SpiExpression>();
private final Query<T> query;
protected final Query<T> query;
private final ExpressionList<T> parentExprList;
protected final ExpressionList<T> parentExprList;
private transient ExpressionFactory expr;
protected transient ExpressionFactory expr;
private final String exprLang;
private final String listAndStart;
@@ -69,7 +69,7 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
}
}
public void trimPath(int prefixTrim) {
public SpiExpressionList<?> trimPath(int prefixTrim) {
throw new RuntimeException("Only allowed on FilterExpressionList");
}
@@ -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<T> extends DefaultExpressionList<T> {
@@ -31,8 +32,8 @@ public class FilterExpressionList<T> extends DefaultExpressionList<T> {
this.rootQuery = rootQuery;
}
public void trimPath(int prefixTrim) {
pathPrefix.trimPath(prefixTrim);
public SpiExpressionList<?> trimPath(int prefixTrim) {
return new FilterExpressionList<T>(pathPrefix.trimPath(prefixTrim), expr, rootQuery);
}
public FilterExprPath getPathPrefix() {