Refactor FetchConfig

This commit is contained in:
rob bygrave
2021-02-12 21:01:57 +13:00
parent 213562fcf1
commit e0c3a92b60
23 changed files with 272 additions and 492 deletions
@@ -17,11 +17,11 @@ class ParseFetchConfig {
if (path.startsWith("lazy")) {
if (path.length() == 4) {
return new FetchConfig().lazy();
return FetchConfig.ofLazy();
} else if (path.charAt(4) == '(') {
path = path.substring(5);
int batchSize = parseBatchSize(path);
return new FetchConfig().lazy(batchSize);
return FetchConfig.ofLazy(batchSize);
} else {
return null;
}
@@ -29,11 +29,11 @@ class ParseFetchConfig {
if (path.startsWith("query")) {
if (path.length() == 5) {
return new FetchConfig().query();
return FetchConfig.ofQuery();
} else if (path.charAt(5) == '(') {
path = path.substring(6);
int batchSize = parseBatchSize(path);
return new FetchConfig().query(batchSize);
return FetchConfig.ofQuery(batchSize);
} else {
return null;
}
@@ -1,6 +1,5 @@
package io.ebeaninternal.server.loadcontext;
import io.ebean.FetchConfig;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.api.SpiQuery;
@@ -28,9 +27,7 @@ abstract class DLoadBaseContext {
final boolean hitCache;
final int firstBatchSize;
final int secondaryBatchSize;
final int batchSize;
final ObjectGraphNode objectGraphNode;
@@ -45,38 +42,11 @@ abstract class DLoadBaseContext {
this.hitCache = parent.isBeanCacheGet() && desc.isBeanCaching();
this.objectGraphNode = parent.getObjectGraphNode(path);
this.queryFetch = queryProps != null && queryProps.isQueryFetch();
this.firstBatchSize = initFirstBatchSize(defaultBatchSize, queryProps);
this.secondaryBatchSize = initSecondaryBatchSize(defaultBatchSize, firstBatchSize, queryProps);
this.batchSize = initBatchSize(defaultBatchSize, queryProps);
}
private int initFirstBatchSize(int batchSize, OrmQueryProperties queryProps) {
if (queryProps == null) {
return batchSize;
}
int queryBatchSize = queryProps.getQueryFetchBatch();
if (queryBatchSize == -1) {
return batchSize;
} else if (queryBatchSize == 0) {
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.isQueryAll()) {
return firstBatchSize;
}
int lazyBatchSize = fetchConfig.getLazyBatchSize();
return (lazyBatchSize > 1) ? lazyBatchSize : defaultBatchSize;
private int initBatchSize(int batchSize, OrmQueryProperties queryProps) {
return queryProps == null ? batchSize : queryProps.getBatchSize();
}
/**
@@ -84,7 +54,6 @@ abstract class DLoadBaseContext {
* set onto the secondary query.
*/
void setLabel(SpiQuery<?> query) {
String label = parent.getPlanLabel();
if (label != null) {
query.setProfilePath(label, fullPath, parent.getProfileLocation());
@@ -35,7 +35,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
super(parent, desc, path, defaultBatchSize, queryProps);
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
this.currentBuffer = createBuffer(firstBatchSize);
this.currentBuffer = createBuffer(batchSize);
this.cache = (queryProps != null) && queryProps.isCache();
}
@@ -52,7 +52,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
if (bufferList != null) {
bufferList.clear();
}
currentBuffer = createBuffer(secondaryBatchSize);
currentBuffer = createBuffer(batchSize);
}
private void configureQuery(SpiQuery<?> query, String lazyLoadProperty) {
@@ -70,7 +70,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
protected void register(EntityBeanIntercept ebi) {
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
currentBuffer = createBuffer(batchSize);
}
ebi.setBeanLoader(currentBuffer, getPersistenceContext());
currentBuffer.add(ebi);
@@ -95,10 +95,6 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
for (LoadBuffer loadBuffer : bufferList) {
if (!loadBuffer.list.isEmpty()) {
parent.getEbeanServer().loadBean(new LoadBeanRequest(loadBuffer, parentRequest));
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
break;
}
}
if (forEach) {
clear();
@@ -188,7 +188,7 @@ public class DLoadContext implements LoadContext {
}
int maxBatch = 0;
for (OrmQueryProperties aSecQuery : secQuery) {
int batchSize = aSecQuery.getQueryFetchBatch();
int batchSize = aSecQuery.getBatchSize();
if (batchSize == 0) {
batchSize = defaultQueryBatch;
}
@@ -300,12 +300,9 @@ public class DLoadContext implements LoadContext {
}
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
int batchSize;
if (props.isQueryFetch()) {
batchSize = 100;
} else {
int lazyJoinBatch = props.getLazyFetchBatch();
batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
int batchSize = props.getBatchSize();
if (batchSize == 0) {
batchSize = defaultBatchSize;
}
String path = props.getPath();
if (many) {
@@ -40,7 +40,7 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
this.docStoreMapped = property.isTargetDocStoreMapped();
// bufferList only required when using query joins (queryFetch)
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
this.currentBuffer = createBuffer(firstBatchSize);
this.currentBuffer = createBuffer(batchSize);
}
private LoadBuffer createBuffer(int size) {
@@ -58,11 +58,10 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
if (bufferList != null) {
bufferList.clear();
}
currentBuffer = createBuffer(secondaryBatchSize);
currentBuffer = createBuffer(batchSize);
}
private void configureQuery(SpiQuery<?> query) {
setLabel(query);
parent.propagateQueryState(query, docStoreMapped);
query.setParentNode(objectGraphNode);
@@ -85,9 +84,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
}
public void register(BeanCollection<?> bc) {
if (currentBuffer.isFull()) {
currentBuffer = createBuffer(secondaryBatchSize);
currentBuffer = createBuffer(batchSize);
}
currentBuffer.add(bc);
bc.setLoader(currentBuffer);
@@ -105,13 +103,8 @@ class DLoadManyContext extends DLoadBaseContext implements LoadManyContext {
if (!loadBuffer.list.isEmpty()) {
LoadManyRequest req = new LoadManyRequest(loadBuffer, parentRequest);
parent.getEbeanServer().loadMany(req);
if (!queryProps.isQueryFetchAll()) {
// Stop - only fetch the first batch ... the rest will be lazy loaded
break;
}
}
}
if (forEach) {
clear();
} else {
@@ -11,11 +11,11 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
*/
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
private final OrmQueryDetail detail;
@@ -45,11 +45,11 @@ import java.util.stream.Stream;
*/
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
private OrmQueryDetail detail = new OrmQueryDetail();
@@ -452,7 +452,7 @@ public final class SqlTreeBuilder {
// Also note that this can include transient properties.
// This makes sense for transient properties used to
// hold sum() count() type values (with SqlSelect)
final Set<String> selectInclude = queryProps.getSelectInclude();
final Set<String> selectInclude = queryProps.getIncluded();
for (String propName : selectInclude) {
if (!propName.isEmpty()) {
addProperty(selectProps, desc, queryProps, propName);
@@ -83,11 +83,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private static final String DEFAULT_QUERY_NAME = "default";
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
private static final FetchConfig FETCH_CACHE = FetchConfig.ofCache();
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
private static final FetchConfig FETCH_QUERY = FetchConfig.ofQuery();
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
private static final FetchConfig FETCH_LAZY = FetchConfig.ofLazy();
private final ReentrantLock lock = new ReentrantLock();
@@ -117,14 +117,14 @@ public class OrmQueryDetail implements Serializable {
public String asString() {
StringBuilder sb = new StringBuilder();
if (!baseProps.isEmpty()) {
baseProps.append("select ", sb);
baseProps.asStringDebug("select ", sb);
}
if (fetchPaths != null) {
for (OrmQueryProperties join : fetchPaths.values()) {
if (sb.length() > 0) {
sb.append(" ");
}
join.append("fetch ", sb);
join.asStringDebug("fetch ", sb);
}
}
return sb.toString();
@@ -30,12 +30,8 @@ public class OrmQueryProperties implements Serializable {
private final String parentPath;
private final String path;
private final String rawProperties;
private final String trimmedProperties;
private final LinkedHashSet<String> included;
private final String properties;
private final Set<String> included;
private final FetchConfig fetchConfig;
/**
@@ -84,8 +80,7 @@ public class OrmQueryProperties implements Serializable {
public OrmQueryProperties(String path) {
this.path = path;
this.parentPath = SplitName.parent(path);
this.rawProperties = null;
this.trimmedProperties = null;
this.properties = null;
this.included = null;
this.fetchConfig = DEFAULT_FETCH;
}
@@ -95,13 +90,11 @@ public class OrmQueryProperties implements Serializable {
}
public OrmQueryProperties(String path, String rawProperties, FetchConfig fetchConfig) {
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
this.path = path;
this.parentPath = SplitName.parent(path);
this.rawProperties = rawProperties;
this.trimmedProperties = response.properties;
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
this.properties = response.properties;
this.included = response.included;
this.cache = response.cache;
this.readOnly = response.readOnly;
@@ -115,39 +108,17 @@ public class OrmQueryProperties implements Serializable {
}
}
public OrmQueryProperties(String path, LinkedHashSet<String> parsedProperties) {
if (parsedProperties == null) {
throw new IllegalArgumentException("parsedProperties is null");
}
public OrmQueryProperties(String path, Set<String> included) {
this.path = path;
this.parentPath = SplitName.parent(path);
// for rawSql parsedProperties can be empty (when only fetching Id property)
this.included = parsedProperties;
this.rawProperties = join(parsedProperties);
this.trimmedProperties = rawProperties;
this.included = included;
this.properties = String.join(",", included);
this.cache = false;
this.readOnly = false;
this.fetchConfig = DEFAULT_FETCH;
}
/**
* Join the set of properties into a comma delimited string.
*/
private String join(LinkedHashSet<String> parsedProperties) {
StringBuilder sb = new StringBuilder(50);
boolean first = true;
for (String property : parsedProperties) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(property);
}
return sb.toString();
}
/**
* Copy constructor.
*/
@@ -155,8 +126,7 @@ public class OrmQueryProperties implements Serializable {
this.fetchConfig = sourceFetchConfig;
this.parentPath = source.parentPath;
this.path = source.path;
this.rawProperties = source.rawProperties;
this.trimmedProperties = source.trimmedProperties;
this.properties = source.properties;
this.cache = source.cache;
this.readOnly = source.readOnly;
this.filterMany = source.filterMany;
@@ -240,8 +210,8 @@ public class OrmQueryProperties implements Serializable {
@SuppressWarnings("unchecked")
public void configureBeanQuery(SpiQuery<?> query) {
if (trimmedProperties != null && !trimmedProperties.isEmpty()) {
query.select(trimmedProperties);
if (properties != null && !properties.isEmpty()) {
query.select(properties);
}
if (filterMany != null) {
@@ -268,7 +238,7 @@ public class OrmQueryProperties implements Serializable {
}
public boolean hasSelectClause() {
if ("*".equals(trimmedProperties)) {
if ("*".equals(properties)) {
// explicitly selected all properties
return true;
}
@@ -280,25 +250,17 @@ public class OrmQueryProperties implements Serializable {
* Return true if the properties and configuration are empty.
*/
public boolean isEmpty() {
return rawProperties == null || rawProperties.isEmpty();
return properties == null || properties.isEmpty();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(40);
append("", sb);
return sb.toString();
}
public String append(String prefix, StringBuilder sb) {
public void asStringDebug(String prefix, StringBuilder sb) {
sb.append(prefix);
if (path != null) {
sb.append(path).append(" ");
}
if (!isEmpty()) {
sb.append("(").append(rawProperties).append(")");
sb.append("(").append(properties).append(")");
}
return sb.toString();
}
boolean isChild(OrmQueryProperties possibleChild) {
@@ -319,7 +281,7 @@ public class OrmQueryProperties implements Serializable {
* Return the raw properties.
*/
public String getProperties() {
return rawProperties;
return properties;
}
/**
@@ -350,10 +312,6 @@ public class OrmQueryProperties implements Serializable {
includedBeanJoin.add(propertyName);
}
public Set<String> getSelectInclude() {
return included;
}
public Set<String> getSelectQueryJoin() {
return secondaryQueryJoins;
}
@@ -373,7 +331,6 @@ public class OrmQueryProperties implements Serializable {
}
boolean isIncluded(String propName) {
if (includedBeanJoin != null && includedBeanJoin.contains(propName)) {
return false;
}
@@ -392,42 +349,25 @@ public class OrmQueryProperties implements Serializable {
* Return true if this path is a 'query join'.
*/
public boolean isQueryFetch() {
return markForQueryJoin || getQueryFetchBatch() > -1;
return markForQueryJoin || cache || fetchConfig.isQuery();
}
/**
* Return true if this path is a 'fetch join'.
*/
boolean isFetchJoin() {
return !isQueryFetch() && !isLazyFetch();
return !markForQueryJoin && fetchConfig.isJoin();
}
/**
* Return true if this path is a lazy fetch.
*/
boolean isLazyFetch() {
return getLazyFetchBatch() > -1;
return fetchConfig.isLazy();
}
/**
* Return the batch size to use for the query join.
*/
public int getQueryFetchBatch() {
return fetchConfig.getQueryBatchSize();
}
/**
* Return true if a query join should eagerly fetch 'all' rather than the 'first'.
*/
public boolean isQueryFetchAll() {
return fetchConfig.isQueryAll();
}
/**
* Return the batch size to use for lazy loading.
*/
public int getLazyFetchBatch() {
return fetchConfig.getLazyBatchSize();
public int getBatchSize() {
return fetchConfig.getBatchSize();
}
/**
@@ -474,26 +414,24 @@ public class OrmQueryProperties implements Serializable {
* Calculate the query plan hash.
*/
public void queryPlanHash(StringBuilder builder) {
builder.append("qpp[");
builder.append(path);
builder.append("{");
if (path != null) {
builder.append(path);
}
if (included != null){
builder.append(" included:").append(included);
builder.append("/i").append(included);
}
if (secondaryQueryJoins != null) {
builder.append(" secondary:").append(secondaryQueryJoins);
builder.append("/s").append(secondaryQueryJoins);
}
if (filterMany != null) {
builder.append(" filterMany[");
builder.append("/f");
filterMany.queryPlanHash(builder);
builder.append("]");
}
if (fetchConfig != null) {
builder.append(" config:").append(fetchConfig.hashCode());
builder.append("/c").append(fetchConfig.hashCode());
}
builder.append("]");
builder.append("}");
}
}
@@ -29,8 +29,10 @@ class OrmQueryPropertiesParser {
this.cache = cache;
this.properties = properties;
this.included = included;
if (lazyFetchBatch > -1 || queryFetchBatch > -1) {
this.fetchConfig = new FetchConfig().lazy(lazyFetchBatch).query(queryFetchBatch);
if (queryFetchBatch > 0) {
this.fetchConfig = FetchConfig.ofQuery(queryFetchBatch);
} else if (lazyFetchBatch > 0) {
this.fetchConfig = FetchConfig.ofLazy(lazyFetchBatch);
} else {
this.fetchConfig = OrmQueryProperties.DEFAULT_FETCH;
}
@@ -59,8 +61,8 @@ class OrmQueryPropertiesParser {
private boolean allProperties;
private boolean readOnly;
private boolean cache;
private int queryFetchBatch = -1;
private int lazyFetchBatch = -1;
private int queryFetchBatch;
private int lazyFetchBatch;
private OrmQueryPropertiesParser(String inputProperties) {
this.inputProperties = inputProperties;