#210 - ENH: Add Query.setLazyLoadBatch(int size) method ... for per query control

This commit is contained in:
rbygrave
2014-11-26 20:13:30 +13:00
parent 1cd31e13b9
commit 478053263f
18 changed files with 239 additions and 112 deletions
+14 -4
View File
@@ -340,22 +340,32 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setAutofetch(boolean autofetch);
/**
* Set the default lazy loading batch size to use.
* <p>
* When lazy loading is invoked on beans loaded by this query then this sets the
* batch size used to load those beans.
*
* @param lazyLoadBatchSize the number of beans to lazy load in a single batch
*/
public Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize);
/**
* Explicitly set a comma delimited list of the properties to fetch on the
* 'main' entity bean (aka partial object). Note that '*' means all
* properties.
*
*
* <pre class="code">
* Query&lt;Customer&gt; query = Ebean.createQuery(Customer.class);
*
*
* // Only fetch the customer id, name and status.
* // This is described as a &quot;Partial Object&quot;
* query.select(&quot;name, status&quot;);
* query.where(&quot;lower(name) like :custname&quot;).setParameter(&quot;custname&quot;, &quot;rob%&quot;);
*
*
* List&lt;Customer&gt; customerList = query.findList();
* </pre>
*
*
* @param fetchProperties
* the properties to fetch for this bean (* = all properties).
*/
@@ -11,6 +11,8 @@ import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
*/
public interface LoadBeanBuffer {
public int getBatchSize();
public List<EntityBeanIntercept> getBatch();
public BeanDescriptor<?> getBeanDescriptor();
@@ -59,4 +59,7 @@ public class LoadBeanRequest extends LoadRequest {
return lazyLoadProperty;
}
public int getBatchSize() {
return getLoadContext().getBatchSize();
}
}
@@ -11,16 +11,16 @@ import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
*/
public interface LoadContext {
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
/**
* Return the minimum batch size when using QueryIterator with query joins.
*/
public int getSecondaryQueriesMinBatchSize(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
/**
* Execute any secondary (+query) queries if there are any defined.
* @param parentRequest the originating query request
*/
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, int defaultQueryBatch);
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest);
/**
* Register any secondary queries (+query or +lazy) with their
@@ -13,6 +13,8 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
*/
public interface LoadManyBuffer {
public int getBatchSize();
public List<BeanCollection<?>> getBatch();
public BeanPropertyAssocMany<?> getBeanProperty();
@@ -18,12 +18,11 @@ public class LoadManyRequest extends LoadRequest {
private final boolean loadCache;
public LoadManyRequest(LoadManyBuffer loadContext, int batchSize, boolean lazy,boolean onlyIds, boolean loadCache) {
this(loadContext, null, batchSize, lazy, onlyIds, loadCache);
public LoadManyRequest(LoadManyBuffer loadContext, boolean lazy, boolean onlyIds, boolean loadCache) {
this(loadContext, null, lazy, onlyIds, loadCache);
}
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, int batchSize, boolean lazy,
boolean onlyIds, boolean loadCache) {
public LoadManyRequest(LoadManyBuffer loadContext, OrmQueryRequest<?> parentRequest, boolean lazy, boolean onlyIds, boolean loadCache) {
super(parentRequest, lazy);
this.loadContext = loadContext;
@@ -69,4 +68,10 @@ public class LoadManyRequest extends LoadRequest {
return loadCache;
}
/**
* Return the batch size used for this load context.
*/
public int getBatchSize() {
return loadContext.getBatchSize();
}
}
@@ -13,9 +13,6 @@ public interface LoadSecondaryQuery {
/**
* Execute the secondary query with a given batch size.
*
* @param parentRequest
* the originating query request
*/
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all);
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest);
}
@@ -27,7 +27,7 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryProperties;
*/
public interface SpiQuery<T> extends Query<T> {
public enum Mode {
public enum Mode {
NORMAL(false), LAZYLOAD_MANY(false), LAZYLOAD_BEAN(true), REFRESH_BEAN(true);
Mode(boolean loadContextBean) {
this.loadContextBean = loadContextBean;
@@ -89,6 +89,11 @@ public interface SpiQuery<T> extends Query<T> {
*/
public PersistenceContextScope getPersistenceContextScope();
/**
* Return the default lazy load batch size.
*/
public int getLazyLoadBatchSize();
/**
* Return true if select all properties was used to ensure the property
* invoking a lazy load was included in the query.
@@ -130,6 +130,11 @@ public class DefaultBeanLoader {
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
if (loadRequest.isLazy()) {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
}
// potentially changes the joins and selected properties
ctx.configureQuery(query);
@@ -198,7 +203,7 @@ public class DefaultBeanLoader {
boolean useManyIdCache = beanCollection != null && parentDesc.isManyPropCaching();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
if (ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheManyPropLoad(many, beanCollection, parentId, readOnly)) {
@@ -240,10 +245,8 @@ public class DefaultBeanLoader {
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
if (ebi != null) {
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
if (ebi.isReadOnly()) {
query.setReadOnly(true);
}
server.findUnique(query, t);
@@ -314,6 +317,11 @@ public class DefaultBeanLoader {
String mode = loadRequest.isLazy() ? "+lazy" : "+query";
query.setLoadDescription(mode, loadRequest.getDescription());
if (loadRequest.isLazy()) {
// cascade the batch size (if set) for further lazy loading
query.setLazyLoadBatchSize(loadRequest.getBatchSize());
}
ctx.configureQuery(query, loadRequest.getLazyLoadProperty());
// make sure the query doesn't use the cache
@@ -353,7 +361,6 @@ public class DefaultBeanLoader {
private void refreshBeanInternal(EntityBean bean, SpiQuery.Mode mode, int embeddedOwnerIndex) {
EntityBeanIntercept ebi = bean._ebean_getIntercept();
;
PersistenceContext pc = ebi.getPersistenceContext();
if (Mode.REFRESH_BEAN == mode) {
// need a new PersistenceContext for REFRESH
@@ -375,9 +382,7 @@ public class DefaultBeanLoader {
// a reference with no existing persistenceContext
pc = new DefaultPersistenceContext();
pc.put(id, bean);
if (ebi != null) {
ebi.setPersistenceContext(pc);
}
ebi.setPersistenceContext(pc);
}
if (embeddedOwnerIndex == -1) {
@@ -393,9 +398,7 @@ public class DefaultBeanLoader {
}
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(desc.getBeanType());
if (ebi != null) {
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
}
query.setLazyLoadProperty(ebi.getLazyLoadProperty());
if (embeddedOwnerIndex > -1) {
String embeddedBeanPropertyName = ebi.getProperty(embeddedOwnerIndex);
@@ -28,7 +28,6 @@ import com.avaje.ebeaninternal.server.loadcontext.DLoadContext;
import com.avaje.ebeaninternal.server.query.CQueryPlan;
import com.avaje.ebeaninternal.server.query.CancelableQuery;
import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext;
import com.avaje.ebeaninternal.server.transaction.NoopPersistenceContext;
/**
* Wraps the objects involved in executing a Query.
@@ -79,8 +78,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return ebeanServer.getDatabasePlatform().getLikeClause();
}
public void executeSecondaryQueries(int defaultQueryBatch) {
loadContext.executeSecondaryQueries(this, defaultQueryBatch);
public void executeSecondaryQueries() {
loadContext.executeSecondaryQueries(this);
}
/**
@@ -405,4 +404,12 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
return query.isLogSecondaryQuery();
}
/**
* Return the batch size for lazy loading on this bean query request.
*/
public int getLazyLoadBatchSize() {
int batchSize = query.getLazyLoadBatchSize();
return (batchSize > 0) ? batchSize : ebeanServer.getLazyLoadBatchSize();
}
}
@@ -69,7 +69,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
return buffer;
}
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all) {
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest) {
if (!queryFetch) {
throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
@@ -111,7 +111,11 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
this.batchSize = batchSize;
this.list = new ArrayList<EntityBeanIntercept>(batchSize);
}
public int getBatchSize() {
return batchSize;
}
/**
* Return true if the buffer is full.
*/
@@ -56,7 +56,7 @@ public class DLoadContext implements LoadContext {
this.persistenceContext = request.getPersistenceContext();
this.ebeanServer = request.getServer();
this.defaultBatchSize = ebeanServer.getLazyLoadBatchSize();
this.defaultBatchSize = request.getLazyLoadBatchSize();
this.rootDescriptor = request.getBeanDescriptor();
SpiQuery<?> query = request.getQuery();
@@ -104,18 +104,13 @@ public class DLoadContext implements LoadContext {
/**
* Execute all the secondary queries.
*/
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, int defaultQueryBatch) {
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest) {
if (secQuery != null){
for (int i = 0; i < secQuery.size(); i++) {
OrmQueryProperties properties = secQuery.get(i);
int batchSize = properties.getQueryFetchBatch();
if (batchSize == 0){
batchSize = defaultQueryBatch;
}
LoadSecondaryQuery load = getLoadSecondaryQuery(properties.getPath());
load.loadSecondaryQuery(parentRequest, batchSize, properties.isQueryFetchAll());
load.loadSecondaryQuery(parentRequest);
}
}
}
@@ -201,10 +196,6 @@ public class DLoadContext implements LoadContext {
return useAutofetchManager;
}
public String getRelativePath() {
return relativePath;
}
protected String getFullPath(String path) {
if (relativePath == null) {
return path;
@@ -83,8 +83,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
bc.setLoader(0, currentBuffer);
}
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest, int requestedBatchSize, boolean all){
public void loadSecondaryQuery(OrmQueryRequest<?> parentRequest) {
if (!queryFetch) {
throw new IllegalStateException("Not expecting loadSecondaryQuery() to be called?");
@@ -93,7 +92,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
if (bufferList != null) {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, requestedBatchSize, false, false, false);
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest, false, false, false);
parent.getEbeanServer().loadMany(req);
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
@@ -127,6 +126,10 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
this.batchSize = batchSize;
this.list = new ArrayList<BeanCollection<?>>(batchSize);
}
public int getBatchSize() {
return batchSize;
}
/**
* Return true if the buffer is full.
@@ -188,7 +191,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
boolean useCache = context.hitCache && !onlyIds;
if (useCache) {
EntityBean ownerBean = bc.getOwnerBean();
BeanDescriptor<? extends Object> parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());
BeanDescriptor<?> parentDesc = context.desc.getBeanDescriptor(ownerBean.getClass());
Object parentId = parentDesc.getId(ownerBean);
if (parentDesc.cacheManyPropLoad(context.property, bc, parentId, context.parent.isReadOnly())) {
// we loaded the bean from cache
@@ -199,7 +202,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
// Should reduce the list by checking each beanCollection in the L2 first before executing the query
LoadManyRequest req = new LoadManyRequest(this, batchSize, true, onlyIds, useCache);
LoadManyRequest req = new LoadManyRequest(this, true, onlyIds, useCache);
context.parent.getEbeanServer().loadMany(req);
}
}
@@ -25,14 +25,14 @@ public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private final DatabasePlatform dbPlatform;
private static final int defaultSecondaryQueryBatchSize = 100;
private final boolean forwardOnlyHintOnFindIterate;
private final CQueryBuilder queryBuilder;
private final int defaultSecondaryQueryBatchSize = 100;
public CQueryEngine(DatabasePlatform dbPlatform, Binder binder) {
this.dbPlatform = dbPlatform;
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
this.queryBuilder = new CQueryBuilder(dbPlatform, binder);
}
@@ -121,7 +121,7 @@ public class CQueryEngine {
try {
if (!cquery.prepareBindExecuteQueryForwardOnly(dbPlatform.isForwardOnlyHintOnFindIterate())) {
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
@@ -131,7 +131,15 @@ public class CQueryEngine {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
if (iterateBufferSize < 1) {
// not set on query joins so check if batch size set on query itself
int queryBatch = request.getQuery().getLazyLoadBatchSize();
if (queryBatch > 0) {
iterateBufferSize = queryBatch;
}
}
QueryIterator<T> readIterate = cquery.readIterate(iterateBufferSize, request);
@@ -178,7 +186,7 @@ public class CQueryEngine {
logFindManySummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
request.executeSecondaryQueries();
return beanCollection;
@@ -223,7 +231,7 @@ public class CQueryEngine {
logFindBeanSummary(cquery);
}
request.executeSecondaryQueries(defaultSecondaryQueryBatchSize);
request.executeSecondaryQueries();
return (T)bean;
@@ -43,7 +43,7 @@ class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
}
}
// execute secondary queries
request.executeSecondaryQueries(bufferSize);
request.executeSecondaryQueries();
}
return !buffer.isEmpty();
@@ -88,7 +88,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private int firstRow;
private int totalHits;
/**
* Lazy loading batch size (can override server wide default).
*/
private int lazyLoadBatchSize;
/**
* The where clause from a parsed query string.
@@ -251,14 +254,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
}
public int getTotalHits() {
return totalHits;
}
public void setTotalHits(int totalHits) {
this.totalHits = totalHits;
}
@Override
public Query<T> apply(PathProperties pathProperties) {
pathProperties.apply(this);
@@ -295,6 +290,16 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public int getLazyLoadBatchSize() {
return lazyLoadBatchSize;
}
public Query<T> setLazyLoadBatchSize(int lazyLoadBatchSize) {
this.lazyLoadBatchSize = lazyLoadBatchSize;
return this;
}
public String getLazyLoadProperty() {
return lazyLoadProperty;
}
@@ -1168,18 +1173,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return query;
}
public DefaultOrmQuery<T> addWhere(String addToWhereClause) {
return where(addToWhereClause);
}
public DefaultOrmQuery<T> addWhere(Expression expression) {
return where(expression);
}
public ExpressionList<T> addWhere() {
return where();
}
public DefaultOrmQuery<T> where(String addToWhereClause) {
if (additionalWhere == null) {
additionalWhere = addToWhereClause;
@@ -1217,18 +1210,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
}
public DefaultOrmQuery<T> addHaving(String addToHavingClause) {
return having(addToHavingClause);
}
public DefaultOrmQuery<T> addHaving(Expression expression) {
return having(expression);
}
public ExpressionList<T> addHaving() {
return having();
}
public DefaultOrmQuery<T> having(String addToHavingClause) {
if (additionalHaving == null) {
additionalHaving = addToHavingClause;