io.ebeaninternal.server.querydefn.OrmQueryProperties Maven / Gradle / Ivy
package io.ebeaninternal.server.querydefn;
import io.ebean.ExpressionFactory;
import io.ebean.FetchConfig;
import io.ebean.OrderBy;
import io.ebean.Query;
import io.ebean.event.BeanQueryRequest;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiExpression;
import io.ebeaninternal.api.SpiExpressionFactory;
import io.ebeaninternal.api.SpiExpressionList;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.expression.FilterExprPath;
import io.ebeaninternal.server.expression.FilterExpressionList;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* Represents the Properties of an Object Relational query.
*/
public final class OrmQueryProperties implements Serializable {
private static final long serialVersionUID = -8785582703966455658L;
static final FetchConfig DEFAULT_FETCH = FetchConfig.ofDefault();
private final String parentPath;
private final String path;
private final boolean allProperties;
private final Set included;
private final FetchConfig fetchConfig;
private final boolean cache;
/**
* Flag set when this fetch path needs to be a query join.
*/
private boolean markForQueryJoin;
/**
* Included bean joins.
*/
private Set includedBeanJoin;
/**
* Add these properties to the select so that the foreign key columns are included in the query.
*/
private Set secondaryQueryJoins;
private List secondaryChildren;
/**
* OrderBy properties that where on the main query but moved here as they relate to this (query join).
*/
@SuppressWarnings("rawtypes")
private OrderBy orderBy;
/**
* A filter that can be applied to the fetch of this path in the object graph.
*/
@SuppressWarnings("rawtypes")
private SpiExpressionList filterMany;
/**
* Construct for root so path (and parentPath) are null.
*/
public OrmQueryProperties() {
this(null);
}
/**
* Construct with a given path.
*/
public OrmQueryProperties(String path) {
this.path = path;
this.parentPath = SplitName.parent(path);
this.allProperties = false;
this.included = null;
this.cache = false;
this.fetchConfig = DEFAULT_FETCH;
}
public OrmQueryProperties(String path, String rawProperties) {
this(path, rawProperties, DEFAULT_FETCH);
}
public OrmQueryProperties(String path, String rawProperties, FetchConfig fetchConfig) {
this.path = path;
this.parentPath = SplitName.parent(path);
OrmQueryPropertiesParser.Response response = OrmQueryPropertiesParser.parse(rawProperties);
this.allProperties = response.allProperties;
this.included = response.included;
if (fetchConfig != null) {
this.fetchConfig = fetchConfig;
this.cache = fetchConfig.isCache();
} else {
this.cache = false;
this.fetchConfig = DEFAULT_FETCH;
}
}
public OrmQueryProperties(String path, Set included) {
this(path, included, DEFAULT_FETCH);
}
OrmQueryProperties(String path, Set included, FetchConfig fetchConfig) {
this.path = path;
this.parentPath = SplitName.parent(path);
this.included = included;
this.allProperties = false;
this.fetchConfig = fetchConfig;
this.cache = fetchConfig.isCache();
}
OrmQueryProperties(String path, OrmQueryProperties other, FetchConfig fetchConfig) {
this.path = path;
this.parentPath = SplitName.parent(path);
this.allProperties = other.allProperties;
this.included = other.included;
this.fetchConfig = fetchConfig;
this.cache = fetchConfig.isCache();
}
/**
* Copy constructor.
*/
private OrmQueryProperties(OrmQueryProperties source, FetchConfig sourceFetchConfig) {
this.fetchConfig = sourceFetchConfig;
this.parentPath = source.parentPath;
this.path = source.path;
this.allProperties = source.allProperties;
this.cache = source.cache;
this.filterMany = source.filterMany;
this.markForQueryJoin = source.markForQueryJoin;
this.included = (source.included == null) ? null : new LinkedHashSet<>(source.included);
}
/**
* Creates a copy of the OrmQueryProperties.
*/
public OrmQueryProperties copy() {
return new OrmQueryProperties(this, this.fetchConfig);
}
/**
* Create a copy with the given fetch config.
*/
public OrmQueryProperties copy(FetchConfig fetchConfig) {
return new OrmQueryProperties(this, fetchConfig);
}
/**
* Move a OrderBy.Property from the main query to this query join.
*/
@SuppressWarnings("rawtypes")
void addSecJoinOrderProperty(OrderBy.Property orderProp) {
if (orderBy == null) {
orderBy = new OrderBy();
}
orderBy.add(orderProp);
}
public FetchConfig getFetchConfig() {
return fetchConfig;
}
/**
* Return the expressions used to filter on this path. This should be a many path to use this
* method.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public SpiExpressionList filterMany(Query rootQuery) {
if (filterMany == null) {
FilterExprPath exprPath = new FilterExprPath(path);
SpiExpressionFactory queryEf = (SpiExpressionFactory) rootQuery.getExpressionFactory();
ExpressionFactory filterEf = queryEf.createExpressionFactory();// exprPath);
filterMany = new FilterExpressionList(exprPath, filterEf, rootQuery);
}
return filterMany;
}
/**
* Return the filterMany expression list (can be null).
*/
private SpiExpressionList> getFilterManyTrimPath(int trimPath) {
if (filterMany == null) {
return null;
}
return filterMany.trimPath(trimPath);
}
/**
* Adjust filterMany expressions for inclusion in main query.
*/
public void filterManyInline() {
if (filterMany != null){
filterMany.prefixProperty(path);
}
}
/**
* Prepare filterMany expressions for query plan key.
*/
public void prepareExpressions(BeanQueryRequest> request) {
if (filterMany != null) {
filterMany.prepareExpression(request);
}
}
/**
* Return the filterMany expression list (can be null).
*/
public SpiExpressionList> getFilterMany() {
return filterMany;
}
/**
* Set the filterMany expression list.
*/
public void setFilterMany(SpiExpressionList> filterMany) {
this.filterMany = filterMany;
}
/**
* Define the select and joins for this query.
*/
@SuppressWarnings("unchecked")
public void configureBeanQuery(SpiQuery> query) {
if (hasProperties()) {
query.selectProperties(this);
}
if (filterMany != null) {
filterMany.applyRowLimits(query);
SpiExpressionList> trimPath = filterMany.trimPath(path.length() + 1);
for (SpiExpression spiExpression : trimPath.underlyingList()) {
query.where().add(spiExpression);
}
}
if (secondaryChildren != null) {
int trimPath = path.length() + 1;
for (OrmQueryProperties p : secondaryChildren) {
String path = p.getPath();
path = path.substring(trimPath);
query.fetchProperties(path, p);
query.setFilterMany(path, p.getFilterManyTrimPath(trimPath));
}
}
if (orderBy != null) {
query.setOrder(orderBy.copyWithTrim(path));
}
}
public boolean hasSelectClause() {
return allProperties || included != null || filterMany != null;
}
/**
* Return true if explicit properties have been specified.
*/
boolean hasProperties() {
return allProperties || included != null;
}
public void asStringDebug(String prefix, StringBuilder sb) {
sb.append(prefix);
if (path != null) {
sb.append(path).append(' ');
}
if (allProperties) {
sb.append("(*)");
} else if (included != null) {
sb.append('(').append(String.join(",", included)).append(')');
}
}
boolean isChild(OrmQueryProperties possibleChild) {
return possibleChild.getPath().startsWith(path + ".");
}
/**
* For secondary queries add a child element.
*/
public void add(OrmQueryProperties child) {
if (secondaryChildren == null) {
secondaryChildren = new ArrayList<>();
}
secondaryChildren.add(child);
}
/**
* Return true if this includes all properties on the path.
*/
public boolean allProperties() {
// this is really "default" properties
return included == null;
}
/**
* Return true if this property is included as a bean join.
*
* If a property is included as a bean join then it should not be included as a reference/proxy to
* avoid duplication.
*
*/
public boolean isIncludedBeanJoin(String propertyName) {
return includedBeanJoin != null && includedBeanJoin.contains(propertyName);
}
/**
* Add a bean join property.
*/
void includeBeanJoin(String propertyName) {
if (includedBeanJoin == null) {
includedBeanJoin = new HashSet<>();
}
includedBeanJoin.add(propertyName);
}
public Set getSelectQueryJoin() {
return secondaryQueryJoins;
}
void addSecondaryQueryJoin(String property) {
if (secondaryQueryJoins == null) {
secondaryQueryJoins = new HashSet<>(4);
}
secondaryQueryJoins.add(property);
}
/**
* Return the property set.
*/
public Set getIncluded() {
return included;
}
boolean isIncluded(String propName) {
if (includedBeanJoin != null && includedBeanJoin.contains(propName)) {
return false;
}
return included == null || included.contains(propName);
}
/**
* Mark this path as needing to be a query join.
*/
void markForQueryJoin() {
markForQueryJoin = true;
}
/**
* Return true if this path is a 'query join'.
*/
public boolean isQueryFetch() {
return markForQueryJoin || cache || fetchConfig.isQuery();
}
/**
* Return true if this path is a 'fetch join'.
*/
boolean isFetchJoin() {
return !markForQueryJoin && fetchConfig.isJoin();
}
/**
* Return true if this path is a lazy fetch.
*/
boolean isLazyFetch() {
return fetchConfig.isLazy();
}
public int getBatchSize() {
return fetchConfig.getBatchSize();
}
/**
* Return true if this path should hit the L2 cache.
*/
public boolean isCache() {
return cache;
}
/**
* Return the parent path.
*/
String getParentPath() {
return parentPath;
}
/**
* Return the path relative to the root of the graph.
*/
public String getPath() {
return path;
}
/**
* Return true if the properties are the same for autoTune purposes.
*/
boolean isSameByAutoTune(OrmQueryProperties p2) {
if (included == null) {
return p2 == null || p2.included == null;
} else if (p2 == null) {
return false;
}
return included.equals(p2.included);
}
/**
* Calculate the query plan hash.
*/
public void queryPlanHash(StringBuilder builder) {
builder.append('{');
if (path != null) {
builder.append(path);
}
if (included != null) {
builder.append("/i").append(included);
}
if (secondaryQueryJoins != null) {
builder.append("/s").append(secondaryQueryJoins);
}
if (filterMany != null) {
builder.append("/f");
filterMany.queryPlanHash(builder);
}
if (fetchConfig != null) {
builder.append("/c").append(fetchConfig.hashCode());
}
builder.append('}');
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy