#566 - Refactor internals - OrmQueryDetailProperties with final FetchConfig

This commit is contained in:
Robin Bygrave
2016-02-17 16:33:38 +13:00
parent 5c6d6608ef
commit 27045baf44
11 changed files with 314 additions and 108 deletions
@@ -249,4 +249,22 @@ public class FetchConfig implements Serializable {
return queryAll;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FetchConfig that = (FetchConfig) o;
if (lazyBatchSize != that.lazyBatchSize) return false;
if (queryBatchSize != that.queryBatchSize) return false;
return queryAll == that.queryAll;
}
@Override
public int hashCode() {
int result = lazyBatchSize;
result = 92821 * result + queryBatchSize;
result = 92821 * result + (queryAll ? 1 : 0);
return result;
}
}
@@ -338,7 +338,7 @@ public interface SpiQuery<T> extends Query<T> {
/**
* Convert joins as necessary to query joins etc.
*/
void convertJoins(int queryBatchSize);
void convertJoins();
/**
* Return the TransactionContext.
@@ -31,7 +31,6 @@ import com.avaje.ebeaninternal.api.SpiBackgroundExecutor;
import com.avaje.ebeaninternal.api.SpiEbeanPlugin;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiQuery.Type;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
@@ -1065,7 +1064,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
OrmQueryRequest<T> request = new OrmQueryRequest<T>(this, queryEngine, query, desc, (SpiTransaction) t);
request.prepareQuery(queryBatchSize);
request.prepareQuery();
return request;
}
@@ -148,12 +148,11 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
/**
* Prepare the query and calculate the query plan key.
*/
public void prepareQuery(int queryBatchSize) {
public void prepareQuery() {
adapterPreQuery();
// determine extra joins required to support where clause predicates on *ToMany properties
query.convertJoins(queryBatchSize);
query.convertJoins();
this.queryJoins = query.removeQueryJoins();
this.lazyJoins = query.removeLazyJoins();
this.queryPlanKey = query.prepare(this);
@@ -375,10 +375,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return expressionFactory;
}
/**
* Return true if the where expressions contains a many property.
*/
private void initManyWhereJoins() {
private void createExtraJoinsToSupportManyWhereClause() {
manyWhereJoins = new ManyWhereJoins();
if (whereExpressions != null) {
whereExpressions.containsMany(beanDescriptor, manyWhereJoins);
@@ -388,6 +385,9 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
}
/**
* Return the extra joins required to support the where clause for 'Many' properties.
*/
public ManyWhereJoins getManyWhereJoins() {
return manyWhereJoins;
}
@@ -442,32 +442,30 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
this.lazyLoadManyPath = lazyLoadManyPath;
}
@Override
public void convertJoins() {
createExtraJoinsToSupportManyWhereClause();
markQueryJoins();
}
/**
* Limit the number of fetch joins to Many properties, mark as query joins as needed.
*/
private void markQueryJoins() {
detail.markQueryJoins(beanDescriptor, lazyLoadManyPath, isAllowOneManyFetch());
}
private boolean isAllowOneManyFetch() {
if (Mode.LAZYLOAD_MANY.equals(getMode())) {
return false;
} else if (hasMaxRowsOrFirstRow() && !isRawSql() && !isSqlSelect()) {
return false;
}
return true;
}
@Override
public void convertJoins(int queryBatchSize) {
initManyWhereJoins();
convertManyFetchJoinsToQueryJoins(queryBatchSize);
}
/**
* Convert any many joins fetch joins to query joins.
*/
private void convertManyFetchJoinsToQueryJoins(int queryBatch) {
boolean allowOne = isAllowOneManyFetch();
detail.convertManyFetchJoinsToQueryJoins(beanDescriptor, lazyLoadManyPath, allowOne, queryBatch);
}
protected void setOrmQueryDetail(OrmQueryDetail detail) {
this.detail = detail;
}
@@ -7,7 +7,6 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.el.ElPropertyDeploy;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.query.SplitName;
import org.jetbrains.annotations.NotNull;
import javax.persistence.PersistenceException;
import java.io.Serializable;
@@ -38,13 +37,12 @@ public class OrmQueryDetail implements Serializable {
/**
* Root level properties.
*/
@NotNull
private OrmQueryProperties baseProps = new OrmQueryProperties();
/**
* Contains the fetch/lazy/query joins and their properties.
*/
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>(8);
private LinkedHashMap<String, OrmQueryProperties> fetchPaths = new LinkedHashMap<String, OrmQueryProperties>();
/**
* Return a deep copy of the OrmQueryDetail.
@@ -302,7 +300,7 @@ public class OrmQueryDetail implements Serializable {
public void sortFetchPaths(BeanDescriptor<?> d) {
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>(fetchPaths.size());
LinkedHashMap<String, OrmQueryProperties> sorted = new LinkedHashMap<String, OrmQueryProperties>();
for (OrmQueryProperties p : fetchPaths.values()) {
sortFetchPaths(d, p, sorted);
@@ -311,8 +309,7 @@ public class OrmQueryDetail implements Serializable {
fetchPaths = sorted;
}
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p,
LinkedHashMap<String, OrmQueryProperties> sorted) {
private void sortFetchPaths(BeanDescriptor<?> d, OrmQueryProperties p, LinkedHashMap<String, OrmQueryProperties> sorted) {
String path = p.getPath();
if (!sorted.containsKey(path)) {
@@ -325,8 +322,7 @@ public class OrmQueryDetail implements Serializable {
if (parentProp == null) {
ElPropertyValue el = d.getElGetValue(parentPath);
if (el == null) {
String msg = "Path [" + parentPath + "] not valid from " + d.getFullName();
throw new PersistenceException(msg);
throw new PersistenceException("Path [" + parentPath + "] not valid from " + d.getFullName());
}
// add a missing parent path just fetching the Id property
BeanPropertyAssoc<?> assocOne = (BeanPropertyAssoc<?>) el.getBeanProperty();
@@ -340,12 +336,9 @@ public class OrmQueryDetail implements Serializable {
}
/**
* Convert 'fetch joins' to 'many' properties over to 'query joins'.
* Mark 'fetch joins' to 'many' properties over to 'query joins' where needed.
*/
public void convertManyFetchJoinsToQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath,
boolean allowOne, int queryBatch) {
ArrayList<OrmQueryProperties> manyChunks = new ArrayList<OrmQueryProperties>(3);
public void markQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath, boolean allowOne) {
// the name of the many fetch property if there is one
String manyFetchProperty = null;
@@ -369,16 +362,11 @@ public class OrmQueryDetail implements Serializable {
manyFetchProperty = fetchPath;
} else {
// convert this one over to a 'query join'
manyChunks.add(chunk);
chunk.markForQueryJoin();
}
}
}
}
for (int i = 0; i < manyChunks.size(); i++) {
// convert 'fetch joins' over to 'query joins'
manyChunks.get(i).setQueryFetch(queryBatch, true);
}
}
/**
@@ -28,35 +28,27 @@ public class OrmQueryProperties implements Serializable {
private static final long serialVersionUID = -8785582703966455658L;
protected static final FetchConfig DEFAULT_FETCH = new FetchConfig();
private final String parentPath;
private final String path;
private final String rawProperties;
private final String trimmedProperties;
/**
* NB: -1 means no +query, 0 means use the default batch size.
*/
private int queryFetchBatch = -1;
private boolean queryFetchAll;
private final LinkedHashSet<String> included;
private final FetchConfig fetchConfig;
/**
* NB: -1 means no +lazy, 0 means use the default batch size.
* Flag set when this fetch path needs to be a query join.
*/
private int lazyFetchBatch = -1;
private FetchConfig fetchConfig;
private boolean markForQueryJoin;
private boolean cache;
private boolean readOnly;
/**
* Note this SHOULD be a LinkedHashSet to preserve order of the properties. This is to make using
* SqlSelect easier with predictable property/column ordering.
*/
private final LinkedHashSet<String> included;
/**
* Included bean joins.
*/
@@ -70,8 +62,7 @@ public class OrmQueryProperties implements Serializable {
private List<OrmQueryProperties> secondaryChildren;
/**
* OrderBy properties that where on the main query but moved here as they relate to this (query
* join).
* OrderBy properties that where on the main query but moved here as they relate to this (query join).
*/
@SuppressWarnings("rawtypes")
private OrderBy orderBy;
@@ -98,6 +89,7 @@ public class OrmQueryProperties implements Serializable {
this.rawProperties = null;
this.trimmedProperties = null;
this.included = null;
this.fetchConfig = DEFAULT_FETCH;
}
public OrmQueryProperties(String path, String rawProperties) {
@@ -113,16 +105,12 @@ public class OrmQueryProperties implements Serializable {
this.rawProperties = rawProperties;
this.trimmedProperties = response.properties;
this.included = response.included;
this.lazyFetchBatch = response.lazyFetchBatch;
this.queryFetchBatch = response.queryFetchBatch;
this.cache = response.cache;
this.readOnly = response.readOnly;
if (fetchConfig != null) {
this.fetchConfig = fetchConfig;
lazyFetchBatch = fetchConfig.getLazyBatchSize();
queryFetchBatch = fetchConfig.getQueryBatchSize();
queryFetchAll = fetchConfig.isQueryAll();
} else {
this.fetchConfig = response.fetchConfig;
}
}
@@ -137,11 +125,9 @@ public class OrmQueryProperties implements Serializable {
this.included = parsedProperties;
this.rawProperties = join(parsedProperties);
this.trimmedProperties = rawProperties;
this.lazyFetchBatch = -1;
this.queryFetchBatch = -1;
this.cache = false;
this.readOnly = false;
this.queryFetchAll = false;
this.fetchConfig = DEFAULT_FETCH;
}
/**
@@ -172,9 +158,7 @@ public class OrmQueryProperties implements Serializable {
this.trimmedProperties = source.trimmedProperties;
this.cache = source.cache;
this.readOnly = source.readOnly;
this.queryFetchAll = source.queryFetchAll;
this.queryFetchBatch = source.queryFetchBatch;
this.lazyFetchBatch = source.lazyFetchBatch;
this.fetchConfig = source.fetchConfig;
this.filterMany = source.filterMany;
this.included = (source.included == null) ? null : new LinkedHashSet<String>(source.included);
if (includedBeanJoin != null) {
@@ -216,9 +200,7 @@ public class OrmQueryProperties implements Serializable {
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
// by default we need to make this a 'query join' now
queryFetchAll = true;
queryFetchBatch = 100;
lazyFetchBatch = 100;
markForQueryJoin = true;
}
return filterMany;
}
@@ -409,9 +391,11 @@ public class OrmQueryProperties implements Serializable {
return included == null || included.contains(propName);
}
public void setQueryFetch(int batch, boolean queryFetchAll) {
this.queryFetchBatch = batch;
this.queryFetchAll = queryFetchAll;
/**
* Mark this path as needing to be a query join.
*/
public void markForQueryJoin() {
markForQueryJoin = true;
}
public boolean isFetchJoin() {
@@ -419,23 +403,23 @@ public class OrmQueryProperties implements Serializable {
}
public boolean isQueryFetch() {
return queryFetchBatch > -1;
return markForQueryJoin || getQueryFetchBatch() > -1;
}
public int getQueryFetchBatch() {
return queryFetchBatch;
return fetchConfig.getQueryBatchSize();
}
public boolean isQueryFetchAll() {
return queryFetchAll;
return fetchConfig.isQueryAll();
}
public boolean isLazyFetch() {
return lazyFetchBatch > -1;
return getLazyFetchBatch() > -1;
}
public int getLazyFetchBatch() {
return lazyFetchBatch;
return fetchConfig.getLazyBatchSize();
}
public boolean isReadOnly() {
@@ -474,9 +458,7 @@ public class OrmQueryProperties implements Serializable {
if (!Same.sameByNull(filterMany, p2.filterMany)) return false;
if (filterMany != null && !filterMany.isSameByPlan(p2.filterMany)) return false;
return lazyFetchBatch == p2.lazyFetchBatch
&& queryFetchBatch == p2.queryFetchBatch
&& queryFetchAll == p2.queryFetchAll;
return fetchConfig.equals(p2.fetchConfig);
}
/**
@@ -492,9 +474,7 @@ public class OrmQueryProperties implements Serializable {
if (filterMany != null) {
filterMany.queryPlanHash(builder);
}
builder.add(lazyFetchBatch);
builder.add(queryFetchBatch);
builder.add(queryFetchAll);
builder.add(fetchConfig.hashCode());
}
}
@@ -1,5 +1,6 @@
package com.avaje.ebeaninternal.server.querydefn;
import com.avaje.ebean.FetchConfig;
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
import java.util.LinkedHashSet;
@@ -18,25 +19,26 @@ public class OrmQueryPropertiesParser {
final boolean readOnly;
final boolean cache;
final int queryFetchBatch;
final int lazyFetchBatch;
final FetchConfig fetchConfig;
final String properties;
final LinkedHashSet<String> included;
public Response(boolean readOnly, boolean cache, int queryFetchBatch, int lazyFetchBatch, String properties, LinkedHashSet<String> included) {
this.readOnly = readOnly;
this.cache = cache;
this.queryFetchBatch = queryFetchBatch;
this.lazyFetchBatch = lazyFetchBatch;
this.properties = properties;
this.included = included;
if (lazyFetchBatch > -1 || queryFetchBatch > -1) {
this.fetchConfig = new FetchConfig().lazy(lazyFetchBatch).query(queryFetchBatch);
} else {
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
}
}
public Response() {
this.readOnly = false;
this.cache = false;
this.queryFetchBatch = -1;
this.lazyFetchBatch = -1;
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
this.properties = "";
this.included = null;
}